From 5bb5a673a3600120a41d34dcc9646a6e2d8b3bc5 Mon Sep 17 00:00:00 2001 From: Justin Tieri <37750742+jtieri@users.noreply.github.com> Date: Fri, 4 Feb 2022 16:40:07 -0600 Subject: [PATCH] Merge PR #569: Refactor & improve new path generation in the relayer * use chain registry + formatting fixes * remove fetch commands and add chain and path fetching behind chains and paths cmd trees * actually fetch a chains assetlist.json file from chain registry * fix quick-start guide * small fixes to README.md * more small fixes to README.md * fix tabs in README.md * fix tabs in README.md * undo last changes * minor changes to readme * added ds_store * Merge PR #564: Add command for creating a new blank path * add command to create a new blank path in the config * add retries & cleanup CreateClients * use tagged lens version * add logging of txs back into relayer * add CreateClient & RelayMsg commands. Fix race conditions * make ibc update headers query work in parallel + small fixes * testing with updated lens repo * add better retries to connection creation * add better retries to client creation * add better retries to channel creation * fix lgtm bot warnings * fix broken tests * remove -race flag from tests * fix broken bash script testing environment * remove double import statement Co-authored-by: Dan Kanefsky Co-authored-by: Jack Zampolin --- Makefile | 2 +- cmd/config.go | 2 +- cmd/tx.go | 140 ++++++++++++++++++++- go.mod | 2 +- go.sum | 28 +++++ relayer/chain.go | 76 ++++++++++- relayer/channel.go | 252 ++++++++++++++++++++++++++++--------- relayer/client.go | 51 ++++++-- relayer/connection.go | 135 +++++++++++++------- relayer/log-chain.go | 60 +++++++-- relayer/naive-strategy.go | 135 +++++++++++++++++++- relayer/query.go | 36 ++++-- scripts/one-chain | 6 +- test/relayer_chain_test.go | 2 + 14 files changed, 779 insertions(+), 148 deletions(-) diff --git a/Makefile b/Makefile index 58e2daf2d70..61e1d604ed5 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ test-akash: @go test -mod=readonly -v -run TestAkashToGaiaRelaying ./test/... test-short: - @go test -mod=readonly -v -run TestOsmoToGaiaRelaying ./test/... + @go test -mod=readonly -v -run TestOsmoToGaiaRelaying ./test/... coverage: @echo "viewing test coverage..." diff --git a/cmd/config.go b/cmd/config.go index 127396fe5f5..eb516c17db0 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -618,7 +618,7 @@ func initConfig(cmd *cobra.Command) error { for _, pcfg := range cfgWrapper.ProviderConfigs { prov, err := pcfg.Value.(provider.ProviderConfig).NewProvider(homePath, debug) if err != nil { - return fmt.Errorf("Error while building ChainProviders. Err: %s\n", err.Error()) + return fmt.Errorf("Error while building ChainProviders. Err: %w\n", err) } chain := &relayer.Chain{ChainProvider: prov} diff --git a/cmd/tx.go b/cmd/tx.go index ce01402eb57..89eab0442bf 100644 --- a/cmd/tx.go +++ b/cmd/tx.go @@ -2,11 +2,14 @@ package cmd import ( "fmt" + "strconv" "strings" "time" + "github.com/avast/retry-go" "github.com/cosmos/cosmos-sdk/client/flags" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ibc-go/v2/modules/core/exported" "github.com/cosmos/relayer/relayer" "github.com/spf13/cobra" ) @@ -29,16 +32,19 @@ Most of these commands take a [path] argument. Make sure: linkCmd(), linkThenStartCmd(), relayMsgsCmd(), + relayMsgCmd(), relayAcksCmd(), xfersend(), flags.LineBreak, createClientsCmd(), + createClientCmd(), updateClientsCmd(), upgradeClientsCmd(), //upgradeChainCmd(), createConnectionCmd(), closeChannelCmd(), flags.LineBreak, + //sendCmd(), ) @@ -146,6 +152,95 @@ func createClientsCmd() *cobra.Command { return overrideFlag(clientParameterFlags(cmd)) } +func createClientCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "client [src-chain-id] [dst-chain-id] [path-name]", + Short: "create a client between two configured chains with a configured path", + Long: "Creates a working ibc client for chain configured on each end of the" + + " path by querying headers from each chain and then sending the corresponding create-client messages", + Args: cobra.ExactArgs(3), + Example: strings.TrimSpace(fmt.Sprintf(`$ %s transact clients demo-path`, appName)), + RunE: func(cmd *cobra.Command, args []string) error { + allowUpdateAfterExpiry, err := cmd.Flags().GetBool(flagUpdateAfterExpiry) + if err != nil { + return err + } + + allowUpdateAfterMisbehaviour, err := cmd.Flags().GetBool(flagUpdateAfterMisbehaviour) + if err != nil { + return err + } + + override, err := cmd.Flags().GetBool(flagOverride) + if err != nil { + return err + } + + src := args[0] + dst := args[1] + c, err := config.Chains.Gets(src, dst) + if err != nil { + return err + } + + pathName := args[2] + path, err := config.Paths.Get(pathName) + if err != nil { + return err + } + + c[src].PathEnd = path.End(c[src].ChainID()) + c[dst].PathEnd = path.End(c[dst].ChainID()) + + // ensure that keys exist + if exists := c[src].ChainProvider.KeyExists(c[src].ChainProvider.Key()); !exists { + return fmt.Errorf("key %s not found on chain %s \n", c[src].ChainProvider.Key(), c[src].ChainID()) + } + if exists := c[dst].ChainProvider.KeyExists(c[dst].ChainProvider.Key()); !exists { + return fmt.Errorf("key %s not found on chain %s \n", c[dst].ChainProvider.Key(), c[dst].ChainID()) + } + + // Query the latest heights on src and dst and retry if the query fails + var srch, dsth int64 + if err = retry.Do(func() error { + srch, dsth, err = relayer.QueryLatestHeights(c[src], c[dst]) + if srch == 0 || dsth == 0 || err != nil { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, relayer.RtyAtt, relayer.RtyDel, relayer.RtyErr); err != nil { + return err + } + + // Query the light signed headers for src & dst at the heights srch & dsth, retry if the query fails + var srcUpdateHeader, dstUpdateHeader exported.Header + if err = retry.Do(func() error { + srcUpdateHeader, dstUpdateHeader, err = relayer.GetLightSignedHeadersAtHeights(c[src], c[dst], srch, dsth) + if err != nil { + return fmt.Errorf("failed to query light signed headers. Err: %w", err) + } + return err + }, relayer.RtyAtt, relayer.RtyDel, relayer.RtyErr, retry.OnRetry(func(n uint, err error) { + c[src].LogRetryGetLightSignedHeader(n, err) + srch, dsth, _ = relayer.QueryLatestHeights(c[src], c[dst]) + })); err != nil { + return err + } + + modified, err := relayer.CreateClient(c[src], c[dst], srcUpdateHeader, dstUpdateHeader, allowUpdateAfterExpiry, allowUpdateAfterMisbehaviour, override) + if modified { + if err = overWriteConfig(config); err != nil { + return err + } + } + + return nil + }, + } + + return overrideFlag(clientParameterFlags(cmd)) +} + func updateClientsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "update-clients [path-name]", @@ -407,7 +502,7 @@ $ %s tx connect demo-path`, } // create channel if it isn't already created - modified, err = c[src].CreateOpenChannels(c[dst], 3, to) + modified, err = c[src].CreateOpenChannels(c[dst], retries, to) if modified { if err := overWriteConfig(config); err != nil { return err @@ -452,6 +547,49 @@ $ %s tx link-then-start demo-path --timeout 5s`, appName, appName)), return overrideFlag(clientParameterFlags(strategyFlag(retryFlag(timeoutFlag(cmd))))) } +func relayMsgCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "relay-packet [path-name] [seq-num]", + Aliases: []string{"relay-pkt"}, + Short: "relay a non-relayed packet with a specific sequence number, in both directions", + Args: cobra.ExactArgs(2), + Example: strings.TrimSpace(fmt.Sprintf(` +$ %s transact relay-packet demo-path 1 +$ %s tx relay-pkt demo-path 1`, + appName, appName, + )), + RunE: func(cmd *cobra.Command, args []string) error { + c, src, dst, err := config.ChainsFromPath(args[0]) + if err != nil { + return err + } + + if err = ensureKeysExist(c); err != nil { + return err + } + + maxTxSize, maxMsgLength, err := GetStartOptions(cmd) + if err != nil { + return err + } + + seqNum, err := strconv.Atoi(args[1]) + if err != nil { + return err + } + + sp, err := relayer.UnrelayedSequences(c[src], c[dst]) + if err != nil { + return err + } + + return relayer.RelayPacket(c[src], c[dst], sp, maxTxSize, maxMsgLength, uint64(seqNum)) + }, + } + + return strategyFlag(cmd) +} + func relayMsgsCmd() *cobra.Command { cmd := &cobra.Command{ Use: "relay-packets [path-name]", diff --git a/go.mod b/go.mod index 460a17678b8..5b3645dd59d 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( require ( github.com/pkg/errors v0.9.1 - github.com/strangelove-ventures/lens v0.2.1 + github.com/strangelove-ventures/lens v0.2.2-0.20220131192754-f2a69f2e3fd7 ) require ( diff --git a/go.sum b/go.sum index 099a850c0f7..d29c7386250 100644 --- a/go.sum +++ b/go.sum @@ -1119,8 +1119,36 @@ github.com/strangelove-ventures/lens v0.2.1-0.20220122022854-f56147e20e5d h1:p54 github.com/strangelove-ventures/lens v0.2.1-0.20220122022854-f56147e20e5d/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= github.com/strangelove-ventures/lens v0.2.1-0.20220122023000-6e6ff07a5193 h1:LAL3EP8RfxaEf+iZqHTpq5xmlKJkcZbDQVGutAJQB58= github.com/strangelove-ventures/lens v0.2.1-0.20220122023000-6e6ff07a5193/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.1-0.20220124194236-ba2d744c75fa h1:FN4EJIaG0vRqm8VgyzDxDowBC1hcQoVLl7lj0/cGUdM= +github.com/strangelove-ventures/lens v0.2.1-0.20220124194236-ba2d744c75fa/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.1-0.20220124200714-c0e835fd6bc4 h1:aw972fGIxQ13w0OxCcIb6bHQqHsxJltRn6tw5X9PbWA= +github.com/strangelove-ventures/lens v0.2.1-0.20220124200714-c0e835fd6bc4/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.1-0.20220124202932-3b0f30df4040 h1:LCpKzDyqazxdkj03psIsbJZMwehQe6jq3TCOJt3tPFQ= +github.com/strangelove-ventures/lens v0.2.1-0.20220124202932-3b0f30df4040/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.1-0.20220124213244-c8de769cb83d h1:t6pwRMNdjRNkLGAQRE0Mfcmq6iybf5fDGVQBBYrQRY8= +github.com/strangelove-ventures/lens v0.2.1-0.20220124213244-c8de769cb83d/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= github.com/strangelove-ventures/lens v0.2.1 h1:naP3VyQKh9b3sYHZMecBmv4sjSRC1PQzPmKyBpSF5/k= github.com/strangelove-ventures/lens v0.2.1/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220125165849-21346dbd1f6b h1:xzOR0OlfOxwUaRivKUMm7RWPpAONXGP3POL2kw97vVE= +github.com/strangelove-ventures/lens v0.2.2-0.20220125165849-21346dbd1f6b/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220125221701-305c589edb5d h1:fpi5gBJD7X1yS2yfNng4SxLqQMkv9InoObtdGrgWMSw= +github.com/strangelove-ventures/lens v0.2.2-0.20220125221701-305c589edb5d/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220125222839-86536e89a85e h1:8qvfIhGxJNdSnOZz+7L7Zp9D00z6Td8mybYNoTXpt2o= +github.com/strangelove-ventures/lens v0.2.2-0.20220125222839-86536e89a85e/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220125223041-5729e6a4a1e1 h1:KVNioTrEOQl9NvoqCyiRL0tK3i0OOKJj1MoATB+3HxE= +github.com/strangelove-ventures/lens v0.2.2-0.20220125223041-5729e6a4a1e1/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220127175506-c30f1b75eb70 h1:SR02xBZ6VOn3VyMXsfdaBK1OsYrE9NBGBiDKYP1TGOc= +github.com/strangelove-ventures/lens v0.2.2-0.20220127175506-c30f1b75eb70/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220127183102-acd44266fed9 h1:3YE+qiQQ/+efRM/aRrflXZBGVz5DPwsT2QP7vhygEu4= +github.com/strangelove-ventures/lens v0.2.2-0.20220127183102-acd44266fed9/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220127184319-56d6f5e912c6 h1:TmT0iWbayW1+inQil6oXSqGghi8ZNFx0nHToUt0fXMg= +github.com/strangelove-ventures/lens v0.2.2-0.20220127184319-56d6f5e912c6/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220128163830-0f63f3a94653 h1:NicvNuR8QkLvuXHywj9ZLrU8eHMhebUU0+HFEusba+Q= +github.com/strangelove-ventures/lens v0.2.2-0.20220128163830-0f63f3a94653/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220131191229-2c7ba13ad8c8 h1:M5mLJ07qLKvD70SoeSkXSw2IIxEDO2/9Zh6dHKrK/l0= +github.com/strangelove-ventures/lens v0.2.2-0.20220131191229-2c7ba13ad8c8/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= +github.com/strangelove-ventures/lens v0.2.2-0.20220131192754-f2a69f2e3fd7 h1:3vwNTIh39q4Vl8tkmV02lAS37SZ6WrkKX/GlsjdVqqg= +github.com/strangelove-ventures/lens v0.2.2-0.20220131192754-f2a69f2e3fd7/go.mod h1:QSKpnL9bDirjJsMApJoHmVU3ZBujFnbXsvY9d5JG8M8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= diff --git a/relayer/chain.go b/relayer/chain.go index acf6e1f0994..c6b1a22e3fc 100644 --- a/relayer/chain.go +++ b/relayer/chain.go @@ -3,13 +3,37 @@ package relayer import ( "encoding/json" "fmt" - "github.com/avast/retry-go" "net/url" "os" "strings" "time" - "github.com/cosmos/cosmos-sdk/simapp/params" + "github.com/avast/retry-go" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/std" + "github.com/cosmos/cosmos-sdk/types/module" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth/tx" + authz "github.com/cosmos/cosmos-sdk/x/authz/module" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/cosmos/cosmos-sdk/x/capability" + "github.com/cosmos/cosmos-sdk/x/crisis" + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/mint" + "github.com/cosmos/cosmos-sdk/x/slashing" + "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/upgrade" + "github.com/cosmos/ibc-go/v2/modules/apps/transfer" + ibc "github.com/cosmos/ibc-go/v2/modules/core" + + cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + simparams "github.com/cosmos/cosmos-sdk/simapp/params" + distrclient "github.com/cosmos/cosmos-sdk/x/distribution/client" + feegrant "github.com/cosmos/cosmos-sdk/x/feegrant/module" + "github.com/cosmos/cosmos-sdk/x/params" + paramsclient "github.com/cosmos/cosmos-sdk/x/params/client" + upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" clienttypes "github.com/cosmos/ibc-go/v2/modules/core/02-client/types" "github.com/cosmos/relayer/relayer/provider" "github.com/gogo/protobuf/proto" @@ -21,6 +45,26 @@ var ( RtyAtt = retry.Attempts(RtyAttNum) RtyDel = retry.Delay(time.Millisecond * 400) RtyErr = retry.LastErrorOnly(true) + + ModuleBasics = []module.AppModuleBasic{ + auth.AppModuleBasic{}, + authz.AppModuleBasic{}, + bank.AppModuleBasic{}, + capability.AppModuleBasic{}, + gov.NewAppModuleBasic( + paramsclient.ProposalHandler, distrclient.ProposalHandler, upgradeclient.ProposalHandler, upgradeclient.CancelProposalHandler, + ), + crisis.AppModuleBasic{}, + distribution.AppModuleBasic{}, + feegrant.AppModuleBasic{}, + mint.AppModuleBasic{}, + params.AppModuleBasic{}, + slashing.AppModuleBasic{}, + staking.AppModuleBasic{}, + upgrade.AppModuleBasic{}, + transfer.AppModuleBasic{}, + ibc.AppModuleBasic{}, + } ) // Chain represents the necessary data for connecting to and identifying a chain and its counterparties @@ -30,13 +74,34 @@ type Chain struct { Chainid string `yaml:"chain-id" json:"chain-id"` RPCAddr string `yaml:"rpc-addr" json:"rpc-addr"` - PathEnd *PathEnd `yaml:"-" json:"-"` - Encoding params.EncodingConfig `yaml:"-" json:"-"` + PathEnd *PathEnd `yaml:"-" json:"-"` + Encoding simparams.EncodingConfig `yaml:"-" json:"-"` logger log.Logger debug bool } +func MakeCodec(moduleBasics []module.AppModuleBasic) simparams.EncodingConfig { + modBasic := module.NewBasicManager(moduleBasics...) + encodingConfig := MakeCodecConfig() + std.RegisterLegacyAminoCodec(encodingConfig.Amino) + std.RegisterInterfaces(encodingConfig.InterfaceRegistry) + modBasic.RegisterLegacyAminoCodec(encodingConfig.Amino) + modBasic.RegisterInterfaces(encodingConfig.InterfaceRegistry) + return encodingConfig +} + +func MakeCodecConfig() simparams.EncodingConfig { + interfaceRegistry := cdctypes.NewInterfaceRegistry() + marshaler := codec.NewProtoCodec(interfaceRegistry) + return simparams.EncodingConfig{ + InterfaceRegistry: interfaceRegistry, + Marshaler: marshaler, + TxConfig: tx.NewTxConfig(marshaler, tx.DefaultSignModes), + Amino: codec.NewLegacyAmino(), + } +} + // ValidatePaths takes two chains and validates their paths func ValidatePaths(src, dst *Chain) error { if err := src.PathEnd.ValidateFull(); err != nil { @@ -101,6 +166,9 @@ func (c *Chain) Init(logger log.Logger, debug bool) { if c.logger == nil { c.logger = defaultChainLogger() } + + // TODO logging/encoding needs refactored + c.Encoding = MakeCodec(ModuleBasics) } func defaultChainLogger() log.Logger { diff --git a/relayer/channel.go b/relayer/channel.go index b7e7e030645..8aebbd34d07 100644 --- a/relayer/channel.go +++ b/relayer/channel.go @@ -5,6 +5,10 @@ import ( "strings" "time" + "github.com/avast/retry-go" + "github.com/cosmos/ibc-go/v2/modules/core/exported" + "github.com/cosmos/relayer/relayer/provider" + chantypes "github.com/cosmos/ibc-go/v2/modules/core/04-channel/types" ) @@ -80,15 +84,27 @@ func (c *Chain) CreateOpenChannels(dst *Chain, maxRetries uint64, to time.Durati // file. The booleans return indicate if the message was successfully // executed and if this was the last handshake step. func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err error) { - srch, dsth, err := QueryLatestHeights(src, dst) - if err != nil { - return false, false, false, err + var ( + srch, dsth int64 + srcHeader, dstHeader exported.Header + msgs []provider.RelayerMessage + res *provider.RelayerTxResponse + ) + + if err = retry.Do(func() error { + srch, dsth, err = QueryLatestHeights(src, dst) + if err != nil || srch == 0 || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } // if either identifier is missing, an existing channel that matches the required fields // is chosen or a new channel is created. if src.PathEnd.ChannelID == "" || dst.PathEnd.ChannelID == "" { - success, modified, err := InitializeChannel(src, dst) + success, modified, err = InitializeChannel(src, dst) if err != nil { return false, false, false, err } @@ -112,22 +128,35 @@ func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err erro logChannelStates(src, dst, srcChan, dstChan) } - dsth, err := dst.ChainProvider.QueryLatestHeight() - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + dsth, err = dst.ChainProvider.QueryLatestHeight() + if err != nil || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + dstHeader, err = dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) + if err != nil || dsth == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + dst.LogRetryGetIBCUpdateHeader(n, err) + dsth, _ = dst.ChainProvider.QueryLatestHeight() + })); err != nil { + return success, last, modified, err } - msgs, err := src.ChainProvider.ChannelOpenTry(dst.ChainProvider, dstHeader, src.PortID(), dst.PortID(), src.ChannelID(), dst.ChannelID(), src.Version(), src.ConnectionID(), src.ClientID()) + msgs, err = src.ChainProvider.ChannelOpenTry(dst.ChainProvider, dstHeader, src.PortID(), dst.PortID(), src.ChannelID(), dst.ChannelID(), src.Version(), src.ConnectionID(), src.ClientID()) if err != nil { return false, false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -144,22 +173,35 @@ func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err erro logChannelStates(src, dst, srcChan, dstChan) } - dsth, err := dst.ChainProvider.QueryLatestHeight() - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + dsth, err = dst.ChainProvider.QueryLatestHeight() + if err != nil || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + dstHeader, err = dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) + if err != nil || dsth == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + dst.LogRetryGetIBCUpdateHeader(n, err) + dsth, _ = dst.ChainProvider.QueryLatestHeight() + })); err != nil { + return success, last, modified, err } - msgs, err := src.ChainProvider.ChannelOpenAck(dst.ChainProvider, dstHeader, src.ClientID(), src.PortID(), src.ChannelID(), dst.ChannelID(), dst.PortID()) + msgs, err = src.ChainProvider.ChannelOpenAck(dst.ChainProvider, dstHeader, src.ClientID(), src.PortID(), src.ChannelID(), dst.ChannelID(), dst.PortID()) if err != nil { return false, false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -175,22 +217,35 @@ func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err erro logChannelStates(dst, src, dstChan, srcChan) } - srch, err := src.ChainProvider.QueryLatestHeight() - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + srch, err = src.ChainProvider.QueryLatestHeight() + if err != nil || srch == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } - srcHeader, err := src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + srcHeader, err = src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) + if err != nil || srch == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + src.LogRetryGetIBCUpdateHeader(n, err) + srch, _ = src.ChainProvider.QueryLatestHeight() + })); err != nil { + return success, last, modified, err } - msgs, err := dst.ChainProvider.ChannelOpenAck(src.ChainProvider, srcHeader, dst.ClientID(), dst.PortID(), dst.ChannelID(), src.ChannelID(), src.PortID()) + msgs, err = dst.ChainProvider.ChannelOpenAck(src.ChainProvider, srcHeader, dst.ClientID(), dst.PortID(), dst.ChannelID(), src.ChannelID(), src.PortID()) if err != nil { return false, false, false, err } - res, success, err := dst.ChainProvider.SendMessages(msgs) + res, success, err = dst.ChainProvider.SendMessages(msgs) if err != nil { dst.LogFailedTx(res, err, msgs) } @@ -204,24 +259,37 @@ func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err erro logChannelStates(src, dst, srcChan, dstChan) } - dsth, err := dst.ChainProvider.QueryLatestHeight() - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + dsth, err = dst.ChainProvider.QueryLatestHeight() + if err != nil || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + dstHeader, err = dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) + if err != nil || dsth == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + dst.LogRetryGetIBCUpdateHeader(n, err) + dsth, _ = dst.ChainProvider.QueryLatestHeight() + })); err != nil { + return success, last, modified, err } - msgs, err := src.ChainProvider.ChannelOpenConfirm(dst.ChainProvider, dstHeader, src.ClientID(), src.PortID(), src.ChannelID(), dst.PortID(), dst.ChannelID()) + msgs, err = src.ChainProvider.ChannelOpenConfirm(dst.ChainProvider, dstHeader, src.ClientID(), src.PortID(), src.ChannelID(), dst.PortID(), dst.ChannelID()) if err != nil { return false, false, false, err } last = true - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -235,22 +303,35 @@ func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err erro logChannelStates(dst, src, dstChan, srcChan) } - srch, err := src.ChainProvider.QueryLatestHeight() - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + srch, err = src.ChainProvider.QueryLatestHeight() + if err != nil || srch == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } - srcHeader, err := src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) - if err != nil { - return false, false, false, err + if err = retry.Do(func() error { + srcHeader, err = src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) + if err != nil || srch == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + src.LogRetryGetIBCUpdateHeader(n, err) + srch, _ = src.ChainProvider.QueryLatestHeight() + })); err != nil { + return success, last, modified, err } - msgs, err := dst.ChainProvider.ChannelOpenConfirm(src.ChainProvider, srcHeader, dst.ClientID(), dst.PortID(), dst.ChannelID(), src.PortID(), src.ChannelID()) + msgs, err = dst.ChainProvider.ChannelOpenConfirm(src.ChainProvider, srcHeader, dst.ClientID(), dst.PortID(), dst.ChannelID(), src.PortID(), src.ChannelID()) if err != nil { return false, false, false, err } - res, success, err := dst.ChainProvider.SendMessages(msgs) + res, success, err = dst.ChainProvider.SendMessages(msgs) if err != nil { dst.LogFailedTx(res, err, msgs) } @@ -273,6 +354,13 @@ func ExecuteChannelStep(src, dst *Chain) (success, last, modified bool, err erro // initialized. The PathEnds are updated upon a successful transaction. // NOTE: This function may need to be called twice if neither channel exists. func InitializeChannel(src, dst *Chain) (success, modified bool, err error) { + var ( + srch, dsth int64 + srcHeader, dstHeader exported.Header + msgs []provider.RelayerMessage + res *provider.RelayerTxResponse + ) + switch { // OpenInit on source @@ -285,22 +373,35 @@ func InitializeChannel(src, dst *Chain) (success, modified bool, err error) { channelID, found := FindMatchingChannel(src, dst) if !found { - dsth, err := dst.ChainProvider.QueryLatestHeight() - if err != nil { + if err = retry.Do(func() error { + dsth, err = dst.ChainProvider.QueryLatestHeight() + if err != nil || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { return false, false, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { + if err = retry.Do(func() error { + dstHeader, err = dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) + if err != nil || dsth == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + dst.LogRetryGetIBCUpdateHeader(n, err) + dsth, _ = dst.ChainProvider.QueryLatestHeight() + })); err != nil { return false, false, err } - msgs, err := src.ChainProvider.ChannelOpenInit(src.ClientID(), src.ConnectionID(), src.PortID(), src.Version(), dst.PortID(), OrderFromString(strings.ToUpper(src.Order())), dstHeader) + msgs, err = src.ChainProvider.ChannelOpenInit(src.ClientID(), src.ConnectionID(), src.PortID(), src.Version(), dst.PortID(), OrderFromString(strings.ToUpper(src.Order())), dstHeader) if err != nil { return false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -331,23 +432,37 @@ func InitializeChannel(src, dst *Chain) (success, modified bool, err error) { channelID, found := FindMatchingChannel(src, dst) if !found { - dsth, err := dst.ChainProvider.QueryLatestHeight() - if err != nil { + + if err = retry.Do(func() error { + dsth, err = dst.ChainProvider.QueryLatestHeight() + if err != nil || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { return false, false, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { + if err = retry.Do(func() error { + dstHeader, err = dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) + if err != nil || dsth == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + dst.LogRetryGetIBCUpdateHeader(n, err) + dsth, _ = dst.ChainProvider.QueryLatestHeight() + })); err != nil { return false, false, err } // open try on source chain - msgs, err := src.ChainProvider.ChannelOpenTry(dst.ChainProvider, dstHeader, src.PortID(), dst.PortID(), src.ChannelID(), dst.ChannelID(), src.Version(), src.ConnectionID(), src.ClientID()) + msgs, err = src.ChainProvider.ChannelOpenTry(dst.ChainProvider, dstHeader, src.PortID(), dst.PortID(), src.ChannelID(), dst.ChannelID(), src.Version(), src.ConnectionID(), src.ClientID()) if err != nil { return false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -379,23 +494,36 @@ func InitializeChannel(src, dst *Chain) (success, modified bool, err error) { channelID, found := FindMatchingChannel(dst, src) if !found { - srch, err := src.ChainProvider.QueryLatestHeight() - if err != nil { + if err = retry.Do(func() error { + srch, err = src.ChainProvider.QueryLatestHeight() + if err != nil || srch == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { return false, false, err } - srcHeader, err := src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) - if err != nil { + if err = retry.Do(func() error { + srcHeader, err = src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) + if err != nil || srch == 0 { + return fmt.Errorf("failed to get IBC update header. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + src.LogRetryGetIBCUpdateHeader(n, err) + srch, _ = src.ChainProvider.QueryLatestHeight() + })); err != nil { return false, false, err } // open try on destination chain - msgs, err := dst.ChainProvider.ChannelOpenTry(src.ChainProvider, srcHeader, dst.PortID(), src.PortID(), dst.ChannelID(), src.ChannelID(), dst.Version(), dst.ConnectionID(), dst.ClientID()) + msgs, err = dst.ChainProvider.ChannelOpenTry(src.ChainProvider, srcHeader, dst.PortID(), src.PortID(), dst.ChannelID(), src.ChannelID(), dst.Version(), dst.ConnectionID(), dst.ClientID()) if err != nil { return false, false, err } - res, success, err := dst.ChainProvider.SendMessages(msgs) + res, success, err = dst.ChainProvider.SendMessages(msgs) if err != nil { dst.LogFailedTx(res, err, msgs) } diff --git a/relayer/client.go b/relayer/client.go index f3102a66b9b..81191402436 100644 --- a/relayer/client.go +++ b/relayer/client.go @@ -2,9 +2,10 @@ package relayer import ( "fmt" - clienttypes "github.com/cosmos/ibc-go/v2/modules/core/02-client/types" "time" + clienttypes "github.com/cosmos/ibc-go/v2/modules/core/02-client/types" + "github.com/avast/retry-go" ibcexported "github.com/cosmos/ibc-go/v2/modules/core/exported" "github.com/cosmos/relayer/relayer/provider" @@ -22,7 +23,7 @@ func (c *Chain) CreateClients(dst *Chain, allowUpdateAfterExpiry, allowUpdateAft // Query the latest heights on src and dst and retry if the query fails if err = retry.Do(func() error { srch, dsth, err = QueryLatestHeights(c, dst) - if srch == 0 || dsth == 0 { + if srch == 0 || dsth == 0 || err != nil { return fmt.Errorf("failed to query latest heights. Err: %w", err) } return err @@ -37,7 +38,10 @@ func (c *Chain) CreateClients(dst *Chain, allowUpdateAfterExpiry, allowUpdateAft return fmt.Errorf("failed to query light signed headers. Err: %w", err) } return err - }, RtyAtt, RtyDel, RtyErr); err != nil { + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + c.LogRetryGetLightSignedHeader(n, err) + srch, dsth, _ = QueryLatestHeights(c, dst) + })); err != nil { return false, err } @@ -110,6 +114,10 @@ func CreateClient(src, dst *Chain, srcUpdateHeader, dstUpdateHeader ibcexported. } if !found || override { + if src.debug { + src.Log(fmt.Sprintf("No client found on src chain {%s} tracking the state of counterparty chain {%s}", src.ChainID(), dst.ChainID())) + } + createMsg, err := src.ChainProvider.CreateClient(clientState, dstUpdateHeader) if err != nil { return modified, fmt.Errorf("failed to compose CreateClient msg for chain{%s}. Err: %w \n", src.ChainID(), err) @@ -155,7 +163,7 @@ func CreateClient(src, dst *Chain, srcUpdateHeader, dstUpdateHeader ibcexported. } } - return modified, err + return modified, nil } // UpdateClients updates clients for src on dst and dst on src given the configured paths @@ -163,24 +171,47 @@ func (c *Chain) UpdateClients(dst *Chain) (err error) { var ( clients = &RelayMsgs{Src: []provider.RelayerMessage{}, Dst: []provider.RelayerMessage{}} srcUpdateHeader, dstUpdateHeader ibcexported.Header + srch, dsth int64 ) - srch, dsth, err := QueryLatestHeights(c, dst) - if err != nil { + if err = retry.Do(func() error { + srch, dsth, err = QueryLatestHeights(c, dst) + if srch == 0 || dsth == 0 || err != nil { + c.Log(fmt.Sprintf("failed to query latest heights. Err: %v", err)) + return err + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { return err } - srcUpdateHeader, dstUpdateHeader, err = GetLightSignedHeadersAtHeights(c, dst, srch, dsth) - if err != nil { + if err = retry.Do(func() error { + srcUpdateHeader, dstUpdateHeader, err = GetIBCUpdateHeaders(srch, dsth, c.ChainProvider, dst.ChainProvider, c.ClientID(), dst.ClientID()) + if err != nil { + c.Log(fmt.Sprintf("failed to query light signed headers. Err: %v", err)) + return err + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + c.Log(fmt.Sprintf("failed to get IBC update headers, try(%d/%d). Err: %v", n+1, RtyAttNum, err)) + srch, dsth, _ = QueryLatestHeights(c, dst) + })); err != nil { return err } srcUpdateMsg, err := c.ChainProvider.UpdateClient(c.ClientID(), dstUpdateHeader) if err != nil { + if c.debug { + c.Log(fmt.Sprintf("failed to update client on chain{%s} \n", c.ChainID())) + } return err } + dstUpdateMsg, err := dst.ChainProvider.UpdateClient(dst.ClientID(), srcUpdateHeader) if err != nil { + if dst.debug { + dst.Log(fmt.Sprintf("failed to update client on chain{%s} \n", dst.ChainID())) + } return err } @@ -198,9 +229,7 @@ func (c *Chain) UpdateClients(dst *Chain) (err error) { dst.ChainID(), dst.PathEnd.ClientID, MustGetHeight(dstUpdateHeader.GetHeight()), - dstUpdateHeader.GetHeight().GetRevisionHeight(), - ), - ) + dstUpdateHeader.GetHeight().GetRevisionHeight())) } } diff --git a/relayer/connection.go b/relayer/connection.go index 515ddda3d92..ae5642a0da1 100644 --- a/relayer/connection.go +++ b/relayer/connection.go @@ -4,6 +4,10 @@ import ( "fmt" "time" + "github.com/avast/retry-go" + "github.com/cosmos/ibc-go/v2/modules/core/exported" + "github.com/cosmos/relayer/relayer/provider" + conntypes "github.com/cosmos/ibc-go/v2/modules/core/03-connection/types" ) @@ -11,7 +15,7 @@ import ( // The returned boolean indicates that the path end has been modified. func (c *Chain) CreateOpenConnections(dst *Chain, maxRetries uint64, to time.Duration) (modified bool, err error) { // client identifiers must be filled in - if err := ValidateClientPaths(c, dst); err != nil { + if err = ValidateClientPaths(c, dst); err != nil { return modified, err } @@ -75,33 +79,45 @@ func (c *Chain) CreateOpenConnections(dst *Chain, maxRetries uint64, to time.Dur // file. The booleans return indicate if the message was successfully // executed and if this was the last handshake step. func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err error) { - srch, dsth, err := QueryLatestHeights(src, dst) - if err != nil { - return false, false, false, err - } - - srcHeader, err := src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) - if err != nil { - return false, false, false, err + var ( + msgs []provider.RelayerMessage + res *provider.RelayerTxResponse + srcHeader, dstHeader exported.Header + srch, dsth int64 + ) + + // Query the latest heights on src and dst and retry if the query fails + if err = retry.Do(func() error { + srch, dsth, err = QueryLatestHeights(src, dst) + if err != nil || srch == 0 || dsth == 0 { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { + return success, last, modified, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { - return false, false, false, err - } - - // TODO: add back retries due to commit delay/update // get headers to update light clients on chain // if either identifier is missing, an existing connection that matches the required fields // is chosen or a new connection is created. // This will perform either an OpenInit or OpenTry step and return + if err = retry.Do(func() error { + srcHeader, dstHeader, err = GetIBCUpdateHeaders(srch, dsth, src.ChainProvider, dst.ChainProvider, src.ClientID(), dst.ClientID()) + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + src.Log(fmt.Sprintf("failed to get IBC update headers, try(%d/%d). Err: %v", n+1, RtyAttNum, err)) + srch, dsth, _ = QueryLatestHeights(src, dst) + })); err != nil { + return success, last, modified, err + } + if src.ConnectionID() == "" || dst.ConnectionID() == "" { - success, modified, err := InitializeConnection(src, dst) + success, modified, err = InitializeConnection(src, dst) if err != nil { return false, false, false, err } - return success, false, modified, nil + return success, last, modified, nil } // Query Connection data from src and dst @@ -120,12 +136,12 @@ func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err e logConnectionStates(src, dst, srcConn, dstConn) } - msgs, err := src.ChainProvider.ConnectionOpenTry(dst.ChainProvider, dstHeader, src.ClientID(), dst.ClientID(), src.ConnectionID(), dst.ConnectionID()) + msgs, err = src.ChainProvider.ConnectionOpenTry(dst.ChainProvider, dstHeader, src.ClientID(), dst.ClientID(), src.ConnectionID(), dst.ConnectionID()) if err != nil { return false, false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -142,12 +158,12 @@ func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err e logConnectionStates(src, dst, srcConn, dstConn) } - msgs, err := src.ChainProvider.ConnectionOpenAck(dst.ChainProvider, dstHeader, src.ClientID(), src.ConnectionID(), dst.ClientID(), dst.ConnectionID()) + msgs, err = src.ChainProvider.ConnectionOpenAck(dst.ChainProvider, dstHeader, src.ClientID(), src.ConnectionID(), dst.ClientID(), dst.ConnectionID()) if err != nil { return false, false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -163,12 +179,12 @@ func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err e logConnectionStates(dst, src, dstConn, srcConn) } - msgs, err := dst.ChainProvider.ConnectionOpenAck(src.ChainProvider, srcHeader, dst.ClientID(), dst.ConnectionID(), src.ClientID(), src.ConnectionID()) + msgs, err = dst.ChainProvider.ConnectionOpenAck(src.ChainProvider, srcHeader, dst.ClientID(), dst.ConnectionID(), src.ClientID(), src.ConnectionID()) if err != nil { return false, false, false, err } - res, success, err := dst.ChainProvider.SendMessages(msgs) + res, success, err = dst.ChainProvider.SendMessages(msgs) if err != nil { dst.LogFailedTx(res, err, msgs) } @@ -182,12 +198,12 @@ func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err e logConnectionStates(src, dst, srcConn, dstConn) } - msgs, err := src.ChainProvider.ConnectionOpenConfirm(dst.ChainProvider, dstHeader, dst.ConnectionID(), src.ClientID(), src.ConnectionID()) + msgs, err = src.ChainProvider.ConnectionOpenConfirm(dst.ChainProvider, dstHeader, dst.ConnectionID(), src.ClientID(), src.ConnectionID()) if err != nil { return false, false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -203,12 +219,12 @@ func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err e logConnectionStates(dst, src, dstConn, srcConn) } - msgs, err := dst.ChainProvider.ConnectionOpenConfirm(src.ChainProvider, srcHeader, src.ConnectionID(), dst.ClientID(), dst.ConnectionID()) + msgs, err = dst.ChainProvider.ConnectionOpenConfirm(src.ChainProvider, srcHeader, src.ConnectionID(), dst.ClientID(), dst.ConnectionID()) if err != nil { return false, false, false, err } - res, success, err := dst.ChainProvider.SendMessages(msgs) + res, success, err = dst.ChainProvider.SendMessages(msgs) if err != nil { dst.LogFailedTx(res, err, msgs) } @@ -231,18 +247,32 @@ func ExecuteConnectionStep(src, dst *Chain) (success, last, modified bool, err e // initialized. The PathEnds are updated upon a successful transaction. // NOTE: This function may need to be called twice if neither connection exists. func InitializeConnection(src, dst *Chain) (success, modified bool, err error) { - srch, dsth, err := QueryLatestHeights(src, dst) - if err != nil { - return false, false, err - } - - srcHeader, err := src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.ClientID()) - if err != nil { + var ( + srcHeader, dstHeader exported.Header + srch, dsth int64 + msgs []provider.RelayerMessage + res *provider.RelayerTxResponse + ) + + // Query the latest heights on src and dst and retry if the query fails + if err = retry.Do(func() error { + srch, dsth, err = QueryLatestHeights(src, dst) + if srch == 0 || dsth == 0 || err != nil { + return fmt.Errorf("failed to query latest heights. Err: %w", err) + } + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { return false, false, err } - dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.ClientID()) - if err != nil { + // Get IBC Update Headers for src and dst which can be used to update an on chain light client on the counterparty + if err = retry.Do(func() error { + srcHeader, dstHeader, err = GetIBCUpdateHeaders(srch, dsth, src.ChainProvider, dst.ChainProvider, src.ClientID(), dst.ClientID()) + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + src.Log(fmt.Sprintf("failed to get IBC update headers, try(%d/%d). Err: %v", n+1, RtyAttNum, err)) + srch, dsth, _ = QueryLatestHeights(src, dst) + })); err != nil { return false, false, err } @@ -258,12 +288,12 @@ func InitializeConnection(src, dst *Chain) (success, modified bool, err error) { connectionID, found := FindMatchingConnection(src, dst) if !found { // construct OpenInit message to be submitted on source chain - msgs, err := src.ChainProvider.ConnectionOpenInit(src.ClientID(), dst.ClientID(), dstHeader) + msgs, err = src.ChainProvider.ConnectionOpenInit(src.ClientID(), dst.ClientID(), dstHeader) if err != nil { return false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -294,12 +324,12 @@ func InitializeConnection(src, dst *Chain) (success, modified bool, err error) { connectionID, found := FindMatchingConnection(src, dst) if !found { - msgs, err := src.ChainProvider.ConnectionOpenTry(dst.ChainProvider, dstHeader, src.ClientID(), dst.ClientID(), src.ConnectionID(), dst.ConnectionID()) + msgs, err = src.ChainProvider.ConnectionOpenTry(dst.ChainProvider, dstHeader, src.ClientID(), dst.ClientID(), src.ConnectionID(), dst.ConnectionID()) if err != nil { return false, false, err } - res, success, err := src.ChainProvider.SendMessages(msgs) + res, success, err = src.ChainProvider.SendMessages(msgs) if err != nil { src.LogFailedTx(res, err, msgs) } @@ -330,12 +360,12 @@ func InitializeConnection(src, dst *Chain) (success, modified bool, err error) { connectionID, found := FindMatchingConnection(dst, src) if !found { - msgs, err := dst.ChainProvider.ConnectionOpenTry(src.ChainProvider, srcHeader, dst.ClientID(), src.ClientID(), dst.ConnectionID(), src.ConnectionID()) + msgs, err = dst.ChainProvider.ConnectionOpenTry(src.ChainProvider, srcHeader, dst.ClientID(), src.ClientID(), dst.ConnectionID(), src.ConnectionID()) if err != nil { return false, false, err } - res, success, err := dst.ChainProvider.SendMessages(msgs) + res, success, err = dst.ChainProvider.SendMessages(msgs) if err != nil { dst.LogFailedTx(res, err, msgs) } @@ -365,12 +395,27 @@ func InitializeConnection(src, dst *Chain) (success, modified bool, err error) { // FindMatchingConnection will determine if there already exists a connection between source and counterparty // that matches the parameters set in the relayer config. func FindMatchingConnection(source, counterparty *Chain) (string, bool) { - // TODO: add appropriate offset and limits, along with retries - connectionsResp, err := source.ChainProvider.QueryConnections() - if err != nil { + // TODO: add appropriate offset and limits + var ( + err error + connectionsResp []*conntypes.IdentifiedConnection + ) + + if err = retry.Do(func() error { + connectionsResp, err = source.ChainProvider.QueryConnections() + if err != nil { + if source.debug { + source.Log(fmt.Sprintf("Error: querying connections on %s failed: %v", source.ChainID(), err)) + } + return err + } + + return err + }, RtyAtt, RtyDel, RtyErr); err != nil { if source.debug { source.Log(fmt.Sprintf("Error: querying connections on %s failed: %v", source.ChainID(), err)) } + return "", false } diff --git a/relayer/log-chain.go b/relayer/log-chain.go index 7aaf0c38702..9329fe07e29 100644 --- a/relayer/log-chain.go +++ b/relayer/log-chain.go @@ -1,8 +1,10 @@ package relayer import ( + "encoding/json" "fmt" "github.com/cosmos/relayer/relayer/provider" + "github.com/strangelove-ventures/lens/client" "strconv" "strings" "time" @@ -16,9 +18,9 @@ import ( func (c *Chain) LogFailedTx(res *provider.RelayerTxResponse, err error, msgs []provider.RelayerMessage) { if c.debug { c.Log(fmt.Sprintf("- [%s] -> failed sending transaction:", c.ChainID())) - //for _, msg := range msgs { - // //c.Print(msg, false, false) - //} + for _, msg := range msgs { + _ = c.Print(client.CosmosMsg(msg), false, false) + } } if err != nil { @@ -28,15 +30,41 @@ func (c *Chain) LogFailedTx(res *provider.RelayerTxResponse, err error, msgs []p } } - if res.Code != 0 && res.Data != "" { - c.logger.Info(fmt.Sprintf("✘ [%s]@{%d} - msg(%s) err(%d:%s)", - c.ChainID(), res.Height, getMsgTypes(msgs), res.Code, res.Data)) + if res != nil { + if res.Code != 0 && res.Data != "" { + c.logger.Info(fmt.Sprintf("✘ [%s]@{%d} - msg(%s) err(%d:%s)", + c.ChainID(), res.Height, getMsgTypes(msgs), res.Code, res.Data)) + } + } + + if c.debug && res != nil { + c.Log("- transaction response:") + _ = c.PrintRelayerTxResponse(res, false, false) + } +} + +func (c *Chain) PrintRelayerTxResponse(res *provider.RelayerTxResponse, text, indent bool) error { + var ( + out []byte + err error + ) + + switch { + case indent && text: + return fmt.Errorf("must pass either indent or text, not both") + case text: + // TODO: This isn't really a good option, + out = []byte(fmt.Sprintf("%v", res)) + default: + out, err = json.Marshal(res) + } + + if err != nil { + return err } - //if c.debug && !res.Empty() { - // c.Log("- transaction response:") - // c.Print(res, false, false) - //} + fmt.Println(string(out)) + return nil } // LogSuccessTx take the transaction and the messages to create it and logs the appropriate data @@ -138,3 +166,15 @@ func (c *Chain) logUnreceivedPackets(dst *Chain, packetType string, log string) func (c *Chain) errQueryUnrelayedPacketAcks() error { return fmt.Errorf("no error on QueryPacketUnrelayedAcknowledgements for %s, however response is nil", c.ChainID()) } + +func (c *Chain) LogRetryGetIBCUpdateHeader(n uint, err error) { + if c.debug { + c.Log(fmt.Sprintf("failed to get IBC update headers, try(%d/%d). Err: %v", n+1, RtyAttNum, err)) + } +} + +func (c *Chain) LogRetryGetLightSignedHeader(n uint, err error) { + if c.debug { + c.Log(fmt.Sprintf("failed to get light signed header, try(%d/%d). Err: %v", n+1, RtyAttNum, err)) + } +} diff --git a/relayer/naive-strategy.go b/relayer/naive-strategy.go index fb4cadc0297..07972d36bec 100644 --- a/relayer/naive-strategy.go +++ b/relayer/naive-strategy.go @@ -125,7 +125,10 @@ func UnrelayedAcknowledgements(src, dst *Chain) (*RelaySequences, error) { } eg.Go(func() error { - var res []*chantypes.PacketState + var ( + res []*chantypes.PacketState + err error + ) if err = retry.Do(func() error { // Query the packet commitment res, err = src.ChainProvider.QueryPacketAcknowledgements(uint64(srch), src.PathEnd.ChannelID, src.PathEnd.PortID) @@ -149,7 +152,10 @@ func UnrelayedAcknowledgements(src, dst *Chain) (*RelaySequences, error) { }) eg.Go(func() error { - var res []*chantypes.PacketState + var ( + res []*chantypes.PacketState + err error + ) if err = retry.Do(func() error { res, err = dst.ChainProvider.QueryPacketAcknowledgements(uint64(dsth), dst.PathEnd.ChannelID, dst.PathEnd.PortID) switch { @@ -177,6 +183,7 @@ func UnrelayedAcknowledgements(src, dst *Chain) (*RelaySequences, error) { eg.Go(func() error { // Query all packets sent by src that have been received by dst + var err error return retry.Do(func() error { rs.Src, err = dst.ChainProvider.QueryUnreceivedAcknowledgements(uint64(dsth), dst.PathEnd.ChannelID, dst.PathEnd.PortID, srcPacketSeq) return err @@ -187,6 +194,7 @@ func UnrelayedAcknowledgements(src, dst *Chain) (*RelaySequences, error) { eg.Go(func() error { // Query all packets sent by dst that have been received by src + var err error return retry.Do(func() error { rs.Dst, err = src.ChainProvider.QueryUnreceivedAcknowledgements(uint64(srch), src.PathEnd.ChannelID, src.PathEnd.PortID, dstPacketSeq) return err @@ -235,10 +243,12 @@ func RelayAcknowledgements(src, dst *Chain, sp *RelaySequences, maxTxSize, maxMs srcHeader, dstHeader ibcexported.Header ) eg.Go(func() error { + var err error srcHeader, err = src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.PathEnd.ClientID) return err }) eg.Go(func() error { + var err error dstHeader, err = dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.PathEnd.ClientID) return err }) @@ -419,3 +429,124 @@ func RelayPackets(src, dst *Chain, sp *RelaySequences, maxTxSize, maxMsgLength u return nil } + +// RelayPacket creates transactions to relay packets from src to dst and from dst to src +func RelayPacket(src, dst *Chain, sp *RelaySequences, maxTxSize, maxMsgLength, seqNum uint64) error { + // set the maximum relay transaction constraints + msgs := &RelayMsgs{ + Src: []provider.RelayerMessage{}, + Dst: []provider.RelayerMessage{}, + MaxTxSize: maxTxSize, + MaxMsgLength: maxMsgLength, + } + + srch, dsth, err := QueryLatestHeights(src, dst) + if err != nil { + return err + } + + // add messages for sequences on src + for _, seq := range sp.Src { + if seq == seqNum { + // Query src for the sequence number to get type of packet + var recvMsg, timeoutMsg provider.RelayerMessage + if err = retry.Do(func() error { + recvMsg, timeoutMsg, err = src.ChainProvider.RelayPacketFromSequence(src.ChainProvider, dst.ChainProvider, uint64(srch), uint64(dsth), seq, dst.PathEnd.ChannelID, dst.PathEnd.PortID, src.PathEnd.ChannelID, src.PathEnd.PortID, src.PathEnd.ClientID) + if err != nil { + fmt.Println("Failing to relay packet from seq on src") + } + return err + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + srch, dsth, _ = QueryLatestHeights(src, dst) + })); err != nil { + return err + } + + // depending on the type of message to be relayed, we need to + // send to different chains + if recvMsg != nil { + msgs.Dst = append(msgs.Dst, recvMsg) + } + + if timeoutMsg != nil { + msgs.Src = append(msgs.Src, timeoutMsg) + } + } + } + + // add messages for sequences on dst + for _, seq := range sp.Dst { + if seq == seqNum { + // Query dst for the sequence number to get type of packet + var recvMsg, timeoutMsg provider.RelayerMessage + if err = retry.Do(func() error { + recvMsg, timeoutMsg, err = dst.ChainProvider.RelayPacketFromSequence(dst.ChainProvider, src.ChainProvider, uint64(dsth), uint64(srch), seq, src.PathEnd.ChannelID, src.PathEnd.PortID, dst.PathEnd.ChannelID, dst.PathEnd.PortID, dst.PathEnd.ClientID) + if err != nil { + fmt.Println("Failing to relay packet from seq on dst") + } + return nil + }, RtyAtt, RtyDel, RtyErr, retry.OnRetry(func(n uint, err error) { + srch, dsth, _ = QueryLatestHeights(src, dst) + })); err != nil { + return err + } + + // depending on the type of message to be relayed, we need to + // send to different chains + if recvMsg != nil { + msgs.Src = append(msgs.Src, recvMsg) + } + + if timeoutMsg != nil { + msgs.Dst = append(msgs.Dst, timeoutMsg) + } + } + } + + if !msgs.Ready() { + src.Log(fmt.Sprintf("- No packets to relay between [%s]port{%s} and [%s]port{%s}", + src.ChainID(), src.PathEnd.PortID, dst.ChainID(), dst.PathEnd.PortID)) + return nil + } + + // Prepend non-empty msg lists with UpdateClient + if len(msgs.Dst) != 0 { + srcHeader, err := src.ChainProvider.GetIBCUpdateHeader(srch, dst.ChainProvider, dst.PathEnd.ClientID) + if err != nil { + return err + } + updateMsg, err := dst.ChainProvider.UpdateClient(dst.PathEnd.ClientID, srcHeader) + if err != nil { + return err + } + + msgs.Dst = append([]provider.RelayerMessage{updateMsg}, msgs.Dst...) + } + + if len(msgs.Src) != 0 { + dstHeader, err := dst.ChainProvider.GetIBCUpdateHeader(dsth, src.ChainProvider, src.PathEnd.ClientID) + if err != nil { + return err + } + updateMsg, err := src.ChainProvider.UpdateClient(src.PathEnd.ClientID, dstHeader) + if err != nil { + return err + } + + msgs.Src = append([]provider.RelayerMessage{updateMsg}, msgs.Src...) + } + + // send messages to their respective chains + if msgs.Send(src, dst); msgs.Success() { + if len(msgs.Dst) > 1 { + dst.logPacketsRelayed(src, len(msgs.Dst)-1) + } + if len(msgs.Src) > 1 { + src.logPacketsRelayed(dst, len(msgs.Src)-1) + } + } else { + fmt.Println() + } + + return nil +} diff --git a/relayer/query.go b/relayer/query.go index 0390ee5c4ac..3ca30af6da6 100644 --- a/relayer/query.go +++ b/relayer/query.go @@ -2,6 +2,7 @@ package relayer import ( "fmt" + "github.com/cosmos/relayer/relayer/provider" codectypes "github.com/cosmos/cosmos-sdk/codec/types" clienttypes "github.com/cosmos/ibc-go/v2/modules/core/02-client/types" @@ -38,6 +39,7 @@ func QueryConnectionPair(src, dst *Chain, srcH, dstH int64) (srcConn, dstConn *c return err }) eg.Go(func() error { + var err error dstConn, err = dst.ChainProvider.QueryConnection(dstH, dst.ConnectionID()) return err }) @@ -55,17 +57,37 @@ func QueryChannelPair(src, dst *Chain, srcH, dstH int64) (srcChan, dstChan *chan }) eg.Go(func() error { var err error - dstChan, err = dst.ChainProvider.QueryChannel(dstH, src.PathEnd.ChannelID, src.PathEnd.PortID) + dstChan, err = dst.ChainProvider.QueryChannel(dstH, dst.PathEnd.ChannelID, dst.PathEnd.PortID) return err }) - err = eg.Wait() + if err = eg.Wait(); err != nil { + return nil, nil, err + } return } -func GetLightSignedHeadersAtHeights(src, dst *Chain, srch, dsth int64) (ibcexported.Header, ibcexported.Header, error) { +// GetIBCUpdateHeaders returns a pair of IBC update headers which can be used to update an on chain light client +func GetIBCUpdateHeaders(srch, dsth int64, src, dst provider.ChainProvider, srcClientID, dstClientID string) (srcHeader, dstHeader ibcexported.Header, err error) { + var eg = new(errgroup.Group) + eg.Go(func() error { + var err error + srcHeader, err = src.GetIBCUpdateHeader(srch, dst, dstClientID) + return err + }) + eg.Go(func() error { + var err error + dstHeader, err = dst.GetIBCUpdateHeader(dsth, src, srcClientID) + return err + }) + if err = eg.Wait(); err != nil { + return nil, nil, err + } + return +} + +func GetLightSignedHeadersAtHeights(src, dst *Chain, srch, dsth int64) (srcUpdateHeader, dstUpdateHeader ibcexported.Header, err error) { var ( - eg = new(errgroup.Group) - srcUpdateHeader, dstUpdateHeader ibcexported.Header + eg = new(errgroup.Group) ) eg.Go(func() error { var err error @@ -80,10 +102,10 @@ func GetLightSignedHeadersAtHeights(src, dst *Chain, srch, dsth int64) (ibcexpor if err := eg.Wait(); err != nil { return nil, nil, err } - return srcUpdateHeader, dstUpdateHeader, nil + return } -// QueryTMClientState retrevies the latest consensus state for a client in state at a given height +// QueryTMClientState retrieves the latest consensus state for a client in state at a given height // and unpacks/cast it to tendermint clientstate func (c *Chain) QueryTMClientState(height int64) (*tmclient.ClientState, error) { clientStateRes, err := c.ChainProvider.QueryClientStateResponse(height, c.ClientID()) diff --git a/scripts/one-chain b/scripts/one-chain index 1d26e3a6274..b59b0ec9dac 100755 --- a/scripts/one-chain +++ b/scripts/one-chain @@ -68,9 +68,9 @@ delegate="100000000000stake" redirect $BINARY --home $CHAINDIR/$CHAINID --chain-id $CHAINID init $CHAINID sleep 1 -$BINARY --home $CHAINDIR/$CHAINID keys add validator $KEYRING --output json > $CHAINDIR/$CHAINID/validator_seed.json 2> /dev/null +$BINARY --home $CHAINDIR/$CHAINID keys add validator $KEYRING --output json > $CHAINDIR/$CHAINID/validator_seed.json 2>&1 sleep 1 -$BINARY --home $CHAINDIR/$CHAINID keys add user $KEYRING --output json > $CHAINDIR/$CHAINID/key_seed.json 2> /dev/null +$BINARY --home $CHAINDIR/$CHAINID keys add user $KEYRING --output json > $CHAINDIR/$CHAINID/key_seed.json 2>&1 sleep 1 redirect $BINARY --home $CHAINDIR/$CHAINID add-genesis-account $($BINARY --home $CHAINDIR/$CHAINID keys $KEYRING show user -a) $coins sleep 1 @@ -109,4 +109,4 @@ else fi # Start the gaia -$BINARY --home $CHAINDIR/$CHAINID start --pruning=nothing --grpc.address="0.0.0.0:$GRPCPORT" > $CHAINDIR/$CHAINID.log 2>&1 & +$BINARY --home $CHAINDIR/$CHAINID start --pruning=nothing --grpc-web.enable=false --grpc.address="0.0.0.0:$GRPCPORT" > $CHAINDIR/$CHAINID.log 2>&1 & diff --git a/test/relayer_chain_test.go b/test/relayer_chain_test.go index 1b631c232af..a0224f8274a 100644 --- a/test/relayer_chain_test.go +++ b/test/relayer_chain_test.go @@ -44,6 +44,7 @@ func chainTest(t *testing.T, tcs []testChain) { ) eg.Go(func() error { return retry.Do(func() error { + var err error srcExpected, err = src.ChainProvider.QueryBalance(src.ChainProvider.Key()) if srcExpected.IsZero() { return fmt.Errorf("expected non-zero balance. Err: %w", err) @@ -53,6 +54,7 @@ func chainTest(t *testing.T, tcs []testChain) { }) eg.Go(func() error { return retry.Do(func() error { + var err error dstExpected, err = dst.ChainProvider.QueryBalance(dst.ChainProvider.Key()) if dstExpected.IsZero() { return fmt.Errorf("expected non-zero balance. Err: %w", err)