From f9d85e246f4a601ad835bb05420413fabcd3e71d Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Wed, 28 Aug 2024 15:16:48 -0700 Subject: [PATCH 1/9] make sure that awm relayer is a separate role --- node/supported.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/node/supported.go b/node/supported.go index b60d8bc..3c36cc9 100644 --- a/node/supported.go +++ b/node/supported.go @@ -101,5 +101,8 @@ func CheckRoles(roles []SupportedRole) error { if slices.Contains(roles, Monitor) && len(roles) > 1 { return fmt.Errorf("%v role cannot be combined with other roles", Monitor) } + if slices.Contains(roles, AWMRelayer) && len(roles) > 1 { + return fmt.Errorf("%v role cannot be combined with other roles", AWMRelayer) + } return nil } From c4331c40a59d0b46eecc300abef51ef8a7bfe07d Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Wed, 28 Aug 2024 15:33:14 -0700 Subject: [PATCH 2/9] add container logs --- node/dockerLogs.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 node/dockerLogs.go diff --git a/node/dockerLogs.go b/node/dockerLogs.go new file mode 100644 index 0000000..8b0a7e3 --- /dev/null +++ b/node/dockerLogs.go @@ -0,0 +1,33 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package node + +import ( + "fmt" + "strings" + "time" +) + +func (h *Node) GetContainerLogs(containerName string, tailLines uint, timeout time.Duration) ([]string, error) { + if containerName == "" { + return nil, fmt.Errorf("container name cannot be empty") + } + tailLinesString := "all" + if tailLines > 0 { + tailLinesString = fmt.Sprintf("%d", tailLines) + } + output, err := h.Commandf(nil, timeout, "docker logs --tail %s %s", tailLinesString, containerName) + if err != nil { + return nil, err + } + return strings.Split(string(output), "\n"), nil +} + +func (h *Node) GetAvalanchegoLogs(tailLines uint, timeout time.Duration) ([]string, error) { + return h.GetContainerLogs("avalanchego", tailLines, timeout) +} + +func (h *Node) GetAWMRelayerLogs(tailLines uint, timeout time.Duration) ([]string, error) { + return h.GetContainerLogs("awm-relayer", tailLines, timeout) +} From 6409a07478dc0988c9a73d248cf80cfe2703f76d Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Fri, 30 Aug 2024 13:58:47 -0700 Subject: [PATCH 3/9] add awm relayer config --- constants/constants.go | 1 + node/config/awmRelayer.go | 26 +++++++ node/dockerConfig.go | 72 ++++++++++++++++++++ node/dockerLogs.go | 6 +- node/dockerSsh.go | 24 ++++++- node/templates/awmrelayer.docker-compose.yml | 10 +-- 6 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 node/config/awmRelayer.go diff --git a/constants/constants.go b/constants/constants.go index cb835fe..a9d74e9 100644 --- a/constants/constants.go +++ b/constants/constants.go @@ -70,6 +70,7 @@ const ( AvalancheGoGitRepo = "https://github.com/ava-labs/avalanchego" SubnetEVMRepoName = "subnet-evm" + CloudNodeAWMRelayerPath = "/home/ubuntu/.awm-relayer" AWMRelayerInstallDir = "awm-relayer" AWMRelayerConfigFilename = "awm-relayer-config.json" diff --git a/node/config/awmRelayer.go b/node/config/awmRelayer.go new file mode 100644 index 0000000..949d07e --- /dev/null +++ b/node/config/awmRelayer.go @@ -0,0 +1,26 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package services + +import ( + "path/filepath" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" +) + +const DockerAWMRelayerPath = "/.awm-relayer" + +func GetRemoteAMWRelayerConfig() string { + return filepath.Join(constants.CloudNodeAWMRelayerPath, constants.AWMRelayerConfigFilename) +} + +func GetDockerAWMRelayerFolder() string { + return filepath.Join(DockerAWMRelayerPath, "storage") +} + +func AWMRelayerFoldersToCreate() []string { + return []string{ + filepath.Join(constants.CloudNodeAWMRelayerPath, "storage"), + } +} diff --git a/node/dockerConfig.go b/node/dockerConfig.go index d327820..008e56a 100644 --- a/node/dockerConfig.go +++ b/node/dockerConfig.go @@ -4,11 +4,19 @@ package node import ( + "encoding/json" + "errors" + "fmt" "os" + "slices" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/interchain/relayer" + "github.com/ava-labs/avalanche-tooling-sdk-go/key" remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/awm-relayer/config" ) // PrepareAvalanchegoConfig creates the config files for the AvalancheGo @@ -102,3 +110,67 @@ func prepareGrafanaConfig() (string, string, string, string, error) { } return grafanaConfigFile.Name(), grafanaDashboardsFile.Name(), grafanaDataSourceFile.Name(), grafanaPromDataSourceFile.Name(), nil } + +func (h *Node) GetAMWRelayerConfig() (*config.Config, error) { + remoteAWMConf := remoteconfig.GetRemoteAMWRelayerConfig() + if slices.Contains(h.Roles, AWMRelayer) { + if configExists, err := h.FileExists(); err != nil || !configExists { + return nil, fmt.Errorf("%s: config file %s does not exist or not available", h.NodeID, remoteAWMConf) + } + if c, err := h.ReadFileBytes(remoteAWMConf, constants.SSHFileOpsTimeout); err != nil { + return nil, fmt.Errorf("%s: failed to read config file %s: %w", h.NodeID, remoteAWMConf, err) + } else { + awmConfig := &config.Config{} + if json.Unmarshal(c, &awmConfig) != nil { + return nil, fmt.Errorf("%s: failed to parse config file %s", remoteAWMConf) + } else { + return awmConfig, nil + } + } + } else { + return nil, errors.New("node is not a AWM Relayer") + } +} + +// AddBlockchainToRelayerConfig adds a blockchain to the AWM relayer config +func (h *Node) AddBlockchainToRelayerConfig( + rpcEndpoint string, + wsEndoint string, + subnetID ids.ID, + blockchainID ids.ID, + registryAddress string, + messengerAddress string, + relayerKey *key.SoftKey, +) error { + if !slices.Contains(h.Roles, AWMRelayer) { + return errors.New("node is not a AWM Relayer") + } + awmConfig, err := h.GetAMWRelayerConfig() + if err != nil { + return err + } + relayer.AddBlockchainToRelayerConfig( + awmConfig, + rpcEndpoint, + wsEndoint, + subnetID, + blockchainID, + registryAddress, + messengerAddress, + relayerKey.C(), + relayerKey.PrivKeyHex(), + ) + tmpRelayerConfig, err := os.CreateTemp("", "avalancecli-awm-relayer-config-*.yml") + if err != nil { + return err + } + defer os.Remove(tmpRelayerConfig.Name()) + if relayer.SaveRelayerConfig(awmConfig, tmpRelayerConfig.Name()) != nil { + return err + } + + if err := h.Upload(tmpRelayerConfig.Name(), remoteconfig.GetRemoteAMWRelayerConfig(), constants.SSHFileOpsTimeout); err != nil { + return err + } + return h.RestartDockerComposeService(utils.GetRemoteComposeFile(), constants.ServiceAWMRelayer, constants.SSHLongRunningScriptTimeout) +} diff --git a/node/dockerLogs.go b/node/dockerLogs.go index 8b0a7e3..1b8ee4e 100644 --- a/node/dockerLogs.go +++ b/node/dockerLogs.go @@ -7,6 +7,8 @@ import ( "fmt" "strings" "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/constants" ) func (h *Node) GetContainerLogs(containerName string, tailLines uint, timeout time.Duration) ([]string, error) { @@ -25,9 +27,9 @@ func (h *Node) GetContainerLogs(containerName string, tailLines uint, timeout ti } func (h *Node) GetAvalanchegoLogs(tailLines uint, timeout time.Duration) ([]string, error) { - return h.GetContainerLogs("avalanchego", tailLines, timeout) + return h.GetContainerLogs(constants.ServiceAvalanchego, tailLines, timeout) } func (h *Node) GetAWMRelayerLogs(tailLines uint, timeout time.Duration) ([]string, error) { - return h.GetContainerLogs("awm-relayer", tailLines, timeout) + return h.GetContainerLogs(constants.ServiceAWMRelayer, tailLines, timeout) } diff --git a/node/dockerSsh.go b/node/dockerSsh.go index f219657..2c9e08f 100644 --- a/node/dockerSsh.go +++ b/node/dockerSsh.go @@ -9,9 +9,12 @@ import ( "path/filepath" "time" + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" "github.com/ava-labs/avalanche-tooling-sdk-go/constants" + "github.com/ava-labs/avalanche-tooling-sdk-go/interchain/relayer" remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" + "github.com/ava-labs/avalanchego/utils/logging" ) // ValidateComposeFile validates a docker-compose file on a remote node. @@ -114,7 +117,26 @@ func (h *Node) ComposeSSHSetupMonitoring() error { dockerComposeInputs{}) } -func (h *Node) ComposeSSHSetupAWMRelayer() error { +func (h *Node) ComposeSSHSetupAWMRelayer(network avalanche.Network) error { + for _, folder := range remoteconfig.AWMRelayerFoldersToCreate() { + if h.MkdirAll(folder, constants.SSHFileOpsTimeout) != nil { + return fmt.Errorf("error creating folder %s on node %s", folder, h.NodeID) + } + } + // provide basic configuration for AWM Relayer + tmpRelayerConfig, err := os.CreateTemp("", "avalancecli-awm-relayer-config-*.yml") + if err != nil { + return err + } + defer os.Remove(tmpRelayerConfig.Name()) + config := relayer.CreateBaseRelayerConfig(logging.Info.LowerString(), remoteconfig.GetDockerAWMRelayerFolder(), 0, network) + if relayer.SaveRelayerConfig(config, tmpRelayerConfig.Name()) != nil { + return err + } + // upload the configuration file to the remote node + if err := h.Upload(tmpRelayerConfig.Name(), remoteconfig.GetRemoteAMWRelayerConfig(), constants.SSHFileOpsTimeout); err != nil { + return err + } return h.ComposeOverSSH("Setup AWM Relayer", constants.SSHScriptTimeout, "templates/awmrelayer.docker-compose.yml", diff --git a/node/templates/awmrelayer.docker-compose.yml b/node/templates/awmrelayer.docker-compose.yml index bbf48c0..38dbf95 100644 --- a/node/templates/awmrelayer.docker-compose.yml +++ b/node/templates/awmrelayer.docker-compose.yml @@ -1,14 +1,10 @@ -name: avalanche-cli +name: awm-relayer services: awm-relayer: - image: avaplatform/awm-relayer + image: avaplatform/awm-relayer:{{ .AWMRelayerVersion }} container_name: awm-relayer restart: unless-stopped user: "1000:1000" # ubuntu user - networks: - - avalanchego_net volumes: - - /home/ubuntu/.avalanche-cli/services/awm-relayer:/.awm-relayer:rw + - /home/ubuntu/.awm-relayer:/.awm-relayer:rw command: 'awm-relayer --config-file /.awm-relayer/awm-relayer-config.json' -networks: - avalanchego_net: From a30a2f2bd76cd8728b42244585e3efc8abdb4fea Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Tue, 3 Sep 2024 15:22:35 -0700 Subject: [PATCH 4/9] fixes --- node/create.go | 6 +++--- node/dockerConfig.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/create.go b/node/create.go index 218e377..47164b0 100644 --- a/node/create.go +++ b/node/create.go @@ -272,7 +272,7 @@ func provisionHost(node Node, nodeParams *NodeParams) error { return err } case AWMRelayer: - if err := provisionAWMRelayerHost(node); err != nil { + if err := provisionAWMRelayerHost(node, nodeParams.Network); err != nil { return err } default: @@ -329,8 +329,8 @@ func provisionMonitoringHost(node Node) error { return nil } -func provisionAWMRelayerHost(node Node) error { // stub - if err := node.ComposeSSHSetupAWMRelayer(); err != nil { +func provisionAWMRelayerHost(node Node, network avalanche.Network) error { // stub + if err := node.ComposeSSHSetupAWMRelayer(network); err != nil { return err } return node.StartDockerComposeService(utils.GetRemoteComposeFile(), constants.ServiceAWMRelayer, constants.SSHLongRunningScriptTimeout) diff --git a/node/dockerConfig.go b/node/dockerConfig.go index 008e56a..90b71a8 100644 --- a/node/dockerConfig.go +++ b/node/dockerConfig.go @@ -114,7 +114,7 @@ func prepareGrafanaConfig() (string, string, string, string, error) { func (h *Node) GetAMWRelayerConfig() (*config.Config, error) { remoteAWMConf := remoteconfig.GetRemoteAMWRelayerConfig() if slices.Contains(h.Roles, AWMRelayer) { - if configExists, err := h.FileExists(); err != nil || !configExists { + if configExists, err := h.FileExists(remoteAWMConf); err != nil || !configExists { return nil, fmt.Errorf("%s: config file %s does not exist or not available", h.NodeID, remoteAWMConf) } if c, err := h.ReadFileBytes(remoteAWMConf, constants.SSHFileOpsTimeout); err != nil { From 3e575a49bde6f42790ffda464f00fc713ec60b5b Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Tue, 3 Sep 2024 15:48:15 -0700 Subject: [PATCH 5/9] fix fmt.errorf --- node/dockerConfig.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/dockerConfig.go b/node/dockerConfig.go index 90b71a8..06ee145 100644 --- a/node/dockerConfig.go +++ b/node/dockerConfig.go @@ -122,7 +122,7 @@ func (h *Node) GetAMWRelayerConfig() (*config.Config, error) { } else { awmConfig := &config.Config{} if json.Unmarshal(c, &awmConfig) != nil { - return nil, fmt.Errorf("%s: failed to parse config file %s", remoteAWMConf) + return nil, fmt.Errorf("%s: failed to parse config file %s", h.NodeID, remoteAWMConf) } else { return awmConfig, nil } From d81e51846283e54e65589e4ebf70c4a8717d20d3 Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Tue, 3 Sep 2024 18:10:31 -0700 Subject: [PATCH 6/9] refactor for the latest relayer --- node/create.go | 9 ++++-- node/docker_compose.go | 12 ++++---- node/docker_config.go | 41 +++++++++++++++----------- node/{dockerLogs.go => docker_logs.go} | 0 node/docker_ssh.go | 26 +++++++++------- node/ssh.go | 12 ++++---- 6 files changed, 57 insertions(+), 43 deletions(-) rename node/{dockerLogs.go => docker_logs.go} (100%) diff --git a/node/create.go b/node/create.go index 47164b0..2af98da 100644 --- a/node/create.go +++ b/node/create.go @@ -43,6 +43,9 @@ type NodeParams struct { // AvalancheGoVersion is the version of Avalanche Go to install in the created node AvalancheGoVersion string + // AWMRelayerVersion is the version of AWM Relayer to install in the created node + AWMRelayerVersion string + // UseStaticIP is whether the created node should have static IP attached to it. Note that // assigning Static IP to a node may incur additional charges on AWS / GCP. There could also be // a limit to how many Static IPs you can have in a region in AWS & GCP. @@ -272,7 +275,7 @@ func provisionHost(node Node, nodeParams *NodeParams) error { return err } case AWMRelayer: - if err := provisionAWMRelayerHost(node, nodeParams.Network); err != nil { + if err := provisionAWMRelayerHost(node, nodeParams); err != nil { return err } default: @@ -329,8 +332,8 @@ func provisionMonitoringHost(node Node) error { return nil } -func provisionAWMRelayerHost(node Node, network avalanche.Network) error { // stub - if err := node.ComposeSSHSetupAWMRelayer(network); err != nil { +func provisionAWMRelayerHost(node Node, nodeParams *NodeParams) error { // stub + if err := node.ComposeSSHSetupAWMRelayer(nodeParams.Network, nodeParams.AWMRelayerVersion); err != nil { return err } return node.StartDockerComposeService(utils.GetRemoteComposeFile(), constants.ServiceAWMRelayer, constants.SSHLongRunningScriptTimeout) diff --git a/node/docker_compose.go b/node/docker_compose.go index 7a2ad34..947c2b6 100644 --- a/node/docker_compose.go +++ b/node/docker_compose.go @@ -19,12 +19,12 @@ import ( ) type dockerComposeInputs struct { - WithMonitoring bool - WithAvalanchego bool - AvalanchegoVersion string - E2E bool - E2EIP string - E2ESuffix string + WithMonitoring bool + WithAvalanchego bool + Version string + E2E bool + E2EIP string + E2ESuffix string } //go:embed templates/*.docker-compose.yml diff --git a/node/docker_config.go b/node/docker_config.go index 06ee145..388936d 100644 --- a/node/docker_config.go +++ b/node/docker_config.go @@ -113,23 +113,25 @@ func prepareGrafanaConfig() (string, string, string, string, error) { func (h *Node) GetAMWRelayerConfig() (*config.Config, error) { remoteAWMConf := remoteconfig.GetRemoteAMWRelayerConfig() - if slices.Contains(h.Roles, AWMRelayer) { - if configExists, err := h.FileExists(remoteAWMConf); err != nil || !configExists { - return nil, fmt.Errorf("%s: config file %s does not exist or not available", h.NodeID, remoteAWMConf) - } - if c, err := h.ReadFileBytes(remoteAWMConf, constants.SSHFileOpsTimeout); err != nil { - return nil, fmt.Errorf("%s: failed to read config file %s: %w", h.NodeID, remoteAWMConf, err) - } else { - awmConfig := &config.Config{} - if json.Unmarshal(c, &awmConfig) != nil { - return nil, fmt.Errorf("%s: failed to parse config file %s", h.NodeID, remoteAWMConf) - } else { - return awmConfig, nil - } - } - } else { - return nil, errors.New("node is not a AWM Relayer") + if !slices.Contains(h.Roles, AWMRelayer) { + return nil, errors.New("node is not an AWM Relayer") + } + + if configExists, err := h.FileExists(remoteAWMConf); err != nil || !configExists { + return nil, fmt.Errorf("%s: config file %s does not exist or not available", h.NodeID, remoteAWMConf) + } + + c, err := h.ReadFileBytes(remoteAWMConf, constants.SSHFileOpsTimeout) + if err != nil { + return nil, fmt.Errorf("%s: failed to read config file %s: %w", h.NodeID, remoteAWMConf, err) } + + awmConfig := &config.Config{} + if err := json.Unmarshal(c, &awmConfig); err != nil { + return nil, fmt.Errorf("%s: failed to parse config file %s", h.NodeID, remoteAWMConf) + } + + return awmConfig, nil } // AddBlockchainToRelayerConfig adds a blockchain to the AWM relayer config @@ -165,10 +167,13 @@ func (h *Node) AddBlockchainToRelayerConfig( return err } defer os.Remove(tmpRelayerConfig.Name()) - if relayer.SaveRelayerConfig(awmConfig, tmpRelayerConfig.Name()) != nil { + configData, err := relayer.SerializeRelayerConfig(awmConfig) + if err != nil { + return err + } + if _, err := tmpRelayerConfig.Write(configData); err != nil { return err } - if err := h.Upload(tmpRelayerConfig.Name(), remoteconfig.GetRemoteAMWRelayerConfig(), constants.SSHFileOpsTimeout); err != nil { return err } diff --git a/node/dockerLogs.go b/node/docker_logs.go similarity index 100% rename from node/dockerLogs.go rename to node/docker_logs.go diff --git a/node/docker_ssh.go b/node/docker_ssh.go index 2c9e08f..3f874cb 100644 --- a/node/docker_ssh.go +++ b/node/docker_ssh.go @@ -49,12 +49,12 @@ func (h *Node) ComposeSSHSetupNode(networkID string, subnetsToTrack []string, av constants.SSHScriptTimeout, "templates/avalanchego.docker-compose.yml", dockerComposeInputs{ - AvalanchegoVersion: avalancheGoVersion, - WithMonitoring: withMonitoring, - WithAvalanchego: true, - E2E: utils.IsE2E(), - E2EIP: utils.E2EConvertIP(h.IP), - E2ESuffix: utils.E2ESuffix(h.IP), + Version: avalancheGoVersion, + WithMonitoring: withMonitoring, + WithAvalanchego: true, + E2E: utils.IsE2E(), + E2EIP: utils.E2EConvertIP(h.IP), + E2ESuffix: utils.E2ESuffix(h.IP), }) } @@ -117,7 +117,7 @@ func (h *Node) ComposeSSHSetupMonitoring() error { dockerComposeInputs{}) } -func (h *Node) ComposeSSHSetupAWMRelayer(network avalanche.Network) error { +func (h *Node) ComposeSSHSetupAWMRelayer(network avalanche.Network, awmRelayerVersion string) error { for _, folder := range remoteconfig.AWMRelayerFoldersToCreate() { if h.MkdirAll(folder, constants.SSHFileOpsTimeout) != nil { return fmt.Errorf("error creating folder %s on node %s", folder, h.NodeID) @@ -129,8 +129,12 @@ func (h *Node) ComposeSSHSetupAWMRelayer(network avalanche.Network) error { return err } defer os.Remove(tmpRelayerConfig.Name()) - config := relayer.CreateBaseRelayerConfig(logging.Info.LowerString(), remoteconfig.GetDockerAWMRelayerFolder(), 0, network) - if relayer.SaveRelayerConfig(config, tmpRelayerConfig.Name()) != nil { + awmConfig := relayer.CreateBaseRelayerConfig(logging.Info.LowerString(), remoteconfig.GetDockerAWMRelayerFolder(), 0, network) + configData, err := relayer.SerializeRelayerConfig(awmConfig) + if err != nil { + return err + } + if _, err := tmpRelayerConfig.Write(configData); err != nil { return err } // upload the configuration file to the remote node @@ -140,5 +144,7 @@ func (h *Node) ComposeSSHSetupAWMRelayer(network avalanche.Network) error { return h.ComposeOverSSH("Setup AWM Relayer", constants.SSHScriptTimeout, "templates/awmrelayer.docker-compose.yml", - dockerComposeInputs{}) + dockerComposeInputs{ + Version: awmRelayerVersion, + }) } diff --git a/node/ssh.go b/node/ssh.go index fca99ea..41f18b1 100644 --- a/node/ssh.go +++ b/node/ssh.go @@ -135,12 +135,12 @@ func (h *Node) RunSSHUpgradeAvalanchego(avalancheGoVersion string) error { constants.SSHScriptTimeout, "templates/avalanchego.docker-compose.yml", dockerComposeInputs{ - AvalanchegoVersion: avalancheGoVersion, - WithMonitoring: withMonitoring, - WithAvalanchego: true, - E2E: utils.IsE2E(), - E2EIP: utils.E2EConvertIP(h.IP), - E2ESuffix: utils.E2ESuffix(h.IP), + Version: avalancheGoVersion, + WithMonitoring: withMonitoring, + WithAvalanchego: true, + E2E: utils.IsE2E(), + E2EIP: utils.E2EConvertIP(h.IP), + E2ESuffix: utils.E2ESuffix(h.IP), }); err != nil { return err } From 0f23efefaaa9f9700b3c157c05abbe6569e79c74 Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Tue, 3 Sep 2024 19:11:07 -0700 Subject: [PATCH 7/9] set and get for awm relayer config --- node/docker_config.go | 27 +------------------- node/templates/awmrelayer.docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 27 deletions(-) diff --git a/node/docker_config.go b/node/docker_config.go index 388936d..51a5416 100644 --- a/node/docker_config.go +++ b/node/docker_config.go @@ -12,10 +12,8 @@ import ( "github.com/ava-labs/avalanche-tooling-sdk-go/constants" "github.com/ava-labs/avalanche-tooling-sdk-go/interchain/relayer" - "github.com/ava-labs/avalanche-tooling-sdk-go/key" remoteconfig "github.com/ava-labs/avalanche-tooling-sdk-go/node/config" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" - "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/awm-relayer/config" ) @@ -135,33 +133,10 @@ func (h *Node) GetAMWRelayerConfig() (*config.Config, error) { } // AddBlockchainToRelayerConfig adds a blockchain to the AWM relayer config -func (h *Node) AddBlockchainToRelayerConfig( - rpcEndpoint string, - wsEndoint string, - subnetID ids.ID, - blockchainID ids.ID, - registryAddress string, - messengerAddress string, - relayerKey *key.SoftKey, -) error { +func (h *Node) SetAMWRelayerConfig(awmConfig *config.Config) error { if !slices.Contains(h.Roles, AWMRelayer) { return errors.New("node is not a AWM Relayer") } - awmConfig, err := h.GetAMWRelayerConfig() - if err != nil { - return err - } - relayer.AddBlockchainToRelayerConfig( - awmConfig, - rpcEndpoint, - wsEndoint, - subnetID, - blockchainID, - registryAddress, - messengerAddress, - relayerKey.C(), - relayerKey.PrivKeyHex(), - ) tmpRelayerConfig, err := os.CreateTemp("", "avalancecli-awm-relayer-config-*.yml") if err != nil { return err diff --git a/node/templates/awmrelayer.docker-compose.yml b/node/templates/awmrelayer.docker-compose.yml index 38dbf95..57bc653 100644 --- a/node/templates/awmrelayer.docker-compose.yml +++ b/node/templates/awmrelayer.docker-compose.yml @@ -1,7 +1,7 @@ name: awm-relayer services: awm-relayer: - image: avaplatform/awm-relayer:{{ .AWMRelayerVersion }} + image: avaplatform/awm-relayer:{{ .Version }} container_name: awm-relayer restart: unless-stopped user: "1000:1000" # ubuntu user From deba8f506ddf06f98681efe1fe0ff82c6a7b0378 Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Tue, 3 Sep 2024 21:18:04 -0700 Subject: [PATCH 8/9] add interchain aws example --- examples/interchain_aws.go | 102 +++++++++++++++++++++++++++++++++++++ node/create.go | 8 ++- node/docker_ssh.go | 1 + 3 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 examples/interchain_aws.go diff --git a/examples/interchain_aws.go b/examples/interchain_aws.go new file mode 100644 index 0000000..033c0e9 --- /dev/null +++ b/examples/interchain_aws.go @@ -0,0 +1,102 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package main + +import ( + "context" + "fmt" + "time" + + "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" + awsAPI "github.com/ava-labs/avalanche-tooling-sdk-go/cloud/aws" + "github.com/ava-labs/avalanche-tooling-sdk-go/node" + "github.com/ava-labs/avalanche-tooling-sdk-go/utils" +) + +func CreateAWMRelayerNode() { + ctx := context.Background() + + // Get the default cloud parameters for AWS + cp, err := node.GetDefaultCloudParams(ctx, node.AWSCloud) + if err != nil { + panic(err) + } + + securityGroupName := "SECURITY_GROUP_NAME" + // Create a new security group in AWS if you do not currently have one in the selected + // AWS region. + sgID, err := awsAPI.CreateSecurityGroup(ctx, securityGroupName, cp.AWSConfig.AWSProfile, cp.Region) + if err != nil { + panic(err) + } + // Set the security group we are using when creating our Avalanche Nodes + cp.AWSConfig.AWSSecurityGroupName = securityGroupName + cp.AWSConfig.AWSSecurityGroupID = sgID + + keyPairName := "KEY_PAIR_NAME" + sshPrivateKeyPath := utils.ExpandHome("PRIVATE_KEY_FILEPATH") + // Create a new AWS SSH key pair if you do not currently have one in your selected AWS region. + // Note that the created key pair can only be used in the region that it was created in. + // The private key to the created key pair will be stored in the filepath provided in + // sshPrivateKeyPath. + if err := awsAPI.CreateSSHKeyPair(ctx, cp.AWSConfig.AWSProfile, cp.Region, keyPairName, sshPrivateKeyPath); err != nil { + panic(err) + } + // Set the key pair we are using when creating our Avalanche Nodes + cp.AWSConfig.AWSKeyPair = keyPairName + + const ( + awmVersion = "v1.4.0" + ) + + // Create two new Avalanche Validator nodes on Fuji Network on AWS without Elastic IPs + // attached. Once CreateNodes is completed, the validators will begin bootstrapping process + // to Primary Network in Fuji Network. Nodes need to finish bootstrapping process + // before they can validate Avalanche Primary Network / Subnet. + // + // SDK function for nodes to start validating Primary Network / Subnet will be available + // in the next Avalanche Tooling SDK release. + hosts, err := node.CreateNodes(ctx, + &node.NodeParams{ + CloudParams: cp, + Count: 1, + Roles: []node.SupportedRole{node.AWMRelayer}, + Network: avalanche.FujiNetwork(), + AWMRelayerVersion: awmVersion, + UseStaticIP: false, + SSHPrivateKeyPath: sshPrivateKeyPath, + }) + if err != nil { + panic(err) + } + + const ( + sshTimeout = 120 * time.Second + sshCommandTimeout = 10 * time.Second + ) + + // Examples showing how to run ssh commands on the created nodes + for _, h := range hosts { + // Wait for the host to be ready (only needs to be done once for newly created nodes) + fmt.Println("Waiting for SSH shell") + if err := h.WaitForSSHShell(sshTimeout); err != nil { + panic(err) + } + // check if awm-relayer is running + if output, err := h.Commandf(nil, sshCommandTimeout, "docker ps"); err != nil { + panic(err) + } else { + fmt.Println(string(output)) + } + } + + for _, h := range hosts { + fmt.Println("Get awm-relayer configuratuin") + awmConfig, err := h.GetAMWRelayerConfig() + if err != nil { + panic(err) + } + fmt.Println(awmConfig) + } +} diff --git a/node/create.go b/node/create.go index 2af98da..fd11d72 100644 --- a/node/create.go +++ b/node/create.go @@ -332,9 +332,13 @@ func provisionMonitoringHost(node Node) error { return nil } -func provisionAWMRelayerHost(node Node, nodeParams *NodeParams) error { // stub +func provisionAWMRelayerHost(node Node, nodeParams *NodeParams) error { + if err := node.RunSSHSetupDockerService(); err != nil { + return err + } if err := node.ComposeSSHSetupAWMRelayer(nodeParams.Network, nodeParams.AWMRelayerVersion); err != nil { return err } - return node.StartDockerComposeService(utils.GetRemoteComposeFile(), constants.ServiceAWMRelayer, constants.SSHLongRunningScriptTimeout) + + return node.StartDockerCompose(constants.SSHScriptTimeout) } diff --git a/node/docker_ssh.go b/node/docker_ssh.go index 3f874cb..d48e313 100644 --- a/node/docker_ssh.go +++ b/node/docker_ssh.go @@ -123,6 +123,7 @@ func (h *Node) ComposeSSHSetupAWMRelayer(network avalanche.Network, awmRelayerVe return fmt.Errorf("error creating folder %s on node %s", folder, h.NodeID) } } + // provide basic configuration for AWM Relayer tmpRelayerConfig, err := os.CreateTemp("", "avalancecli-awm-relayer-config-*.yml") if err != nil { From 5676f469337def604160c4d296a19fcabc74a762 Mon Sep 17 00:00:00 2001 From: Artur Reznikov Date: Thu, 5 Sep 2024 21:38:52 -0700 Subject: [PATCH 9/9] add test send message --- examples/interchain_aws.go | 189 ++++++++++++++++++++++++++++++++----- 1 file changed, 165 insertions(+), 24 deletions(-) diff --git a/examples/interchain_aws.go b/examples/interchain_aws.go index 033c0e9..393dc96 100644 --- a/examples/interchain_aws.go +++ b/examples/interchain_aws.go @@ -1,20 +1,35 @@ // Copyright (C) 2024, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. -package main +package examples import ( "context" "fmt" + "math/big" + "os" "time" "github.com/ava-labs/avalanche-tooling-sdk-go/avalanche" awsAPI "github.com/ava-labs/avalanche-tooling-sdk-go/cloud/aws" + "github.com/ava-labs/avalanche-tooling-sdk-go/interchain/relayer" + "github.com/ava-labs/avalanche-tooling-sdk-go/key" "github.com/ava-labs/avalanche-tooling-sdk-go/node" "github.com/ava-labs/avalanche-tooling-sdk-go/utils" + "github.com/ava-labs/avalanchego/ids" ) -func CreateAWMRelayerNode() { +func InterchainAWSExample( + network avalanche.Network, + chain1RPC string, + chain1PK string, + chain1SubnetID ids.ID, + chain1BlockchainID ids.ID, + chain2RPC string, + chain2PK string, + chain2SubnetID ids.ID, + chain2BlockchainID ids.ID, +) error { ctx := context.Background() // Get the default cloud parameters for AWS @@ -70,33 +85,159 @@ func CreateAWMRelayerNode() { if err != nil { panic(err) } + if len(hosts) != 1 { + panic("expected 1 host") + } + awmRelayerHost := hosts[0] // single awm-relayer host const ( sshTimeout = 120 * time.Second sshCommandTimeout = 10 * time.Second ) - // Examples showing how to run ssh commands on the created nodes - for _, h := range hosts { - // Wait for the host to be ready (only needs to be done once for newly created nodes) - fmt.Println("Waiting for SSH shell") - if err := h.WaitForSSHShell(sshTimeout); err != nil { - panic(err) - } - // check if awm-relayer is running - if output, err := h.Commandf(nil, sshCommandTimeout, "docker ps"); err != nil { - panic(err) - } else { - fmt.Println(string(output)) - } - } - - for _, h := range hosts { - fmt.Println("Get awm-relayer configuratuin") - awmConfig, err := h.GetAMWRelayerConfig() - if err != nil { - panic(err) - } - fmt.Println(awmConfig) + // Wait for the host to be ready (only needs to be done once for newly created nodes) + fmt.Println("Waiting for SSH shell") + if err := awmRelayerHost.WaitForSSHShell(sshTimeout); err != nil { + panic(err) + } + + fmt.Println("Deploying Interchain Messenger to AWS") + chain1RelayerKey, err := key.NewSoft() + if err != nil { + return err + } + chain2RelayerKey, err := key.NewSoft() + if err != nil { + return err + } + chain1RegistryAddress, chain1MessengerAddress, chain2RegistryAddress, chain2MessengerAddress, err := SetupICM( + chain1RPC, + chain1PK, + chain2RPC, + chain2PK, + ) + if err != nil { + return err + } + // Get default awm-relayer configuration + relayerConfig, err := awmRelayerHost.GetAMWRelayerConfig() + if err != nil { + panic(err) + } + // Add blockchain chain1 to the relayer config, + // setting it both as source and as destination. + // So the relayer will both listed for new messages in it, + // and send to it new messages from other blockchains. + relayer.AddBlockchainToRelayerConfig( + relayerConfig, + chain1RPC, + "", + chain1SubnetID, + chain1BlockchainID, + chain1RegistryAddress, + chain1MessengerAddress, + chain1RelayerKey.C(), + chain1RelayerKey.PrivKeyHex(), + ) + // Add blockchain chain2 to the relayer config, + // setting it both as source and as destination. + // So the relayer will both listed for new messages in it, + // and send to it new messages from other blockchains. + relayer.AddBlockchainToRelayerConfig( + relayerConfig, + chain2RPC, + "", + chain2SubnetID, + chain2BlockchainID, + chain2RegistryAddress, + chain2MessengerAddress, + chain2RelayerKey.C(), + chain2RelayerKey.PrivKeyHex(), + ) + // Set awm-relayer configuration for the host + if err := awmRelayerHost.SetAMWRelayerConfig(relayerConfig); err != nil { + panic(err) + } + + // Fund each relayer key with 10 TOKENs + // Where TOKEN is the native gas token of each blockchain + // Assumes that the TOKEN decimals are 18, so, this equals + // to 1e18 of the smallest gas amount in each chain + fmt.Printf("Funding relayer keys %s, %s\n", chain1RelayerKey.C(), chain2RelayerKey.C()) + desiredRelayerBalance := big.NewInt(0).Mul(big.NewInt(1e18), big.NewInt(10)) + + // chain1PK will have a balance 10 native gas tokens on chain. + if err := relayer.FundRelayer( + relayerConfig, + chain1BlockchainID, + chain1PK, + nil, + desiredRelayerBalance, + ); err != nil { + return err + } + // chain2PK will have a balance 10 native gas tokens on chain2 + if err := relayer.FundRelayer( + relayerConfig, + chain2BlockchainID, + chain2PK, + nil, + desiredRelayerBalance, + ); err != nil { + return err + } + + // send a message from chain1 to chain2 + fmt.Println("Verifying message delivery") + if err := TestMessageDelivery( + chain1RPC, + chain1PK, + chain1MessengerAddress, + chain2BlockchainID, + chain2RPC, + chain2MessengerAddress, + []byte("hello world"), + ); err != nil { + return err + } + + fmt.Println("Message successfully delivered") + + return nil +} + +func main() { + chain1RPC := os.Getenv("CHAIN1_RPC") + chain1PK := os.Getenv("CHAIN1_PK") + chain1SubnetID, err := ids.FromString(os.Getenv("CHAIN1_SUBNET_ID")) + if err != nil { + panic(err) + } + chain1BlockchainID, err := ids.FromString(os.Getenv("CHAIN1_BLOCKCHAIN_ID")) + if err != nil { + panic(err) + } + chain2RPC := os.Getenv("CHAIN2_RPC") + chain2PK := os.Getenv("CHAIN2_PK") + chain2SubnetID, err := ids.FromString(os.Getenv("CHAIN2_SUBNET_ID")) + if err != nil { + panic(err) + } + chain2BlockchainID, err := ids.FromString(os.Getenv("CHAIN2_BLOCKCHAIN_ID")) + if err != nil { + panic(err) + } + if err := InterchainAWSExample( + avalanche.FujiNetwork(), + chain1RPC, + chain1PK, + chain1SubnetID, + chain1BlockchainID, + chain2RPC, + chain2PK, + chain2SubnetID, + chain2BlockchainID, + ); err != nil { + panic(err) } }