mirror of
https://github.com/luxfi/cli.git
synced 2026-10-30 17:01:20 +00:00
1062 lines
37 KiB
Go
1062 lines
37 KiB
Go
// Copyright (C) 2025, Lux Industries, Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
package nodecmd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/luxfi/cli/v2/cmd/blockchaincmd"
|
|
"github.com/luxfi/cli/v2/cmd/interchaincmd/messengercmd"
|
|
"github.com/luxfi/cli/v2/pkg/ansible"
|
|
awsAPI "github.com/luxfi/cli/v2/pkg/cloud/aws"
|
|
"github.com/luxfi/cli/v2/pkg/cobrautils"
|
|
"github.com/luxfi/cli/v2/pkg/constants"
|
|
"github.com/luxfi/cli/v2/pkg/contract"
|
|
"github.com/luxfi/cli/v2/pkg/docker"
|
|
"github.com/luxfi/cli/v2/pkg/interchain"
|
|
"github.com/luxfi/cli/v2/pkg/interchain/relayer"
|
|
"github.com/luxfi/cli/v2/pkg/metrics"
|
|
"github.com/luxfi/cli/v2/pkg/models"
|
|
"github.com/luxfi/cli/v2/pkg/networkoptions"
|
|
"github.com/luxfi/cli/v2/pkg/node"
|
|
"github.com/luxfi/cli/v2/pkg/ssh"
|
|
"github.com/luxfi/cli/v2/pkg/subnet"
|
|
"github.com/luxfi/cli/v2/pkg/utils"
|
|
"github.com/luxfi/cli/v2/pkg/ux"
|
|
"github.com/luxfi/ids"
|
|
"github.com/luxfi/node/v2/v2/utils/logging"
|
|
"github.com/luxfi/node/v2/v2/utils/set"
|
|
"github.com/luxfi/node/v2/v2/vms/platformvm/status"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
const (
|
|
healthCheckPoolTime = 60 * time.Second
|
|
healthCheckTimeout = 3 * time.Minute
|
|
syncCheckPoolTime = 10 * time.Second
|
|
syncCheckTimeout = 1 * time.Minute
|
|
validateCheckPoolTime = 10 * time.Second
|
|
validateCheckTimeout = 1 * time.Minute
|
|
)
|
|
|
|
var (
|
|
forceSubnetCreate bool
|
|
subnetGenesisFile string
|
|
useEvmSubnet bool
|
|
useCustomSubnet bool
|
|
evmVersion string
|
|
evmChainID uint64
|
|
evmToken string
|
|
evmTestDefaults bool
|
|
evmProductionDefaults bool
|
|
useLatestEvmReleasedVersion bool
|
|
useLatestEvmPreReleasedVersion bool
|
|
customVMRepoURL string
|
|
customVMBranch string
|
|
customVMBuildScript string
|
|
nodeConf string
|
|
subnetConf string
|
|
chainConf string
|
|
validators []string
|
|
customGrafanaDashboardPath string
|
|
warpReady bool
|
|
runRelayer bool
|
|
warpVersion string
|
|
warpMessengerContractAddressPath string
|
|
warpMessengerDeployerAddressPath string
|
|
warpMessengerDeployerTxPath string
|
|
warpRegistryBydecodePath string
|
|
deployWarpMessenger bool
|
|
deployWarpRegistry bool
|
|
replaceKeyPair bool
|
|
)
|
|
|
|
func newWizCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "wiz [clusterName] [subnetName]",
|
|
Short: "(ALPHA Warning) Creates a devnet together with a fully validated subnet into it.",
|
|
Long: `(ALPHA Warning) This command is currently in experimental mode.
|
|
|
|
The node wiz command creates a devnet and deploys, sync and validate a subnet into it. It creates the subnet if so needed.
|
|
`,
|
|
Args: cobrautils.RangeArgs(1, 2),
|
|
RunE: wiz,
|
|
PersistentPostRun: handlePostRun,
|
|
}
|
|
cmd.Flags().BoolVar(&useStaticIP, "use-static-ip", true, "attach static Public IP on cloud servers")
|
|
cmd.Flags().BoolVar(&useAWS, "aws", false, "create node/s in AWS cloud")
|
|
cmd.Flags().BoolVar(&useGCP, "gcp", false, "create node/s in GCP cloud")
|
|
cmd.Flags().StringSliceVar(&cmdLineRegion, "region", []string{}, "create node/s in given region(s). Use comma to separate multiple regions")
|
|
cmd.Flags().BoolVar(&authorizeAccess, "authorize-access", false, "authorize CLI to create cloud resources")
|
|
cmd.Flags().IntSliceVar(&numValidatorsNodes, "num-validators", []int{}, "number of nodes to create per region(s). Use comma to separate multiple numbers for each region in the same order as --region flag")
|
|
cmd.Flags().StringVar(&nodeType, "node-type", "", "cloud instance type. Use 'default' to use recommended default instance type")
|
|
cmd.Flags().StringVar(&cmdLineGCPCredentialsPath, "gcp-credentials", "", "use given GCP credentials")
|
|
cmd.Flags().StringVar(&cmdLineGCPProjectName, "gcp-project", "", "use given GCP project")
|
|
cmd.Flags().StringVar(&cmdLineAlternativeKeyPairName, "alternative-key-pair-name", "", "key pair name to use if default one generates conflicts")
|
|
cmd.Flags().StringVar(&awsProfile, "aws-profile", constants.AWSDefaultCredential, "aws profile to use")
|
|
cmd.Flags().BoolVar(&defaultValidatorParams, "default-validator-params", false, "use default weight/start/duration params for subnet validator")
|
|
cmd.Flags().BoolVar(&forceSubnetCreate, "force-subnet-create", false, "overwrite the existing subnet configuration if one exists")
|
|
cmd.Flags().StringVar(&subnetGenesisFile, "subnet-genesis", "", "file path of the subnet genesis")
|
|
cmd.Flags().BoolVar(&warpReady, "teleporter", false, "generate an warp-ready vm")
|
|
cmd.Flags().BoolVar(&warpReady, "warp", false, "generate an warp-ready vm")
|
|
cmd.Flags().BoolVar(&runRelayer, "relayer", false, "run AWM relayer when deploying the vm")
|
|
cmd.Flags().BoolVar(&useEvmSubnet, "evm-subnet", false, "use Subnet-EVM as the subnet virtual machine")
|
|
cmd.Flags().BoolVar(&useCustomSubnet, "custom-subnet", false, "use a custom VM as the subnet virtual machine")
|
|
cmd.Flags().StringVar(&evmVersion, "evm-version", "", "version of Subnet-EVM to use")
|
|
cmd.Flags().Uint64Var(&evmChainID, "evm-chain-id", 0, "chain ID to use with Subnet-EVM")
|
|
cmd.Flags().StringVar(&evmToken, "evm-token", "", "token name to use with Subnet-EVM")
|
|
cmd.Flags().BoolVar(&evmProductionDefaults, "evm-defaults", false, "use default production settings with Subnet-EVM")
|
|
cmd.Flags().BoolVar(&evmProductionDefaults, "evm-production-defaults", false, "use default production settings for your blockchain")
|
|
cmd.Flags().BoolVar(&evmTestDefaults, "evm-test-defaults", false, "use default test settings for your blockchain")
|
|
cmd.Flags().BoolVar(&useLatestEvmReleasedVersion, "latest-evm-version", false, "use latest Subnet-EVM released version")
|
|
cmd.Flags().BoolVar(&useLatestEvmPreReleasedVersion, "latest-pre-released-evm-version", false, "use latest Subnet-EVM pre-released version")
|
|
cmd.Flags().StringVar(&customVMRepoURL, "custom-vm-repo-url", "", "custom vm repository url")
|
|
cmd.Flags().StringVar(&customVMBranch, "custom-vm-branch", "", "custom vm branch or commit")
|
|
cmd.Flags().StringVar(&customVMBuildScript, "custom-vm-build-script", "", "custom vm build-script")
|
|
cmd.Flags().StringVar(&customGrafanaDashboardPath, "add-grafana-dashboard", "", "path to additional grafana dashboard json file")
|
|
cmd.Flags().StringVar(&nodeConf, "node-config", "", "path to luxd node configuration for subnet")
|
|
cmd.Flags().StringVar(&subnetConf, "subnet-config", "", "path to the subnet configuration for subnet")
|
|
cmd.Flags().StringVar(&chainConf, "chain-config", "", "path to the chain configuration for subnet")
|
|
cmd.Flags().BoolVar(&useSSHAgent, "use-ssh-agent", false, "use ssh agent for ssh")
|
|
cmd.Flags().StringVar(&sshIdentity, "ssh-agent-identity", "", "use given ssh identity(only for ssh agent). If not set, default will be used.")
|
|
cmd.Flags().BoolVar(&useLatestLuxgoReleaseVersion, "latest-luxd-version", false, "install latest luxd release version on node/s")
|
|
cmd.Flags().BoolVar(&useLatestLuxgoPreReleaseVersion, "latest-luxd-pre-release-version", false, "install latest luxd pre-release version on node/s")
|
|
cmd.Flags().StringVar(&useCustomLuxgoVersion, "custom-luxd-version", "", "install given luxd version on node/s")
|
|
cmd.Flags().StringSliceVar(&validators, "validators", []string{}, "deploy subnet into given comma separated list of validators. defaults to all cluster nodes")
|
|
cmd.Flags().BoolVar(&addMonitoring, enableMonitoringFlag, false, " set up Prometheus monitoring for created nodes. Please note that this option creates a separate monitoring instance and incures additional cost")
|
|
cmd.Flags().IntSliceVar(&numAPINodes, "num-apis", []int{}, "number of API nodes(nodes without stake) to create in the new Devnet")
|
|
cmd.Flags().IntVar(&iops, "aws-volume-iops", constants.AWSGP3DefaultIOPS, "AWS iops (for gp3, io1, and io2 volume types only)")
|
|
cmd.Flags().IntVar(&throughput, "aws-volume-throughput", constants.AWSGP3DefaultThroughput, "AWS throughput in MiB/s (for gp3 volume type only)")
|
|
cmd.Flags().StringVar(&volumeType, "aws-volume-type", "gp3", "AWS volume type")
|
|
cmd.Flags().IntVar(&volumeSize, "aws-volume-size", constants.CloudServerStorageSize, "AWS volume size in GB")
|
|
cmd.Flags().StringVar(&grafanaPkg, "grafana-pkg", "", "use grafana pkg instead of apt repo(by default), for example https://dl.grafana.com/oss/release/grafana_10.4.1_amd64.deb")
|
|
cmd.Flags().StringVar(&warpVersion, "teleporter-version", "latest", "warp version to deploy")
|
|
cmd.Flags().StringVar(&warpMessengerContractAddressPath, "teleporter-messenger-contract-address-path", "", "path to an warp messenger contract address file")
|
|
cmd.Flags().StringVar(&warpMessengerDeployerAddressPath, "teleporter-messenger-deployer-address-path", "", "path to an warp messenger deployer address file")
|
|
cmd.Flags().StringVar(&warpMessengerDeployerTxPath, "teleporter-messenger-deployer-tx-path", "", "path to an warp messenger deployer tx file")
|
|
cmd.Flags().StringVar(&warpRegistryBydecodePath, "teleporter-registry-bytecode-path", "", "path to an warp registry bytecode file")
|
|
cmd.Flags().BoolVar(&deployWarpMessenger, "deploy-teleporter-messenger", true, "deploy Interchain Messenger")
|
|
cmd.Flags().BoolVar(&deployWarpRegistry, "deploy-teleporter-registry", true, "deploy Interchain Registry")
|
|
cmd.Flags().StringVar(&warpVersion, "warp-version", "latest", "warp version to deploy")
|
|
cmd.Flags().StringVar(&warpMessengerContractAddressPath, "warp-messenger-contract-address-path", "", "path to an warp messenger contract address file")
|
|
cmd.Flags().StringVar(&warpMessengerDeployerAddressPath, "warp-messenger-deployer-address-path", "", "path to an warp messenger deployer address file")
|
|
cmd.Flags().StringVar(&warpMessengerDeployerTxPath, "warp-messenger-deployer-tx-path", "", "path to an warp messenger deployer tx file")
|
|
cmd.Flags().StringVar(&warpRegistryBydecodePath, "warp-registry-bytecode-path", "", "path to an warp registry bytecode file")
|
|
cmd.Flags().BoolVar(&deployWarpMessenger, "deploy-warp-messenger", true, "deploy Interchain Messenger")
|
|
cmd.Flags().BoolVar(&deployWarpRegistry, "deploy-warp-registry", true, "deploy Interchain Registry")
|
|
cmd.Flags().BoolVar(&replaceKeyPair, "auto-replace-keypair", false, "automatically replaces key pair to access node if previous key pair is not found")
|
|
cmd.Flags().BoolVar(&publicHTTPPortAccess, "public-http-port", false, "allow public access to luxd HTTP port")
|
|
cmd.Flags().StringSliceVar(&subnetAliases, "subnet-aliases", nil, "additional subnet aliases to be used for RPC calls in addition to subnet blockchain name")
|
|
return cmd
|
|
}
|
|
|
|
func wiz(cmd *cobra.Command, args []string) error {
|
|
clusterName := args[0]
|
|
subnetName := ""
|
|
if len(args) > 1 {
|
|
subnetName = args[1]
|
|
}
|
|
c := make(chan os.Signal, 1)
|
|
// Destroy cluster if user calls ctrl ^ c
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
go func() {
|
|
for range c {
|
|
if err := CallDestroyNode(clusterName); err != nil {
|
|
ux.Logger.RedXToUser("Unable to delete cluster %s due to %s", clusterName, err)
|
|
ux.Logger.RedXToUser("Please try again by calling lux node destroy %s", clusterName)
|
|
}
|
|
os.Exit(0)
|
|
}
|
|
}()
|
|
clusterAlreadyExists, err := app.ClusterExists(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if clusterAlreadyExists {
|
|
if err := checkClusterIsADevnet(clusterName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if clusterAlreadyExists && subnetName == "" {
|
|
return fmt.Errorf("expecting to add subnet to existing cluster but no subnet-name was provided")
|
|
}
|
|
if subnetName != "" && (!app.SidecarExists(subnetName) || forceSubnetCreate) {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Creating the subnet"))
|
|
ux.Logger.PrintToUser("")
|
|
if err := blockchaincmd.CallCreate(
|
|
cmd,
|
|
subnetName,
|
|
forceSubnetCreate,
|
|
subnetGenesisFile,
|
|
useEvmSubnet,
|
|
useCustomSubnet,
|
|
evmVersion,
|
|
evmChainID,
|
|
evmToken,
|
|
evmProductionDefaults,
|
|
evmTestDefaults,
|
|
useLatestEvmReleasedVersion,
|
|
useLatestEvmPreReleasedVersion,
|
|
customVMRepoURL,
|
|
customVMBranch,
|
|
customVMBuildScript,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
if chainConf != "" || subnetConf != "" || nodeConf != "" {
|
|
if err := blockchaincmd.CallConfigure(
|
|
cmd,
|
|
subnetName,
|
|
chainConf,
|
|
subnetConf,
|
|
nodeConf,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
if !clusterAlreadyExists {
|
|
globalNetworkFlags.UseDevnet = true
|
|
if len(useCustomLuxgoVersion) == 0 && !useLatestLuxgoReleaseVersion && !useLatestLuxgoPreReleaseVersion {
|
|
useLuxgoVersionFromSubnet = subnetName
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Creating the devnet..."))
|
|
ux.Logger.PrintToUser("")
|
|
// wizSubnet is used to get more metrics sent from node create command on whether if vm is custom or subnetEVM
|
|
wizSubnet = subnetName
|
|
if err := createNodes(cmd, []string{clusterName}); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Adding subnet into existing devnet %s..."), clusterName)
|
|
}
|
|
|
|
// check all validators are found
|
|
if len(validators) != 0 {
|
|
allHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
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)
|
|
}
|
|
hosts := cluster.GetValidatorHosts(allHosts) // exlude api nodes
|
|
_, err = filterHosts(hosts, validators)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := node.WaitForHealthyCluster(app, clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil {
|
|
return err
|
|
}
|
|
|
|
if subnetName == "" {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Devnet %s has been created!"), clusterName)
|
|
return nil
|
|
}
|
|
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Checking subnet compatibility"))
|
|
ux.Logger.PrintToUser("")
|
|
if err := checkRPCCompatibility(clusterName, subnetName); err != nil {
|
|
return err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Creating the blockchain"))
|
|
ux.Logger.PrintToUser("")
|
|
avoidChecks = true
|
|
if err := deploySubnet(cmd, []string{clusterName, subnetName}); err != nil {
|
|
return err
|
|
}
|
|
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Adding nodes as subnet validators"))
|
|
ux.Logger.PrintToUser("")
|
|
avoidSubnetValidationChecks = true
|
|
useEwoq = true
|
|
if err := validateSubnet(cmd, []string{clusterName, subnetName}); err != nil {
|
|
return err
|
|
}
|
|
|
|
network, err := app.GetClusterNetwork(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sc, err := app.LoadSidecar(subnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
subnetID := sc.Networks[network.Name()].SubnetID
|
|
if subnetID == ids.Empty {
|
|
return constants.ErrNoSubnetID
|
|
}
|
|
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Waiting for nodes to be validating the subnet"))
|
|
ux.Logger.PrintToUser("")
|
|
if err := waitForSubnetValidators(network, clusterName, subnetID, validateCheckTimeout, validateCheckPoolTime); err != nil {
|
|
return err
|
|
}
|
|
|
|
isEVMGenesis, _, err := app.HasSubnetEVMGenesis(subnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var awmRelayerHost *models.Host
|
|
if sc.TeleporterReady && sc.RunRelayer && isEVMGenesis {
|
|
// get or set AWM Relayer host and configure/stop service
|
|
awmRelayerHost, err = node.GetWarpRelayerHost(app, clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if awmRelayerHost == nil {
|
|
awmRelayerHost, err = chooseWarpRelayerHost(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// get awm-relayer latest version
|
|
relayerVersion, err := relayer.GetLatestRelayerReleaseVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := setWarpRelayerHost(awmRelayerHost, relayerVersion); err != nil {
|
|
return err
|
|
}
|
|
if err := setWarpRelayerSecurityGroupRule(clusterName, awmRelayerHost); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Stopping AWM Relayer Service"))
|
|
if err := ssh.RunSSHStopWarpRelayerService(awmRelayerHost); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Setting the nodes as subnet trackers"))
|
|
ux.Logger.PrintToUser("")
|
|
if err := syncSubnet(cmd, []string{clusterName, subnetName}); err != nil {
|
|
return err
|
|
}
|
|
if err := node.WaitForHealthyCluster(app, clusterName, healthCheckTimeout, healthCheckPoolTime); err != nil {
|
|
return err
|
|
}
|
|
blockchainID := sc.Networks[network.Name()].BlockchainID
|
|
if blockchainID == ids.Empty {
|
|
return constants.ErrNoBlockchainID
|
|
}
|
|
// update logging
|
|
if addMonitoring {
|
|
// set up subnet logs in Loki
|
|
if err = setUpSubnetLogging(clusterName, subnetName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := waitForClusterSubnetStatus(clusterName, subnetName, blockchainID, status.Validating, validateCheckTimeout, validateCheckPoolTime); err != nil {
|
|
return err
|
|
}
|
|
|
|
if b, err := hasWarpDeploys(clusterName); err != nil {
|
|
return err
|
|
} else if b {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Updating Proposer VMs"))
|
|
ux.Logger.PrintToUser("")
|
|
if err := updateProposerVMs(network); err != nil {
|
|
// not going to consider fatal, as warp messaging will be working fine after a failed first msg
|
|
ux.Logger.PrintToUser(logging.Yellow.Wrap("failure setting proposer: %s"), err)
|
|
}
|
|
}
|
|
|
|
if sc.TeleporterReady && sc.RunRelayer && isEVMGenesis {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Setting up Warp on subnet"))
|
|
ux.Logger.PrintToUser("")
|
|
flags := messengercmd.DeployFlags{
|
|
ChainFlags: contract.ChainSpec{
|
|
BlockchainName: subnetName,
|
|
},
|
|
PrivateKeyFlags: contract.PrivateKeyFlags{
|
|
KeyName: constants.WarpKeyName,
|
|
},
|
|
Network: networkoptions.NetworkFlags{
|
|
ClusterName: clusterName,
|
|
},
|
|
DeployMessenger: deployWarpMessenger,
|
|
DeployRegistry: deployWarpRegistry,
|
|
ForceRegistryDeploy: true,
|
|
Version: warpVersion,
|
|
MessengerContractAddressPath: warpMessengerContractAddressPath,
|
|
MessengerDeployerAddressPath: warpMessengerDeployerAddressPath,
|
|
MessengerDeployerTxPath: warpMessengerDeployerTxPath,
|
|
RegistryBydecodePath: warpRegistryBydecodePath,
|
|
IncludeCChain: true,
|
|
}
|
|
if err := messengercmd.CallDeploy([]string{}, flags, models.UndefinedNetwork); err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Starting AWM Relayer Service"))
|
|
ux.Logger.PrintToUser("")
|
|
if err := updateWarpRelayerFunds(network, sc, blockchainID); err != nil {
|
|
return err
|
|
}
|
|
if err := updateWarpRelayerHostConfig(network, awmRelayerHost, subnetName); 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)
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
|
|
ux.Logger.PrintToUser(logging.Green.Wrap("Subnet %s RPC URL: %s"), subnetName, network.BlockchainEndpoint(blockchainID.String()))
|
|
ux.Logger.PrintToUser("")
|
|
|
|
if addMonitoring {
|
|
if customGrafanaDashboardPath != "" {
|
|
if err = addCustomDashboard(clusterName, subnetName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// no need to check for error, as it's ok not to have monitoring host
|
|
monitoringHosts, _ := ansible.GetInventoryFromAnsibleInventoryFile(app.GetMonitoringInventoryDir(clusterName))
|
|
if len(monitoringHosts) > 0 {
|
|
getMonitoringHint(monitoringHosts[0].IP)
|
|
}
|
|
}
|
|
|
|
if err := deployClusterYAMLFile(clusterName, subnetName); err != nil {
|
|
return err
|
|
}
|
|
sendNodeWizMetrics()
|
|
return nil
|
|
}
|
|
|
|
func hasWarpDeploys(
|
|
clusterName string,
|
|
) (bool, error) {
|
|
clusterConfig, err := app.GetClusterConfig(clusterName)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
for _, deployedSubnetName := range clusterConfig.Subnets {
|
|
deployedSubnetIsEVMGenesis, _, err := app.HasSubnetEVMGenesis(deployedSubnetName)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
deployedSubnetSc, err := app.LoadSidecar(deployedSubnetName)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if deployedSubnetSc.TeleporterReady && deployedSubnetIsEVMGenesis {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func updateProposerVMs(
|
|
network models.Network,
|
|
) error {
|
|
clusterConfig, err := app.GetClusterConfig(network.ClusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, deployedSubnetName := range clusterConfig.Subnets {
|
|
deployedSubnetIsEVMGenesis, _, err := app.HasSubnetEVMGenesis(deployedSubnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deployedSubnetSc, err := app.LoadSidecar(deployedSubnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if deployedSubnetSc.TeleporterReady && deployedSubnetIsEVMGenesis {
|
|
ux.Logger.PrintToUser("Updating proposerVM on %s", deployedSubnetName)
|
|
blockchainID := deployedSubnetSc.Networks[network.Name()].BlockchainID
|
|
if blockchainID == ids.Empty {
|
|
return constants.ErrNoBlockchainID
|
|
}
|
|
if err := interchain.SetProposerVM(app, network, blockchainID.String(), deployedSubnetSc.TeleporterKey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
ux.Logger.PrintToUser("Updating proposerVM on c-chain")
|
|
return interchain.SetProposerVM(app, network, "C", "")
|
|
}
|
|
|
|
func setWarpRelayerHost(host *models.Host, relayerVersion string) error {
|
|
cloudID := host.GetCloudID()
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("configuring AWM Relayer on host %s", cloudID)
|
|
nodeConfig, err := app.LoadClusterNodeConfig(cloudID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := ssh.ComposeSSHSetupWarpRelayer(host, relayerVersion); err != nil {
|
|
return err
|
|
}
|
|
nodeConfig.IsWarpRelayer = true
|
|
return app.CreateNodeCloudConfigFile(cloudID, &nodeConfig)
|
|
}
|
|
|
|
func updateWarpRelayerHostConfig(network models.Network, host *models.Host, blockchainName string) error {
|
|
ux.Logger.PrintToUser("setting AWM Relayer on host %s to relay blockchain %s", host.GetCloudID(), blockchainName)
|
|
if err := addBlockchainToRelayerConf(network, host.GetCloudID(), blockchainName); err != nil {
|
|
return err
|
|
}
|
|
if err := ssh.RunSSHUploadNodeWarpRelayerConfig(host, app.GetNodeInstanceDirPath(host.GetCloudID())); err != nil {
|
|
return err
|
|
}
|
|
return ssh.RunSSHStartWarpRelayerService(host)
|
|
}
|
|
|
|
func chooseWarpRelayerHost(clusterName string) (*models.Host, error) {
|
|
// first look up for separate monitoring host
|
|
monitoringInventoryFile := app.GetMonitoringInventoryDir(clusterName)
|
|
if utils.FileExists(monitoringInventoryFile) {
|
|
monitoringHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(monitoringInventoryFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(monitoringHosts) > 0 {
|
|
return monitoringHosts[0], nil
|
|
}
|
|
}
|
|
// then look up for API nodes
|
|
clusterConfig, err := app.GetClusterConfig(clusterName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(clusterConfig.APINodes) > 0 {
|
|
return node.GetHostWithCloudID(app, clusterName, clusterConfig.APINodes[0])
|
|
}
|
|
// finally go for other hosts
|
|
if len(clusterConfig.Nodes) > 0 {
|
|
return node.GetHostWithCloudID(app, clusterName, clusterConfig.Nodes[0])
|
|
}
|
|
return nil, fmt.Errorf("no hosts found on cluster")
|
|
}
|
|
|
|
func updateWarpRelayerFunds(network models.Network, sc models.Sidecar, blockchainID ids.ID) error {
|
|
_, relayerAddress, _, err := relayer.GetDefaultRelayerKeyInfo(app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
warpKey, err := app.GetKey(sc.TeleporterKey, network, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := relayer.FundRelayer(
|
|
network.BlockchainEndpoint(blockchainID.String()),
|
|
warpKey.PrivKeyHex(),
|
|
relayerAddress,
|
|
); err != nil {
|
|
return nil
|
|
}
|
|
ewoqKey, err := app.GetKey("ewoq", network, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return relayer.FundRelayer(
|
|
network.BlockchainEndpoint("C"),
|
|
ewoqKey.PrivKeyHex(),
|
|
relayerAddress,
|
|
)
|
|
}
|
|
|
|
func deployClusterYAMLFile(clusterName, subnetName string) error {
|
|
var separateHosts []*models.Host
|
|
var err error
|
|
loadTestInventoryDir := app.GetLoadTestInventoryDir(clusterName)
|
|
if utils.FileExists(loadTestInventoryDir) {
|
|
separateHosts, err = ansible.GetInventoryFromAnsibleInventoryFile(loadTestInventoryDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
subnetID, chainID, err := getDeployedSubnetInfo(clusterName, subnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var externalHost *models.Host
|
|
if len(separateHosts) > 0 {
|
|
externalHost = separateHosts[0]
|
|
}
|
|
if err = createClusterYAMLFile(clusterName, subnetID, chainID, externalHost); err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.GreenCheckmarkToUser("Cluster information YAML file can be found at %s at local host", app.GetClusterYAMLFilePath(clusterName))
|
|
// deploy YAML file to external host, if it exists
|
|
if len(separateHosts) > 0 {
|
|
if err = ssh.RunSSHCopyYAMLFile(separateHosts[0], app.GetClusterYAMLFilePath(clusterName)); err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.GreenCheckmarkToUser("Cluster information YAML file can be found at /home/ubuntu/%s at external host", constants.ClusterYAMLFileName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func checkRPCCompatibility(
|
|
clusterName string,
|
|
subnetName string,
|
|
) error {
|
|
clusterConfig, err := app.GetClusterConfig(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
allHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hosts := clusterConfig.GetValidatorHosts(allHosts) // exlude api nodes
|
|
if len(validators) != 0 {
|
|
hosts, err = filterHosts(hosts, validators)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
defer node.DisconnectHosts(hosts)
|
|
return node.CheckHostsAreRPCCompatible(app, hosts, subnetName)
|
|
}
|
|
|
|
func waitForSubnetValidators(
|
|
network models.Network,
|
|
clusterName string,
|
|
subnetID ids.ID,
|
|
timeout time.Duration,
|
|
poolTime time.Duration,
|
|
) error {
|
|
ux.Logger.PrintToUser("Waiting for node(s) in cluster %s to be validators of subnet ID %s...", clusterName, subnetID)
|
|
clusterConfig, err := app.GetClusterConfig(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
allHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hosts := clusterConfig.GetValidatorHosts(allHosts) // exlude api nodes
|
|
if len(validators) != 0 {
|
|
hosts, err = filterHosts(hosts, validators)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
defer node.DisconnectHosts(hosts)
|
|
nodeIDMap, failedNodesMap := getNodeIDs(hosts)
|
|
startTime := time.Now()
|
|
for {
|
|
failedNodes := []string{}
|
|
for _, host := range hosts {
|
|
nodeID, b := nodeIDMap[host.NodeID]
|
|
if !b {
|
|
err, b := failedNodesMap[host.NodeID]
|
|
if !b {
|
|
return fmt.Errorf("expected to found an error for non mapped node")
|
|
}
|
|
return err
|
|
}
|
|
isValidator, err := subnet.IsSubnetValidator(subnetID, nodeID, network)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !isValidator {
|
|
failedNodes = append(failedNodes, host.GetCloudID())
|
|
}
|
|
}
|
|
if len(failedNodes) == 0 {
|
|
ux.Logger.PrintToUser("Nodes validating subnet ID %s after %d seconds", subnetID, uint32(time.Since(startTime).Seconds()))
|
|
return nil
|
|
}
|
|
if time.Since(startTime) > timeout {
|
|
ux.Logger.PrintToUser("Nodes not validating subnet ID %sf", subnetID)
|
|
for _, failedNode := range failedNodes {
|
|
ux.Logger.PrintToUser(" " + failedNode)
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
return fmt.Errorf("cluster %s not validating subnet ID %s after %d seconds", clusterName, subnetID, uint32(timeout.Seconds()))
|
|
}
|
|
time.Sleep(poolTime)
|
|
}
|
|
}
|
|
|
|
func waitForClusterSubnetStatus(
|
|
clusterName string,
|
|
subnetName string,
|
|
blockchainID ids.ID,
|
|
targetStatus status.BlockchainStatus,
|
|
timeout time.Duration,
|
|
poolTime time.Duration,
|
|
) error {
|
|
ux.Logger.PrintToUser("")
|
|
ux.Logger.PrintToUser("Waiting for node(s) in cluster %s to be %s subnet %s...", clusterName, strings.ToLower(targetStatus.String()), subnetName)
|
|
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
|
|
if len(validators) != 0 {
|
|
hosts, err = filterHosts(hosts, validators)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
defer node.DisconnectHosts(hosts)
|
|
startTime := time.Now()
|
|
for {
|
|
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 syncstatus, err := ssh.RunSSHSubnetSyncStatus(host, blockchainID.String()); err != nil {
|
|
nodeResults.AddResult(host.NodeID, nil, err)
|
|
return
|
|
} else {
|
|
if subnetSyncStatus, err := parseSubnetSyncOutput(syncstatus); err != nil {
|
|
nodeResults.AddResult(host.NodeID, nil, err)
|
|
return
|
|
} else {
|
|
nodeResults.AddResult(host.NodeID, subnetSyncStatus, err)
|
|
}
|
|
}
|
|
}(&wgResults, host)
|
|
}
|
|
wg.Wait()
|
|
if wgResults.HasErrors() {
|
|
return fmt.Errorf("failed to check sync status for node(s) %s", wgResults.GetErrorHostMap())
|
|
}
|
|
failedNodes := []string{}
|
|
for host, subnetSyncStatus := range wgResults.GetResultMap() {
|
|
if subnetSyncStatus != targetStatus.String() {
|
|
failedNodes = append(failedNodes, host)
|
|
}
|
|
}
|
|
if len(failedNodes) == 0 {
|
|
ux.Logger.PrintToUser("Nodes %s %s after %d seconds", targetStatus.String(), subnetName, uint32(time.Since(startTime).Seconds()))
|
|
return nil
|
|
}
|
|
if time.Since(startTime) > timeout {
|
|
ux.Logger.PrintToUser("Nodes not %s %s", targetStatus.String(), subnetName)
|
|
for _, failedNode := range failedNodes {
|
|
ux.Logger.PrintToUser(" " + failedNode)
|
|
}
|
|
ux.Logger.PrintToUser("")
|
|
return fmt.Errorf("cluster not %s subnet %s after %d seconds", strings.ToLower(targetStatus.String()), subnetName, uint32(timeout.Seconds()))
|
|
}
|
|
time.Sleep(poolTime)
|
|
}
|
|
}
|
|
|
|
func checkClusterIsADevnet(clusterName string) error {
|
|
exists, err := app.ClusterExists(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exists {
|
|
return 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 nil
|
|
}
|
|
|
|
func filterHosts(hosts []*models.Host, nodes []string) ([]*models.Host, error) {
|
|
indices := set.Set[int]{}
|
|
for _, node := range nodes {
|
|
added := false
|
|
for i, host := range hosts {
|
|
cloudID := host.GetCloudID()
|
|
ip := host.IP
|
|
nodeID, err := getNodeID(app.GetNodeInstanceDirPath(cloudID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if slices.Contains([]string{cloudID, ip, nodeID.String()}, node) {
|
|
added = true
|
|
indices.Add(i)
|
|
}
|
|
}
|
|
if !added {
|
|
return nil, fmt.Errorf("node %q not found", node)
|
|
}
|
|
}
|
|
filteredHosts := []*models.Host{}
|
|
for i, host := range hosts {
|
|
if indices.Contains(i) {
|
|
filteredHosts = append(filteredHosts, host)
|
|
}
|
|
}
|
|
return filteredHosts, nil
|
|
}
|
|
|
|
func setWarpRelayerSecurityGroupRule(clusterName string, awmRelayerHost *models.Host) error {
|
|
clusterConfig, err := app.GetClusterConfig(clusterName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hasGCPNodes := false
|
|
lastRegion := ""
|
|
var ec2Svc *awsAPI.AwsCloud
|
|
for _, cloudID := range clusterConfig.GetCloudIDs() {
|
|
nodeConfig, err := app.LoadClusterNodeConfig(cloudID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch {
|
|
case nodeConfig.CloudService == "" || nodeConfig.CloudService == constants.AWSCloudService:
|
|
if nodeConfig.Region != lastRegion {
|
|
ec2Svc, err = awsAPI.NewAwsCloud(awsProfile, nodeConfig.Region)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
lastRegion = nodeConfig.Region
|
|
}
|
|
securityGroupExists, sg, err := ec2Svc.CheckSecurityGroupExists(nodeConfig.SecurityGroup)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !securityGroupExists {
|
|
return fmt.Errorf("security group %s doesn't exist in region %s", nodeConfig.SecurityGroup, nodeConfig.Region)
|
|
}
|
|
if inSG := awsAPI.CheckIPInSg(&sg, awmRelayerHost.IP, constants.LuxdAPIPort); !inSG {
|
|
if err = ec2Svc.AddSecurityGroupRule(
|
|
*sg.GroupId,
|
|
"ingress",
|
|
"tcp",
|
|
awmRelayerHost.IP+constants.IPAddressSuffix,
|
|
constants.LuxdAPIPort,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case nodeConfig.CloudService == constants.GCPCloudService:
|
|
hasGCPNodes = true
|
|
default:
|
|
return fmt.Errorf("cloud %s is not supported", nodeConfig.CloudService)
|
|
}
|
|
}
|
|
if hasGCPNodes {
|
|
if err := setGCPWarpRelayerSecurityGroupRule(awmRelayerHost); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func sendNodeWizMetrics() {
|
|
flags := make(map[string]string)
|
|
populateSubnetVMMetrics(flags, wizSubnet)
|
|
metrics.HandleTracking(app, flags, nil)
|
|
}
|
|
|
|
func populateSubnetVMMetrics(flags map[string]string, subnetName string) {
|
|
sc, err := app.LoadSidecar(subnetName)
|
|
if err == nil {
|
|
switch sc.VM {
|
|
case models.SubnetEvm:
|
|
flags[constants.MetricsSubnetVM] = "Subnet-EVM"
|
|
case models.CustomVM:
|
|
flags[constants.MetricsSubnetVM] = "Custom-VM"
|
|
flags[constants.MetricsCustomVMRepoURL] = sc.CustomVMRepoURL
|
|
flags[constants.MetricsCustomVMBranch] = sc.CustomVMBranch
|
|
flags[constants.MetricsCustomVMBuildScript] = sc.CustomVMBuildScript
|
|
}
|
|
}
|
|
flags[constants.MetricsEnableMonitoring] = strconv.FormatBool(addMonitoring)
|
|
}
|
|
|
|
// setUPSubnetLogging sets up the subnet logging for the subnet
|
|
func setUpSubnetLogging(clusterName, subnetName string) error {
|
|
_, chainID, err := getDeployedSubnetInfo(clusterName, subnetName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
wg := sync.WaitGroup{}
|
|
wgResults := models.NodeResults{}
|
|
spinSession := ux.NewUserSpinner()
|
|
hosts, err := ansible.GetInventoryFromAnsibleInventoryFile(app.GetAnsibleInventoryDirPath(clusterName))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
monitoringInventoryPath := app.GetMonitoringInventoryDir(clusterName)
|
|
monitoringHosts, err := ansible.GetInventoryFromAnsibleInventoryFile(monitoringInventoryPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, host := range hosts {
|
|
if !addMonitoring {
|
|
continue
|
|
}
|
|
wg.Add(1)
|
|
go func(host *models.Host) {
|
|
defer wg.Done()
|
|
spinner := spinSession.SpinToUser(utils.ScriptLog(host.NodeID, "Setup Subnet Logs"))
|
|
cloudID := host.GetCloudID()
|
|
nodeID, err := getNodeID(app.GetNodeInstanceDirPath(cloudID))
|
|
if err != nil {
|
|
wgResults.AddResult(host.NodeID, nil, err)
|
|
ux.SpinFailWithError(spinner, "", err)
|
|
return
|
|
}
|
|
if err = ssh.RunSSHSetupPromtailConfig(host, monitoringHosts[0].IP, constants.LuxdLokiPort, cloudID, nodeID.String(), chainID); err != nil {
|
|
wgResults.AddResult(host.NodeID, nil, err)
|
|
ux.SpinFailWithError(spinner, "", err)
|
|
return
|
|
}
|
|
if err := docker.RestartDockerComposeService(host, utils.GetRemoteComposeFile(), "promtail", constants.SSHLongRunningScriptTimeout); err != nil {
|
|
wgResults.AddResult(host.NodeID, nil, err)
|
|
ux.SpinFailWithError(spinner, "", err)
|
|
return
|
|
}
|
|
ux.SpinComplete(spinner)
|
|
}(host)
|
|
}
|
|
wg.Wait()
|
|
for _, node := range hosts {
|
|
if wgResults.HasIDWithError(node.NodeID) {
|
|
ux.Logger.RedXToUser("Node %s is ERROR with error: %s", node.NodeID, wgResults.GetErrorHostMap()[node.NodeID])
|
|
}
|
|
}
|
|
spinSession.Stop()
|
|
return nil
|
|
}
|
|
|
|
func addBlockchainToRelayerConf(network models.Network, cloudNodeID string, blockchainName string) error {
|
|
_, relayerAddress, relayerPrivateKey, err := relayer.GetDefaultRelayerKeyInfo(app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
storageBasePath := constants.WarpRelayerDockerDir
|
|
configBasePath := app.GetNodeInstanceDirPath(cloudNodeID)
|
|
|
|
configPath := app.GetWarpRelayerServiceConfigPath(configBasePath)
|
|
if err := os.MkdirAll(filepath.Dir(configPath), constants.DefaultPerms755); err != nil {
|
|
return err
|
|
}
|
|
ux.Logger.PrintToUser("updating configuration file %s", configPath)
|
|
|
|
if err := relayer.CreateBaseRelayerConfigIfMissing(
|
|
configPath,
|
|
logging.Info.LowerString(),
|
|
app.GetWarpRelayerServiceStorageDir(storageBasePath),
|
|
constants.RemoteWarpRelayerMetricsPort,
|
|
network,
|
|
true,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
chainSpec := contract.ChainSpec{CChain: true}
|
|
subnetID, err := contract.GetSubnetID(app, network, chainSpec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
blockchainID, err := contract.GetBlockchainID(app, network, chainSpec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
registryAddress, messengerAddress, err := contract.GetWarpInfo(app, network, chainSpec, false, false, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rpcEndpoint, wsEndpoint, err := contract.GetBlockchainEndpoints(app, network, chainSpec, false, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = relayer.AddSourceAndDestinationToRelayerConfig(
|
|
configPath,
|
|
rpcEndpoint,
|
|
wsEndpoint,
|
|
subnetID.String(),
|
|
blockchainID.String(),
|
|
registryAddress,
|
|
messengerAddress,
|
|
relayerAddress,
|
|
relayerPrivateKey,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
chainSpec = contract.ChainSpec{BlockchainName: blockchainName}
|
|
subnetID, err = contract.GetSubnetID(app, network, chainSpec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
blockchainID, err = contract.GetBlockchainID(app, network, chainSpec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
registryAddress, messengerAddress, err = contract.GetWarpInfo(app, network, chainSpec, false, false, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rpcEndpoint, wsEndpoint, err = contract.GetBlockchainEndpoints(app, network, chainSpec, false, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = relayer.AddSourceAndDestinationToRelayerConfig(
|
|
configPath,
|
|
rpcEndpoint,
|
|
wsEndpoint,
|
|
subnetID.String(),
|
|
blockchainID.String(),
|
|
registryAddress,
|
|
messengerAddress,
|
|
relayerAddress,
|
|
relayerPrivateKey,
|
|
); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|