From 6df92be90147ad9288317ec2f927246a27a5ec42 Mon Sep 17 00:00:00 2001 From: Gjermund Garaba Date: Thu, 23 Feb 2023 20:40:19 +0100 Subject: [PATCH] chore: v0.47 branch merge and bumps (#406) * Param proposals, balance inquiries, and IBC transfer error handling (#393) * Extract file writing/copying logic to helpers * Add ParamChangeProposal * Update file naming for param change proposal * Use Code to detect errors in IBC transfers * Add AllBalances and ParamChangeProposal * Add support for the Hermes relayer (#396) * adding some scaffolding for hermes relayer * chore: updating interface to accept create connection options type * chore: wip * wip: adding path map to hermes relayer type * writing mnemonic file * correctly reading toml config * ibc test passing with hermes relayer * learn ibc test passing with hermes relayer * adding parse client and connection output * adding hermes test cases * remove unused types * undid import change * reverted import change * reverted some unintentional changes * fix linting error * adding hermes to conformance matrix * adding default value for matrix file for CI * pass absolute value for matrix file * removed extra part of path * adding hermes to Labels function * add capabilities for hermes * temporarily strip down number of tests to verify hermes relayer * fixing conformance tests * fixing channel tests in TestRelayerSetup * revert to go rly to test * bump hermes version * extract json response correctly * extract json result from stdout * correct channel parsing json stdout * set field ClearOnStart to true * switch back to go relayer as default * add hermes to the default relayers list * Update version of ibc-go from v6 to v7 from merge * Bump sdk and ibc-go --------- Co-authored-by: bigs Co-authored-by: Cian Hatton --- .gitignore | 3 +- chain/cosmos/chain_node.go | 70 +++-- chain/cosmos/cosmos_chain.go | 34 +++ cmd/interchaintest/example_matrix.json | 2 +- cmd/interchaintest/interchaintest_test.go | 2 +- conformance/flush.go | 8 +- conformance/relayersetup.go | 15 +- conformance/test.go | 4 +- examples/ibc/interchain_accounts_test.go | 8 +- go.mod | 42 ++- go.sum | 81 +++--- interchain_test.go | 36 ++- internal/dockerutil/filewriter.go | 5 +- relayer/docker.go | 27 +- relayer/hermes/hermes_commander.go | 208 +++++++++++++++ relayer/hermes/hermes_config.go | 175 ++++++++++++ relayer/hermes/hermes_relayer.go | 307 ++++++++++++++++++++++ relayer/hermes/hermes_types.go | 95 +++++++ relayer/hermes/hermes_wallet.go | 42 +++ relayer/options.go | 11 + relayer/rly/cosmos_relayer.go | 6 +- relayerfactory.go | 16 ++ test_setup.go | 6 +- 23 files changed, 1089 insertions(+), 114 deletions(-) create mode 100644 relayer/hermes/hermes_commander.go create mode 100644 relayer/hermes/hermes_config.go create mode 100644 relayer/hermes/hermes_relayer.go create mode 100644 relayer/hermes/hermes_types.go create mode 100644 relayer/hermes/hermes_wallet.go diff --git a/.gitignore b/.gitignore index dff1bd8a8..d39e39ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Don't commit the interchaintest.test file, # regardless of where it was built. interchaintest.test - -/bin .idea +/bin vendor diff --git a/chain/cosmos/chain_node.go b/chain/cosmos/chain_node.go index 8ac5e9d86..0686f46f1 100644 --- a/chain/cosmos/chain_node.go +++ b/chain/cosmos/chain_node.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/types" + paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -145,8 +146,8 @@ func (tn *ChainNode) genesisFileContent(ctx context.Context) ([]byte, error) { } func (tn *ChainNode) overwriteGenesisFile(ctx context.Context, content []byte) error { - fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName) - if err := fw.WriteFile(ctx, tn.VolumeName, "config/genesis.json", content); err != nil { + err := tn.WriteFile(ctx, content, "config/genesis.json") + if err != nil { return fmt.Errorf("overwriting genesis.json: %w", err) } @@ -167,8 +168,8 @@ func (tn *ChainNode) copyGentx(ctx context.Context, destVal *ChainNode) error { return fmt.Errorf("getting gentx content: %w", err) } - fw := dockerutil.NewFileWriter(destVal.logger(), destVal.DockerClient, destVal.TestName) - if err := fw.WriteFile(ctx, destVal.VolumeName, relPath, gentx); err != nil { + err = destVal.WriteFile(ctx, gentx, relPath) + if err != nil { return fmt.Errorf("overwriting gentx: %w", err) } @@ -510,6 +511,25 @@ func (tn *ChainNode) InitHomeFolder(ctx context.Context) error { return err } +// WriteFile accepts file contents in a byte slice and writes the contents to +// the docker filesystem. relPath describes the location of the file in the +// docker volume relative to the home directory +func (tn *ChainNode) WriteFile(ctx context.Context, content []byte, relPath string) error { + fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName) + return fw.WriteFile(ctx, tn.VolumeName, relPath, content) +} + +// CopyFile adds a file from the host filesystem to the docker filesystem +// relPath describes the location of the file in the docker volume relative to +// the home directory +func (tn *ChainNode) CopyFile(ctx context.Context, srcPath, dstPath string) error { + content, err := os.ReadFile(srcPath) + if err != nil { + return err + } + return tn.WriteFile(ctx, content, dstPath) +} + // CreateKey creates a key in the keyring backend test for the given node func (tn *ChainNode) CreateKey(ctx context.Context, name string) error { tn.lock.Lock() @@ -653,14 +673,9 @@ type CodeInfosResponse struct { // StoreContract takes a file path to smart contract and stores it on-chain. Returns the contracts code id. func (tn *ChainNode) StoreContract(ctx context.Context, keyName string, fileName string) (string, error) { - content, err := os.ReadFile(fileName) - if err != nil { - return "", err - } - _, file := filepath.Split(fileName) - fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName) - if err := fw.WriteFile(ctx, tn.VolumeName, file, content); err != nil { + err := tn.CopyFile(ctx, fileName, file) + if err != nil { return "", fmt.Errorf("writing contract file to docker volume: %w", err) } @@ -736,13 +751,9 @@ func (tn *ChainNode) QueryContract(ctx context.Context, contractAddress string, // StoreClientContract takes a file path to a client smart contract and stores it on-chain. Returns the contracts code id. func (tn *ChainNode) StoreClientContract(ctx context.Context, keyName string, fileName string) (string, error) { content, err := os.ReadFile(fileName) - if err != nil { - return "", err - } - _, file := filepath.Split(fileName) - fw := dockerutil.NewFileWriter(tn.logger(), tn.DockerClient, tn.TestName) - if err := fw.WriteFile(ctx, tn.VolumeName, file, content); err != nil { + err = tn.WriteFile(ctx, content, file) + if err != nil { return "", fmt.Errorf("writing contract file to docker volume: %w", err) } @@ -824,6 +835,31 @@ func (tn *ChainNode) TextProposal(ctx context.Context, keyName string, prop Text return tn.ExecTx(ctx, keyName, command...) } +// ParamChangeProposal submits a param change proposal to the chain, signed by keyName. +func (tn *ChainNode) ParamChangeProposal(ctx context.Context, keyName string, prop *paramsutils.ParamChangeProposalJSON) (string, error) { + content, err := json.Marshal(prop) + if err != nil { + return "", err + } + + hash := sha256.Sum256(content) + proposalFilename := fmt.Sprintf("%x.json", hash) + err = tn.WriteFile(ctx, content, proposalFilename) + if err != nil { + return "", fmt.Errorf("writing param change proposal: %w", err) + } + + proposalPath := filepath.Join(tn.HomeDir(), proposalFilename) + + command := []string{ + "gov", "submit-proposal", + "param-change", + proposalPath, + } + + return tn.ExecTx(ctx, keyName, command...) +} + // DumpContractState dumps the state of a contract at a block height. func (tn *ChainNode) DumpContractState(ctx context.Context, contractAddress string, height int64) (*DumpContractStateResponse, error) { stdout, _, err := tn.ExecQuery(ctx, diff --git a/chain/cosmos/cosmos_chain.go b/chain/cosmos/cosmos_chain.go index c8f992f76..03b7eceb6 100644 --- a/chain/cosmos/cosmos_chain.go +++ b/chain/cosmos/cosmos_chain.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/cosmos-sdk/types" authTx "github.com/cosmos/cosmos-sdk/x/auth/tx" bankTypes "github.com/cosmos/cosmos-sdk/x/bank/types" + paramsutils "github.com/cosmos/cosmos-sdk/x/params/client/utils" chanTypes "github.com/cosmos/ibc-go/v7/modules/core/04-channel/types" dockertypes "github.com/docker/docker/api/types" volumetypes "github.com/docker/docker/api/types/volume" @@ -304,6 +305,9 @@ func (c *CosmosChain) SendIBCTransfer( if err != nil { return tx, fmt.Errorf("failed to get transaction %s: %w", txHash, err) } + if txResp.Code != 0 { + return tx, fmt.Errorf("error in transaction (code: %d): %s", txResp.Code, txResp.RawLog) + } tx.Height = uint64(txResp.Height) tx.TxHash = txHash // In cosmos, user is charged for entire gas requested, not the actual gas used. @@ -367,6 +371,16 @@ func (c *CosmosChain) TextProposal(ctx context.Context, keyName string, prop Tex return c.txProposal(txHash) } +// ParamChangeProposal submits a param change proposal to the chain, signed by keyName. +func (c *CosmosChain) ParamChangeProposal(ctx context.Context, keyName string, prop *paramsutils.ParamChangeProposalJSON) (tx TxProposal, _ error) { + txHash, err := c.getFullNode().ParamChangeProposal(ctx, keyName, prop) + if err != nil { + return tx, fmt.Errorf("failed to submit param change proposal: %w", err) + } + + return c.txProposal(txHash) +} + func (c *CosmosChain) txProposal(txHash string) (tx TxProposal, _ error) { txResp, err := c.getTransaction(txHash) if err != nil { @@ -449,6 +463,26 @@ func (c *CosmosChain) GetBalance(ctx context.Context, address string, denom stri return res.Balance.Amount.Int64(), nil } +// AllBalances fetches an account address's balance for all denoms it holds +func (c *CosmosChain) AllBalances(ctx context.Context, address string) (types.Coins, error) { + params := bankTypes.QueryAllBalancesRequest{Address: address} + grpcAddress := c.getFullNode().hostGRPCPort + conn, err := grpc.Dial(grpcAddress, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return nil, err + } + defer conn.Close() + + queryClient := bankTypes.NewQueryClient(conn) + res, err := queryClient.AllBalances(ctx, ¶ms) + + if err != nil { + return nil, err + } + + return res.GetBalances(), nil +} + func (c *CosmosChain) getTransaction(txHash string) (*types.TxResponse, error) { // Retry because sometimes the tx is not committed to state yet. var txResp *types.TxResponse diff --git a/cmd/interchaintest/example_matrix.json b/cmd/interchaintest/example_matrix.json index 6ecae2e45..d5dbe96c9 100644 --- a/cmd/interchaintest/example_matrix.json +++ b/cmd/interchaintest/example_matrix.json @@ -1,5 +1,5 @@ { - "Relayers": ["rly"], + "Relayers": ["rly", "hermes"], "ChainSets": [ [ diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 76625105a..dd65f4b5d 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -103,7 +103,7 @@ func setUpTestMatrix() error { if extraFlags.MatrixFile == "" { fmt.Fprintln(os.Stderr, "No matrix file provided, falling back to rly with gaia and osmosis") - testMatrix.Relayers = []string{"rly"} + testMatrix.Relayers = []string{"rly", "hermes"} testMatrix.ChainSets = [][]*interchaintest.ChainSpec{ { {Name: "gaia", Version: "v7.0.1"}, diff --git a/conformance/flush.go b/conformance/flush.go index 69fcfe579..0ee683bee 100644 --- a/conformance/flush.go +++ b/conformance/flush.go @@ -9,6 +9,7 @@ import ( interchaintest "github.com/strangelove-ventures/interchaintest/v7" "github.com/strangelove-ventures/interchaintest/v7/ibc" "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/relayer/hermes" "github.com/strangelove-ventures/interchaintest/v7/testreporter" "github.com/strangelove-ventures/interchaintest/v7/testutil" "github.com/stretchr/testify/require" @@ -100,9 +101,14 @@ func TestRelayerFlushing(t *testing.T, ctx context.Context, cf interchaintest.Ch afterFlushHeight, err := c0.Height(ctx) req.NoError(err) + //flush packets and flush acks are the same command in hermes and are not separated as in the go relayer // Ack shouldn't happen yet. _, err = testutil.PollForAck(ctx, c0, beforeTransferHeight, afterFlushHeight+2, tx.Packet) - req.ErrorIs(err, testutil.ErrNotFound) + if _, isHermes := r.(*hermes.Relayer); isHermes { + req.NoError(err) + } else { + req.ErrorIs(err, testutil.ErrNotFound) + } }) t.Run("flush acks", func(t *testing.T) { diff --git a/conformance/relayersetup.go b/conformance/relayersetup.go index 7e9e9702c..75d0427fb 100644 --- a/conformance/relayersetup.go +++ b/conformance/relayersetup.go @@ -5,13 +5,14 @@ import ( "fmt" "testing" - conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" interchaintest "github.com/strangelove-ventures/interchaintest/v7" "github.com/strangelove-ventures/interchaintest/v7/ibc" "github.com/strangelove-ventures/interchaintest/v7/testreporter" "github.com/strangelove-ventures/interchaintest/v7/testutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + conntypes "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" ) // TestRelayerSetup contains a series of subtests that configure a relayer step-by-step. @@ -102,7 +103,7 @@ func TestRelayerSetup(t *testing.T, ctx context.Context, cf interchaintest.Chain conn0 := conns0[0] req.NotEmpty(conn0.ID) req.NotEmpty(conn0.ClientID) - req.Equal(conn0.State, conntypes.OPEN.String()) + req.Subset([]string{conntypes.OPEN.String(), "Open"}, []string{conn0.State}) conns1, err := r.GetConnections(ctx, eRep, c1.Config().ChainID) req.NoError(err) @@ -111,7 +112,7 @@ func TestRelayerSetup(t *testing.T, ctx context.Context, cf interchaintest.Chain conn1 := conns1[0] req.NotEmpty(conn1.ID) req.NotEmpty(conn1.ClientID) - req.Equal(conn1.State, conntypes.OPEN.String()) + req.Subset([]string{conntypes.OPEN.String(), "Open"}, []string{conn1.State}) // Now validate counterparties. req.Equal(conn0.Counterparty.ClientId, conn1.ClientID) @@ -160,14 +161,14 @@ func TestRelayerSetup(t *testing.T, ctx context.Context, cf interchaintest.Chain // Piecemeal assertions against each channel. // Not asserting against ConnectionHops or ChannelID. - req.Equal(ch0.State, "STATE_OPEN") - req.Equal(ch0.Ordering, "ORDER_UNORDERED") + req.Subset([]string{"STATE_OPEN", "Open"}, []string{ch0.State}) + req.Subset([]string{"ORDER_UNORDERED", "Unordered"}, []string{ch0.Ordering}) req.Equal(ch0.Counterparty, ibc.ChannelCounterparty{PortID: "transfer", ChannelID: ch1.ChannelID}) req.Equal(ch0.Version, "ics20-1") req.Equal(ch0.PortID, "transfer") - req.Equal(ch1.State, "STATE_OPEN") - req.Equal(ch1.Ordering, "ORDER_UNORDERED") + req.Subset([]string{"STATE_OPEN", "Open"}, []string{ch1.State}) + req.Subset([]string{"ORDER_UNORDERED", "Unordered"}, []string{ch1.Ordering}) req.Equal(ch1.Counterparty, ibc.ChannelCounterparty{PortID: "transfer", ChannelID: ch0.ChannelID}) req.Equal(ch1.Version, "ics20-1") req.Equal(ch1.PortID, "transfer") diff --git a/conformance/test.go b/conformance/test.go index 51d8d0ebb..a9d470396 100644 --- a/conformance/test.go +++ b/conformance/test.go @@ -35,7 +35,6 @@ import ( "testing" "time" - transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" "github.com/docker/docker/client" interchaintest "github.com/strangelove-ventures/interchaintest/v7" "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" @@ -47,6 +46,8 @@ import ( "github.com/strangelove-ventures/interchaintest/v7/testutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" ) const ( @@ -328,6 +329,7 @@ func TestChainPair( } if relayerImpl == nil { + t.Logf("creating relayer: %s", rf.Name()) // startup both chains. // creates wallets in the relayer for src and dst chain. // funds relayer src and dst wallets on respective chain in genesis. diff --git a/examples/ibc/interchain_accounts_test.go b/examples/ibc/interchain_accounts_test.go index db9b0ab04..45cfcbdd6 100644 --- a/examples/ibc/interchain_accounts_test.go +++ b/examples/ibc/interchain_accounts_test.go @@ -268,12 +268,12 @@ func TestInterchainAccounts(t *testing.T) { chain1Chans, err := r.GetChannels(ctx, eRep, chain1.Config().ChainID) require.NoError(t, err) require.Equal(t, 1, len(chain1Chans)) - require.Equal(t, "STATE_CLOSED", chain1Chans[0].State) + require.Subset(t, []string{"STATE_CLOSED", "Closed"}, []string{chain1Chans[0].State}) chain2Chans, err := r.GetChannels(ctx, eRep, chain2.Config().ChainID) require.NoError(t, err) require.Equal(t, 1, len(chain2Chans)) - require.Equal(t, "STATE_CLOSED", chain2Chans[0].State) + require.Subset(t, []string{"STATE_CLOSED", "Closed"}, []string{chain2Chans[0].State}) // Attempt to open another channel for the same ICA _, _, err = chain1.Exec(ctx, registerICA, nil) @@ -294,12 +294,12 @@ func TestInterchainAccounts(t *testing.T) { chain1Chans, err = r.GetChannels(ctx, eRep, chain1.Config().ChainID) require.NoError(t, err) require.Equal(t, 2, len(chain1Chans)) - require.Equal(t, "STATE_OPEN", chain1Chans[1].State) + require.Subset(t, []string{"STATE_OPEN", "Open"}, []string{chain1Chans[1].State}) chain2Chans, err = r.GetChannels(ctx, eRep, chain2.Config().ChainID) require.NoError(t, err) require.Equal(t, 2, len(chain2Chans)) - require.Equal(t, "STATE_OPEN", chain2Chans[1].State) + require.Subset(t, []string{"STATE_OPEN", "Open"}, []string{chain2Chans[1].State}) } // parseInterchainAccountField takes a slice of bytes which should be returned when querying for an ICA via diff --git a/go.mod b/go.mod index 3dfc8ec36..e522e061a 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,9 @@ require ( github.com/atotto/clipboard v0.1.4 github.com/avast/retry-go/v4 v4.0.4 github.com/centrifuge/go-substrate-rpc-client/v4 v4.0.10 - github.com/cosmos/cosmos-sdk v0.47.0-rc1 + github.com/cosmos/cosmos-sdk v0.47.0-rc2 github.com/cosmos/go-bip39 v1.0.0 - github.com/cosmos/ibc-go/v7 v7.0.0-20230120105519-ae96bf3d5ee9 + github.com/cosmos/ibc-go/v7 v7.0.0-rc0.0.20230221164520-2b69d18da6eb github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v2 v2.0.0 github.com/docker/docker v20.10.17+incompatible @@ -23,6 +23,7 @@ require ( github.com/icza/dyno v0.0.0-20220812133438-f0b6f8a18845 github.com/libp2p/go-libp2p-core v0.15.1 github.com/mr-tron/base58 v1.2.0 + github.com/pelletier/go-toml v1.9.5 github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 github.com/stretchr/testify v1.8.1 github.com/tendermint/tendermint v0.37.0-rc2 @@ -31,16 +32,16 @@ require ( golang.org/x/crypto v0.4.0 golang.org/x/sync v0.1.0 golang.org/x/tools v0.1.12 - google.golang.org/grpc v1.52.0 + google.golang.org/grpc v1.52.3 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.17.3 ) require ( cloud.google.com/go v0.105.0 // indirect - cloud.google.com/go/compute v1.12.1 // indirect - cloud.google.com/go/compute/metadata v0.2.1 // indirect - cloud.google.com/go/iam v0.7.0 // indirect + cloud.google.com/go/compute v1.14.0 // indirect + cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/iam v0.8.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect cosmossdk.io/api v0.2.6 // indirect cosmossdk.io/core v0.3.2 // indirect @@ -57,7 +58,7 @@ require ( github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/bgentry/speakeasy v0.1.0 // indirect + github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/btcsuite/btcd v0.22.3 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cenkalti/backoff/v4 v4.1.3 // indirect @@ -68,9 +69,9 @@ require ( github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.1 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect - github.com/cosmos/gogoproto v1.4.3 // indirect + github.com/cosmos/gogoproto v1.4.4 // indirect github.com/cosmos/gorocksdb v1.2.0 // indirect - github.com/cosmos/iavl v0.19.4 // indirect + github.com/cosmos/iavl v0.19.5-rc.1 // indirect github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab // indirect github.com/cosmos/ledger-cosmos-go v0.12.1 // indirect github.com/cosmos/rosetta-sdk-go v0.9.0 // indirect @@ -107,8 +108,8 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect - github.com/googleapis/gax-go/v2 v2.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect + github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect @@ -137,7 +138,7 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-openssl v0.0.7 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect @@ -160,8 +161,7 @@ require ( github.com/multiformats/go-varint v0.0.6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect github.com/pierrec/xxHash v0.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -180,16 +180,14 @@ require ( github.com/sirupsen/logrus v1.9.0 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/cobra v1.6.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.14.0 // indirect - github.com/subosito/gotenv v1.4.1 // indirect + github.com/spf13/viper v1.15.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect - github.com/tendermint/btcd v0.1.1 // indirect - github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/tendermint/tm-db v0.6.7 // indirect github.com/tidwall/btree v1.5.2 // indirect @@ -198,7 +196,7 @@ require ( github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.14.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect - go.opencensus.io v0.23.0 // indirect + go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect golang.org/x/exp v0.0.0-20221019170559-20944726eadf // indirect @@ -209,9 +207,9 @@ require ( golang.org/x/term v0.3.0 // indirect golang.org/x/text v0.5.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.102.0 // indirect + google.golang.org/api v0.107.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect + google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect diff --git a/go.sum b/go.sum index 4a94e222d..055ecef5e 100644 --- a/go.sum +++ b/go.sum @@ -28,14 +28,14 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= -cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -148,8 +148,9 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s= +github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd v0.22.3 h1:kYNaWFvOw6xvqP0vR20RP1Zq1DVMBxEO8QN5d1/EfNg= @@ -216,22 +217,22 @@ github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= github.com/cosmos/cosmos-proto v1.0.0-beta.1 h1:iDL5qh++NoXxG8hSy93FdYJut4XfgbShIocllGaXx/0= github.com/cosmos/cosmos-proto v1.0.0-beta.1/go.mod h1:8k2GNZghi5sDRFw/scPL8gMSowT1vDA+5ouxL8GjaUE= -github.com/cosmos/cosmos-sdk v0.47.0-rc1 h1:ptoLIOAqFGoqnZeqgec9KvC2JIZ6CVIyMHHjti9p6dQ= -github.com/cosmos/cosmos-sdk v0.47.0-rc1/go.mod h1:yWd503ULBJ71Zuv7GD0/dYJuyeg4LGWAvjeI4wK/dfY= +github.com/cosmos/cosmos-sdk v0.47.0-rc2 h1:BwQC41zQXG/pN9DdLaWzYJrC911St5lYOQIoW4Hf5wQ= +github.com/cosmos/cosmos-sdk v0.47.0-rc2/go.mod h1:e0ZEpY/nhVoXAkijdHPdFOJNOXCddfvyFrFLp2QmCCY= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= -github.com/cosmos/gogoproto v1.4.3 h1:RP3yyVREh9snv/lsOvmsAPQt8f44LgL281X0IOIhhcI= -github.com/cosmos/gogoproto v1.4.3/go.mod h1:0hLIG5TR7IvV1fme1HCFKjfzW9X2x0Mo+RooWXCnOWU= +github.com/cosmos/gogoproto v1.4.4 h1:nVAsgLlAf5jeN0fV7hRlkZvf768zU+dy4pG+hxc2P34= +github.com/cosmos/gogoproto v1.4.4/go.mod h1:/yl6/nLwsZcZ2JY3OrqjRqvqCG9InUMcXRfRjQiF9DU= github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y= github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw= -github.com/cosmos/iavl v0.19.4 h1:t82sN+Y0WeqxDLJRSpNd8YFX5URIrT+p8n6oJbJ2Dok= -github.com/cosmos/iavl v0.19.4/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= -github.com/cosmos/ibc-go/v7 v7.0.0-20230120105519-ae96bf3d5ee9 h1:sFDeAy3X9Oyvvc93Lnmu2Ui5EIDFmQoRmTV3t1oh6bQ= -github.com/cosmos/ibc-go/v7 v7.0.0-20230120105519-ae96bf3d5ee9/go.mod h1:MPkVYbOkwFTK9b7Va8kQuK78zkyc1FRkgdHmPKMllKA= +github.com/cosmos/iavl v0.19.5-rc.1 h1:4PjF2PdScyPbN1WbXpiQU21YtyonnrMU31xN74g8Rkg= +github.com/cosmos/iavl v0.19.5-rc.1/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw= +github.com/cosmos/ibc-go/v7 v7.0.0-rc0.0.20230221164520-2b69d18da6eb h1:6StWAbbb0aPj6HxkFUJAoSjTwJtsrC56HK65JlMGjp0= +github.com/cosmos/ibc-go/v7 v7.0.0-rc0.0.20230221164520-2b69d18da6eb/go.mod h1:cD4Mltu+wH/FRXKaUP3tf0K/CzVoBp9e0r4x8OLUuSo= github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab h1:I9ialKTQo7248V827Bba4OuKPmk+FPzmTVHsLXaIJWw= github.com/cosmos/ics23/go v0.9.1-0.20221207100636-b1abd8678aab/go.mod h1:2CwqasX5dSD7Hbp/9b6lhK6BwoBDCBldx7gPKRukR60= github.com/cosmos/ledger-cosmos-go v0.12.1 h1:sMBxza5p/rNK/06nBSNmsI/WDqI0pVJFVNihy1Y984w= @@ -490,12 +491,12 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.0 h1:y8Yozv7SZtlU//QXbezB6QkpuE6jMD2/gfzk4AftXjs= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.6.0 h1:SXk3ABtQYDT/OH8jAyvEOQ58mgawq5C4o/4/89qN2ZU= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= @@ -679,8 +680,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= @@ -834,8 +835,8 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= @@ -955,8 +956,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= @@ -972,8 +973,8 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= github.com/strangelove-ventures/go-subkey v1.0.7 h1:cOP/Lajg3uxV/tvspu0m6+0Cu+DJgygkEAbx/s+f35I= github.com/strangelove-ventures/go-subkey v1.0.7/go.mod h1:E34izOIEm+sZ1YmYawYRquqBQWeZBjVB4pF7bMuhc1c= @@ -997,15 +998,11 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= -github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= -github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME= github.com/tendermint/tendermint v0.37.0-rc2 h1:2n1em+jfbhSv6QnBj8F6KHCpbIzZCB8KgcjidJUQNlY= @@ -1061,8 +1058,8 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1347,7 +1344,7 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U= +golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1445,8 +1442,8 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.102.0 h1:JxJl2qQ85fRMPNvlZY/enexbxpCjLwGhZUtgfGeQ51I= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.107.0 h1:I2SlFjD8ZWabaIFOfeEDg3pf0BHJDh6iYQ1ic3Yu/UU= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1501,8 +1498,8 @@ google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -1529,8 +1526,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk= -google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ= +google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/interchain_test.go b/interchain_test.go index 17f476ddd..77950022a 100644 --- a/interchain_test.go +++ b/interchain_test.go @@ -25,7 +25,15 @@ import ( clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" ) -func TestInterchain_DuplicateChain(t *testing.T) { +func TestInterchain_DuplicateChain_CosmosRly(t *testing.T) { + duplicateChainTest(t, ibc.CosmosRly) +} + +func TestInterchain_DuplicateChain_HermesRelayer(t *testing.T) { + duplicateChainTest(t, ibc.Hermes) +} + +func duplicateChainTest(t *testing.T, relayerImpl ibc.RelayerImplementation) { if testing.Short() { t.Skip("skipping in short mode") } @@ -45,7 +53,7 @@ func TestInterchain_DuplicateChain(t *testing.T) { gaia0, gaia1 := chains[0], chains[1] - r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( + r := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build( t, client, network, ) @@ -73,7 +81,15 @@ func TestInterchain_DuplicateChain(t *testing.T) { _ = ic.Close() } -func TestInterchain_GetRelayerWallets(t *testing.T) { +func TestInterchain_GetRelayerWallets_CosmosRly(t *testing.T) { + getRelayerWalletsTest(t, ibc.CosmosRly) +} + +func TestInterchain_GetRelayerWallets_HermesRelayer(t *testing.T) { + getRelayerWalletsTest(t, ibc.Hermes) +} + +func getRelayerWalletsTest(t *testing.T, relayerImpl ibc.RelayerImplementation) { if testing.Short() { t.Skip("skipping in short mode") } @@ -93,7 +109,7 @@ func TestInterchain_GetRelayerWallets(t *testing.T) { gaia0, gaia1 := chains[0], chains[1] - r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( + r := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build( t, client, network, ) @@ -229,7 +245,15 @@ func TestInterchain_CreateUser(t *testing.T) { }) } -func TestCosmosChain_BroadcastTx(t *testing.T) { +func TestCosmosChain_BroadcastTx_CosmosRly(t *testing.T) { + broadcastTxCosmosChainTest(t, ibc.CosmosRly) +} + +func TestCosmosChain_BroadcastTx_HermesRelayer(t *testing.T) { + broadcastTxCosmosChainTest(t, ibc.Hermes) +} + +func broadcastTxCosmosChainTest(t *testing.T, relayerImpl ibc.RelayerImplementation) { if testing.Short() { t.Skip("skipping in short mode") } @@ -249,7 +273,7 @@ func TestCosmosChain_BroadcastTx(t *testing.T) { gaia0, gaia1 := chains[0], chains[1] - r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( + r := interchaintest.NewBuiltinRelayerFactory(relayerImpl, zaptest.NewLogger(t)).Build( t, client, network, ) diff --git a/internal/dockerutil/filewriter.go b/internal/dockerutil/filewriter.go index 44bde3f4b..7b66ca2f2 100644 --- a/internal/dockerutil/filewriter.go +++ b/internal/dockerutil/filewriter.go @@ -5,7 +5,6 @@ import ( "bytes" "context" "fmt" - "path" "time" "github.com/docker/docker/api/types" @@ -48,10 +47,10 @@ func (w *FileWriter) WriteFile(ctx context.Context, volumeName, relPath string, Cmd: []string{ // Take the uid and gid of the mount path, // and set that as the owner of the new relative path. - `chown "$(stat -c '%u:%g' "$1")" "$2"`, + `chown -R "$(stat -c '%u:%g' "$1")" "$2"`, "_", // Meaningless arg0 for sh -c with positional args. mountPath, - path.Join(mountPath, relPath), + mountPath, }, // Use root user to avoid permission issues when reading files from the volume. diff --git a/relayer/docker.go b/relayer/docker.go index d8fc9b2da..1e330f4e2 100644 --- a/relayer/docker.go +++ b/relayer/docker.go @@ -20,6 +20,10 @@ import ( "go.uber.org/zap" ) +const ( + defaultRlyHomeDirectory = "/home/relayer" +) + // DockerRelayer provides a common base for relayer implementations // that run on Docker. type DockerRelayer struct { @@ -42,6 +46,8 @@ type DockerRelayer struct { // wallets contains a mapping of chainID to relayer wallet wallets map[string]ibc.Wallet + + homeDir string } var _ ibc.Relayer = (*DockerRelayer)(nil) @@ -64,12 +70,16 @@ func NewDockerRelayer(ctx context.Context, log *zap.Logger, testName string, cli wallets: map[string]ibc.Wallet{}, } + r.homeDir = defaultRlyHomeDirectory + for _, opt := range options { switch o := opt.(type) { case RelayerOptionDockerImage: r.customImage = &o.DockerImage case RelayerOptionImagePull: r.pullImage = o.Pull + case RelayerOptionHomeDir: + r.homeDir = o.HomeDir } } @@ -122,6 +132,21 @@ func NewDockerRelayer(ctx context.Context, log *zap.Logger, testName string, cli return &r, nil } +// WriteFileToHomeDir writes the given contents to a file at the relative path specified. The file is relative +// to the home directory in the relayer container. +func (r *DockerRelayer) WriteFileToHomeDir(ctx context.Context, relativePath string, contents []byte) error { + fw := dockerutil.NewFileWriter(r.log, r.client, r.testName) + if err := fw.WriteFile(ctx, r.volumeName, relativePath, contents); err != nil { + return fmt.Errorf("failed to write file: %w", err) + } + return nil +} + +// AddWallet adds a stores a wallet for the given chain ID. +func (r *DockerRelayer) AddWallet(chainID string, wallet ibc.Wallet) { + r.wallets[chainID] = wallet +} + func (r *DockerRelayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { // For rly this file is json, but the file extension should not matter. // Using .config to avoid implying any particular format. @@ -469,7 +494,7 @@ func (r *DockerRelayer) Bind() []string { // HomeDir returns the home directory of the relayer on the underlying Docker container's filesystem. func (r *DockerRelayer) HomeDir() string { - return "/home/relayer" + return r.homeDir } func (r *DockerRelayer) HostName(pathName string) string { diff --git a/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go new file mode 100644 index 000000000..f2174c096 --- /dev/null +++ b/relayer/hermes/hermes_commander.go @@ -0,0 +1,208 @@ +package hermes + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "go.uber.org/zap" + + ibcexported "github.com/cosmos/ibc-go/v7/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v7/modules/core/23-commitment/types" +) + +var _ relayer.RelayerCommander = &commander{} + +type commander struct { + log *zap.Logger +} + +func (c commander) Name() string { + return hermes +} + +func (c commander) DefaultContainerImage() string { + return defaultContainerImage +} + +func (c commander) DefaultContainerVersion() string { + return DefaultContainerVersion +} + +func (c commander) DockerUser() string { + return hermesDefaultUidGid +} + +func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { + jsonBz := extractJsonResult([]byte(stdout)) + var result ChannelOutputResult + if err := json.Unmarshal(jsonBz, &result); err != nil { + return nil, err + } + + var ibcChannelOutput []ibc.ChannelOutput + for _, r := range result.Result { + ibcChannelOutput = append(ibcChannelOutput, ibc.ChannelOutput{ + State: r.ChannelEnd.State, + Ordering: r.ChannelEnd.Ordering, + Counterparty: ibc.ChannelCounterparty{ + PortID: r.CounterPartyChannelEnd.Remote.PortID, + ChannelID: r.CounterPartyChannelEnd.Remote.ChannelID, + }, + ConnectionHops: r.ChannelEnd.ConnectionHops, + Version: r.ChannelEnd.Version, + PortID: r.ChannelEnd.Remote.PortID, + ChannelID: r.ChannelEnd.Remote.ChannelID, + }) + } + + return ibcChannelOutput, nil +} + +func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { + jsonBz := extractJsonResult([]byte(stdout)) + var queryResult ConnectionQueryResult + if err := json.Unmarshal(jsonBz, &queryResult); err != nil { + return ibc.ConnectionOutputs{}, err + } + + var outputs ibc.ConnectionOutputs + for _, r := range queryResult.Result { + + var versions []*ibcexported.Version + for _, v := range r.ConnectionEnd.Versions { + versions = append(versions, &ibcexported.Version{ + Identifier: v.Identifier, + Features: v.Features, + }) + } + + outputs = append(outputs, &ibc.ConnectionOutput{ + ID: r.ConnectionID, + ClientID: r.ConnectionEnd.ClientID, + Versions: versions, + State: r.ConnectionEnd.State, + Counterparty: &ibcexported.Counterparty{ + ClientId: r.ConnectionEnd.Counterparty.ClientID, + ConnectionId: r.ConnectionEnd.Counterparty.ConnectionID, + Prefix: types.MerklePrefix{ + KeyPrefix: []byte(r.ConnectionEnd.Counterparty.Prefix), + }, + }, + }) + } + return outputs, nil +} + +func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { + jsonBz := extractJsonResult([]byte(stdout)) + var queryResult ClientQueryResult + if err := json.Unmarshal(jsonBz, &queryResult); err != nil { + return ibc.ClientOutputs{}, err + } + + var clientOutputs []*ibc.ClientOutput + for _, r := range queryResult.ClientResult { + clientOutputs = append(clientOutputs, &ibc.ClientOutput{ + ClientID: r.ClientID, + ClientState: ibc.ClientState{ + ChainID: r.ChainID, + }, + }) + } + + return clientOutputs, nil +} + +func (c commander) Init(homeDir string) []string { + return nil +} + +func (c commander) GetChannels(chainID, homeDir string) []string { + // the --verbose and --show-counterparty options are required to get enough information to correctly populate + // the path. + return []string{hermes, "--json", "query", "channels", "--chain", chainID, "--show-counterparty", "--verbose"} +} + +func (c commander) GetConnections(chainID, homeDir string) []string { + return []string{hermes, "--config", fmt.Sprintf("%s/%s", homeDir, hermesConfigPath), "--json", "query", "connections", "--chain", chainID, "--verbose"} +} + +func (c commander) GetClients(chainID, homeDir string) []string { + return []string{hermes, "--config", fmt.Sprintf("%s/%s", homeDir, hermesConfigPath), "--json", "query", "clients", "--host-chain", chainID} +} + +func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { + return []string{hermes, "--config", fmt.Sprintf("%s/%s", homeDir, hermesConfigPath), "start", "--full-scan"} +} + +func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { + return NewWallet(keyName, address, mnemonic) +} + +func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { + // TODO: figure out how to implement this. + panic("implement me") +} + +// the following methods do not have a single command that cleanly maps to a single hermes command without +// additional logic wrapping them. They have been implemented one layer up in the hermes relayer. + +func (c commander) UpdateClients(pathName, homeDir string) []string { + panic("update clients implemented in hermes relayer not the commander") +} + +func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { + panic("generate path implemented in hermes relayer not the commander") +} + +func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { + panic("link path implemented in hermes relayer not the commander") +} + +func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { + panic("restore key implemented in hermes relayer not the commander") +} + +func (c commander) AddChainConfiguration(containerFilePath, homeDir string) []string { + panic("add chain configuration implemented in hermes relayer not the commander") +} + +func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { + panic("add key implemented in hermes relayer not the commander") +} + +func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { + panic("create channel implemented in hermes relayer not the commander") +} + +func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { + panic("create clients implemented in hermes relayer not the commander") +} + +func (c commander) CreateConnections(pathName string, homeDir string) []string { + panic("create connections implemented in hermes relayer not the commander") +} + +func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { + panic("flush acks implemented in hermes relayer not the commander") +} + +func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { + panic("flush packets implemented in hermes relayer not the commander") +} + +func (c commander) ConfigContent(ctx context.Context, cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { + panic("config content implemented in hermes relayer not the commander") +} + +func (c commander) ParseAddKeyOutput(stdout, stderr string) (ibc.Wallet, error) { + panic("add key implemented in Hermes Relayer") +} + +// ParseRestoreKeyOutput extracts the address from the hermes output. +func (c commander) ParseRestoreKeyOutput(stdout, stderr string) string { + panic("implemented in Hermes Relayer") +} diff --git a/relayer/hermes/hermes_config.go b/relayer/hermes/hermes_config.go new file mode 100644 index 000000000..50e1b9695 --- /dev/null +++ b/relayer/hermes/hermes_config.go @@ -0,0 +1,175 @@ +package hermes + +import ( + "fmt" + "strconv" + "strings" +) + + +// NewConfig returns a hermes Config with an entry for each of the provided ChainConfigs. +// The defaults were adapted from the sample config file found here: https://github.com/informalsystems/hermes/blob/master/config.toml +func NewConfig(chainConfigs ...ChainConfig) Config { + var chains []Chain + for _, hermesCfg := range chainConfigs { + chainCfg := hermesCfg.cfg + + gasPricesStr, err := strconv.ParseFloat(strings.ReplaceAll(chainCfg.GasPrices, chainCfg.Denom, ""), 32) + if err != nil { + panic(err) + } + + chains = append(chains, Chain{ + ID: chainCfg.ChainID, + RPCAddr: hermesCfg.rpcAddr, + GrpcAddr: fmt.Sprintf("http://%s", hermesCfg.grpcAddr), + WebsocketAddr: strings.ReplaceAll(fmt.Sprintf("%s/websocket", hermesCfg.rpcAddr), "http", "ws"), + RPCTimeout: "10s", + AccountPrefix: chainCfg.Bech32Prefix, + KeyName: hermesCfg.keyName, + AddressType: AddressType{ + Derivation: "cosmos", + }, + StorePrefix: "ibc", + DefaultGas: 100000, + MaxGas: 400000, + GasPrice: GasPrice{ + Price: gasPricesStr, + Denom: chainCfg.Denom, + }, + GasMultiplier: chainCfg.GasAdjustment, + MaxMsgNum: 30, + MaxTxSize: 2097152, + ClockDrift: "5s", + MaxBlockTime: "30s", + TrustingPeriod: "14days", + TrustThreshold: TrustThreshold{ + Numerator: "1", + Denominator: "3", + }, + MemoPrefix: "hermes", + }, + ) + } + + return Config{ + Global: Global{ + LogLevel: "info", + }, + Mode: Mode{ + Clients: Clients{ + Enabled: true, + Refresh: true, + Misbehaviour: true, + }, + Connections: Connections{ + Enabled: true, + }, + Channels: Channels{ + Enabled: true, + }, + Packets: Packets{ + Enabled: true, + ClearInterval: 0, + ClearOnStart: true, + TxConfirmation: false, + }, + }, + Rest: Rest{ + Enabled: false, + }, + Telemetry: Telemetry{ + Enabled: false, + }, + Chains: chains, + } +} + +type Config struct { + Global Global `toml:"global"` + Mode Mode `toml:"mode"` + Rest Rest `toml:"rest"` + Telemetry Telemetry `toml:"telemetry"` + Chains []Chain `toml:"chains"` +} + +type Global struct { + LogLevel string `toml:"log_level"` +} + +type Clients struct { + Enabled bool `toml:"enabled"` + Refresh bool `toml:"refresh"` + Misbehaviour bool `toml:"misbehaviour"` +} + +type Connections struct { + Enabled bool `toml:"enabled"` +} + +type Channels struct { + Enabled bool `toml:"enabled"` +} + +type Packets struct { + Enabled bool `toml:"enabled"` + ClearInterval int `toml:"clear_interval"` + ClearOnStart bool `toml:"clear_on_start"` + TxConfirmation bool `toml:"tx_confirmation"` +} + +type Mode struct { + Clients Clients `toml:"clients"` + Connections Connections `toml:"connections"` + Channels Channels `toml:"channels"` + Packets Packets `toml:"packets"` +} + +type Rest struct { + Enabled bool `toml:"enabled"` + Host string `toml:"host"` + Port int `toml:"port"` +} + +type Telemetry struct { + Enabled bool `toml:"enabled"` + Host string `toml:"host"` + Port int `toml:"port"` +} + +type AddressType struct { + Derivation string `toml:"derivation"` +} + +type GasPrice struct { + Price float64 `toml:"price"` + Denom string `toml:"denom"` +} + +type TrustThreshold struct { + Numerator string `toml:"numerator"` + Denominator string `toml:"denominator"` +} + +type Chain struct { + ID string `toml:"id"` + RPCAddr string `toml:"rpc_addr"` + GrpcAddr string `toml:"grpc_addr"` + WebsocketAddr string `toml:"websocket_addr"` + RPCTimeout string `toml:"rpc_timeout"` + AccountPrefix string `toml:"account_prefix"` + KeyName string `toml:"key_name"` + AddressType AddressType `toml:"address_type"` + StorePrefix string `toml:"store_prefix"` + DefaultGas int `toml:"default_gas"` + MaxGas int `toml:"max_gas"` + GasPrice GasPrice `toml:"gas_price"` + GasMultiplier float64 `toml:"gas_multiplier"` + MaxMsgNum int `toml:"max_msg_num"` + MaxTxSize int `toml:"max_tx_size"` + ClockDrift string `toml:"clock_drift"` + MaxBlockTime string `toml:"max_block_time"` + TrustingPeriod string `toml:"trusting_period"` + TrustThreshold TrustThreshold `toml:"trust_threshold"` + MemoPrefix string `toml:"memo_prefix,omitempty"` +} diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go new file mode 100644 index 000000000..f5ee2da87 --- /dev/null +++ b/relayer/hermes/hermes_relayer.go @@ -0,0 +1,307 @@ +package hermes + +import ( + "context" + "encoding/json" + "fmt" + "regexp" + "strings" + "time" + + "github.com/docker/docker/client" + "github.com/pelletier/go-toml" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/relayer" + "go.uber.org/zap" +) + +const ( + hermes = "hermes" + defaultContainerImage = "docker.io/informalsystems/hermes" + DefaultContainerVersion = "1.2.0" + + hermesDefaultUidGid = "1000:1000" + hermesHome = "/home/hermes" + hermesConfigPath = ".hermes/config.toml" +) + +var ( + _ ibc.Relayer = &Relayer{} + // parseRestoreKeyOutputPattern extracts the address from the hermes output. + // SUCCESS Restored key 'g2-2' (cosmos1czklnpzwaq3hfxtv6ne4vas2p9m5q3p3fgkz8e) on chain g2-2 + parseRestoreKeyOutputPattern = regexp.MustCompile(`\((.*)\)`) +) + +// Relayer is the ibc.Relayer implementation for hermes. +type Relayer struct { + *relayer.DockerRelayer + paths map[string]*pathConfiguration + chainConfigs []ChainConfig +} + +// ChainConfig holds all values required to write an entry in the "chains" section in the hermes config file. +type ChainConfig struct { + cfg ibc.ChainConfig + keyName, rpcAddr, grpcAddr string +} + +// pathConfiguration represents the concept of a "path" which is implemented at the interchain test level rather +// than the hermes level. +type pathConfiguration struct { + chainA, chainB pathChainConfig +} + +// pathChainConfig holds all values that will be required when interacting with a path. +type pathChainConfig struct { + chainID string + clientID string + connectionID string + portID string +} + +// NewHermesRelayer returns a new hermes relayer. +func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, networkID string, options ...relayer.RelayerOption) *Relayer { + c := commander{log: log} + options = append(options, relayer.HomeDir(hermesHome)) + dr, err := relayer.NewDockerRelayer(context.TODO(), log, testName, cli, networkID, c, options...) + if err != nil { + panic(err) + } + + return &Relayer{ + DockerRelayer: dr, + } +} + +// AddChainConfiguration is called once per chain configuration, which means that in the case of hermes, the single +// config file is overwritten with a new entry each time this function is called. +func (r *Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { + configContent, err := r.configContent(chainConfig, keyName, rpcAddr, grpcAddr) + if err != nil { + return fmt.Errorf("failed to generate config content: %w", err) + } + + if err := r.WriteFileToHomeDir(ctx, hermesConfigPath, configContent); err != nil { + return fmt.Errorf("failed to write hermes config: %w", err) + } + + return r.validateConfig(ctx, rep) +} + +// LinkPath performs the operations that happen when a path is linked. This includes creating clients, creating connections +// and establishing a channel. This happens across multiple operations rather than a single link path cli command. +func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) error { + _, ok := r.paths[pathName] + if !ok { + return fmt.Errorf("path %s not found", pathName) + } + + if err := r.CreateClients(ctx, rep, pathName, clientOpts); err != nil { + return err + } + + if err := r.CreateConnections(ctx, rep, pathName); err != nil { + return err + } + + if err := r.CreateChannel(ctx, rep, pathName, channelOpts); err != nil { + return err + } + + return nil +} + +func (r *Relayer) CreateChannel(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, opts ibc.CreateChannelOptions) error { + pathConfig := r.paths[pathName] + cmd := []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--a-connection", pathConfig.chainA.connectionID} + res := r.Exec(ctx, rep, cmd, nil) + if res.Err != nil { + return res.Err + } + pathConfig.chainA.portID = opts.SourcePortName + pathConfig.chainB.portID = opts.DestPortName + return nil +} + +func (r *Relayer) CreateConnections(ctx context.Context, rep ibc.RelayerExecReporter, pathName string) error { + pathConfig := r.paths[pathName] + cmd := []string{hermes, "--json", "create", "connection", "--a-chain", pathConfig.chainA.chainID, "--a-client", pathConfig.chainA.clientID, "--b-client", pathConfig.chainB.clientID} + + res := r.Exec(ctx, rep, cmd, nil) + if res.Err != nil { + return res.Err + } + + chainAConnectionID, chainBConnectionID, err := getConnectionIDsFromStdout(res.Stdout) + if err != nil { + return err + } + pathConfig.chainA.connectionID = chainAConnectionID + pathConfig.chainB.connectionID = chainBConnectionID + return res.Err +} + +func (r *Relayer) UpdateClients(ctx context.Context, rep ibc.RelayerExecReporter, pathName string) error { + pathConfig, ok := r.paths[pathName] + if !ok { + return fmt.Errorf("path %s not found", pathName) + } + updateChainACmd := []string{hermes, "--json", "update", "client", "--host-chain", pathConfig.chainA.chainID, "--client", pathConfig.chainA.clientID} + res := r.Exec(ctx, rep, updateChainACmd, nil) + if res.Err != nil { + return res.Err + } + updateChainBCmd := []string{hermes, "--json", "update", "client", "--host-chain", pathConfig.chainB.chainID, "--client", pathConfig.chainB.clientID} + return r.Exec(ctx, rep, updateChainBCmd, nil).Err +} + +// CreateClients creates clients on both chains. +// Note: in the go relayer this can be done with a single command using the path reference, +// however in Hermes this needs to be done as two separate commands. +func (r *Relayer) CreateClients(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, opts ibc.CreateClientOptions) error { + pathConfig := r.paths[pathName] + chainACreateClientCmd := []string{hermes, "--json", "create", "client", "--host-chain", pathConfig.chainA.chainID, "--reference-chain", pathConfig.chainB.chainID} + res := r.Exec(ctx, rep, chainACreateClientCmd, nil) + if res.Err != nil { + return res.Err + } + + chainAClientId, err := getClientIdFromStdout(res.Stdout) + if err != nil { + return err + } + pathConfig.chainA.clientID = chainAClientId + + chainBCreateClientCmd := []string{hermes, "--json", "create", "client", "--host-chain", pathConfig.chainB.chainID, "--reference-chain", pathConfig.chainA.chainID} + res = r.Exec(ctx, rep, chainBCreateClientCmd, nil) + if res.Err != nil { + return res.Err + } + + chainBClientId, err := getClientIdFromStdout(res.Stdout) + if err != nil { + return err + } + pathConfig.chainB.clientID = chainBClientId + + return res.Err +} + +// RestoreKey restores a key from a mnemonic. In hermes, you must provide a file containing the mnemonic. We need +// to copy the contents of the mnemonic into a file on disk and then reference the newly created file. +func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, chainID, keyName, coinType, mnemonic string) error { + + relativeMnemonicFilePath := fmt.Sprintf("%s/mnemonic.txt", chainID) + if err := r.WriteFileToHomeDir(ctx, relativeMnemonicFilePath, []byte(mnemonic)); err != nil { + return fmt.Errorf("failed to write mnemonic file: %w", err) + } + + cmd := []string{hermes, "keys", "add", "--chain", chainID, "--mnemonic-file", fmt.Sprintf("%s/%s", r.HomeDir(), relativeMnemonicFilePath), "--key-name", keyName} + + // Restoring a key should be near-instantaneous, so add a 1-minute timeout + // to detect if Docker has hung. + ctx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + + res := r.Exec(ctx, rep, cmd, nil) + if res.Err != nil { + return res.Err + } + + addrBytes := parseRestoreKeyOutput(string(res.Stdout)) + r.AddWallet(chainID, NewWallet(chainID, addrBytes, mnemonic)) + return nil +} + +func (r *Relayer) FlushAcknowledgements(ctx context.Context, rep ibc.RelayerExecReporter, pathName, channelID string) error { + return r.FlushPackets(ctx, rep, pathName, channelID) +} + +func (r *Relayer) FlushPackets(ctx context.Context, rep ibc.RelayerExecReporter, pathName, channelID string) error { + path := r.paths[pathName] + cmd := []string{hermes, "clear", "packets", "--chain", path.chainA.chainID, "--channel", channelID, "--port", path.chainA.portID} + res := r.Exec(ctx, rep, cmd, nil) + return res.Err +} + +// GeneratePath establishes an in memory path representation. The concept does not exist in hermes, so it is handled +// at the interchain test level. +func (r *Relayer) GeneratePath(ctx context.Context, rep ibc.RelayerExecReporter, srcChainID, dstChainID, pathName string) error { + if r.paths == nil { + r.paths = map[string]*pathConfiguration{} + } + r.paths[pathName] = &pathConfiguration{ + chainA: pathChainConfig{ + chainID: srcChainID, + }, + chainB: pathChainConfig{ + chainID: dstChainID, + }, + } + return nil +} + +// configContent returns the contents of the hermes config file as a byte array. Note: as hermes expects a single file +// rather than multiple config files, we need to maintain a list of chain configs each time they are added to write the +// full correct file update calling Relayer.AddChainConfiguration. +func (r *Relayer) configContent(cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { + r.chainConfigs = append(r.chainConfigs, ChainConfig{ + cfg: cfg, + keyName: keyName, + rpcAddr: rpcAddr, + grpcAddr: grpcAddr, + }) + hermesConfig := NewConfig(r.chainConfigs...) + bz, err := toml.Marshal(hermesConfig) + if err != nil { + return nil, err + } + return bz, nil +} + +// validateConfig validates the hermes config file. Any errors are propagated to the test. +func (r *Relayer) validateConfig(ctx context.Context, rep ibc.RelayerExecReporter) error { + cmd := []string{hermes, "--config", fmt.Sprintf("%s/%s", r.HomeDir(), hermesConfigPath), "config", "validate"} + res := r.Exec(ctx, rep, cmd, nil) + if res.Err != nil { + return res.Err + } + return nil +} + +// extractJsonResult extracts the json result for the hermes query. +func extractJsonResult(stdout []byte) []byte { + stdoutLines := strings.Split(string(stdout), "\n") + var jsonOutput string + for _, line := range stdoutLines { + if strings.Contains(line, "result") { + jsonOutput = line + break + } + } + return []byte(jsonOutput) +} + +// getClientIdFromStdout extracts the client ID from stdout. +func getClientIdFromStdout(stdout []byte) (string, error) { + var clientCreationResult ClientCreationResponse + if err := json.Unmarshal(extractJsonResult(stdout), &clientCreationResult); err != nil { + return "", err + } + return clientCreationResult.Result.CreateClient.ClientID, nil +} + +// getConnectionIDsFromStdout extracts the connectionIDs on both ends from the stdout. +func getConnectionIDsFromStdout(stdout []byte) (string, string, error) { + var connectionResponse ConnectionResponse + if err := json.Unmarshal(extractJsonResult(stdout), &connectionResponse); err != nil { + return "", "", err + } + return connectionResponse.Result.ASide.ConnectionID, connectionResponse.Result.BSide.ConnectionID, nil +} + +// parseRestoreKeyOutput extracts the address from the hermes output. +func parseRestoreKeyOutput(stdout string) string { + fullMatchIdx, addressGroupIdx := 0, 1 + return parseRestoreKeyOutputPattern.FindAllStringSubmatch(stdout, -1)[fullMatchIdx][addressGroupIdx] +} diff --git a/relayer/hermes/hermes_types.go b/relayer/hermes/hermes_types.go new file mode 100644 index 000000000..0a13c286c --- /dev/null +++ b/relayer/hermes/hermes_types.go @@ -0,0 +1,95 @@ +package hermes + +// ClientCreationResponse contains the minimum required values to extract the client id from the hermes response. +type ClientCreationResponse struct { + Result CreateClientResult `json:"result"` +} + +type CreateClient struct { + ClientID string `json:"client_id"` + ClientType string `json:"client_type"` +} + +type CreateClientResult struct { + CreateClient CreateClient `json:"CreateClient"` +} + +// ConnectionResponse contains the minimum required values to extract the connection id from both sides. +type ConnectionResponse struct { + Result ConnectionResult `json:"result"` +} + +type ConnectionResult struct { + ASide ConnectionSide `json:"a_side"` + BSide ConnectionSide `json:"b_side"` +} + +type ConnectionSide struct { + ConnectionID string `json:"connection_id"` +} + +// ChannelOutputResult contains the minimum required channel values. +type ChannelOutputResult struct { + Result []ChannelResult `json:"result"` +} + +type ChannelResult struct { + ChannelEnd ChannelEnd `json:"channel_end"` + CounterPartyChannelEnd ChannelEnd `json:"counterparty_channel_end"` +} + +type ChannelEnd struct { + ConnectionHops []string `json:"connection_hops"` + Ordering string `json:"ordering"` + State string `json:"state"` + Version string `json:"version"` + Remote ChannelAndPortId `json:"remote"` +} + +type ChannelAndPortId struct { + ChannelID string `json:"channel_id"` + PortID string `json:"port_id"` +} + +type ConnectionQueryResult struct { + Result []Result `json:"result"` + Status string `json:"status"` +} + +type Counterparty struct { + ClientID string `json:"client_id"` + ConnectionID string `json:"connection_id"` + Prefix string `json:"prefix"` +} + +type DelayPeriod struct { + Nanos int `json:"nanos"` + Secs int `json:"secs"` +} + +type Versions struct { + Features []string `json:"features"` + Identifier string `json:"identifier"` +} + +type ConnectionEnd struct { + ClientID string `json:"client_id"` + Counterparty Counterparty `json:"counterparty"` + DelayPeriod DelayPeriod `json:"delay_period"` + State string `json:"state"` + Versions []Versions `json:"versions"` +} + +type Result struct { + ConnectionEnd ConnectionEnd `json:"connection_end"` + ConnectionID string `json:"connection_id"` +} + +type ClientQueryResult struct { + ClientResult []ClientResult `json:"result"` +} + +type ClientResult struct { + ChainID string `json:"chain_id"` + ClientID string `json:"client_id"` +} diff --git a/relayer/hermes/hermes_wallet.go b/relayer/hermes/hermes_wallet.go new file mode 100644 index 000000000..f3742cd7a --- /dev/null +++ b/relayer/hermes/hermes_wallet.go @@ -0,0 +1,42 @@ +package hermes + +import "github.com/strangelove-ventures/interchaintest/v7/ibc" + +var _ ibc.Wallet = &Wallet{} + +type WalletModel struct { + Mnemonic string `json:"mnemonic"` + Address string `json:"address"` +} + +type Wallet struct { + mnemonic string + address string + keyName string +} + +func NewWallet(keyname string, address string, mnemonic string) *Wallet { + return &Wallet{ + mnemonic: mnemonic, + address: address, + keyName: keyname, + } +} + +func (w *Wallet) KeyName() string { + return w.keyName +} + +func (w *Wallet) FormattedAddress() string { + return w.address +} + +// Get mnemonic, only used for relayer wallets +func (w *Wallet) Mnemonic() string { + return w.mnemonic +} + +// Get Address +func (w *Wallet) Address() []byte { + return []byte(w.address) +} diff --git a/relayer/options.go b/relayer/options.go index 659140f30..fdbb73ec9 100644 --- a/relayer/options.go +++ b/relayer/options.go @@ -16,6 +16,13 @@ type RelayerOptionDockerImage struct { DockerImage ibc.DockerImage } +// RelayerOptionHomeDir allows the configuration of the relayer home directory. +type RelayerOptionHomeDir struct { + HomeDir string +} + +func (r RelayerOptionHomeDir) relayerOption() {} + // CustomDockerImage overrides the default relayer docker image. // uidGid is the uid:gid format owner that should be used within the container. // If uidGid is empty, root user will be assumed. @@ -29,6 +36,10 @@ func CustomDockerImage(repository string, version string, uidGid string) Relayer } } +func HomeDir(homeDir string) RelayerOption { + return RelayerOptionHomeDir{HomeDir: homeDir} +} + func (opt RelayerOptionDockerImage) relayerOption() {} type RelayerOptionImagePull struct { diff --git a/relayer/rly/cosmos_relayer.go b/relayer/rly/cosmos_relayer.go index 57528d4d2..d044bdc13 100644 --- a/relayer/rly/cosmos_relayer.go +++ b/relayer/rly/cosmos_relayer.go @@ -14,7 +14,9 @@ import ( "go.uber.org/zap" ) -const RlyDefaultUidGid = "100:1000" +const ( + RlyDefaultUidGid = "100:1000" +) // CosmosRelayer is the ibc.Relayer implementation for github.com/cosmos/relayer. type CosmosRelayer struct { @@ -155,7 +157,7 @@ func (commander) CreateClient(pathName, homeDir, customeClientTrustingPeriod str } } -func (commander) CreateConnections(pathName, homeDir string) []string { +func (commander) CreateConnections(pathName string, homeDir string) []string { return []string{ "rly", "tx", "connection", pathName, "--home", homeDir, diff --git a/relayerfactory.go b/relayerfactory.go index 2dc271380..34c5e8cd6 100644 --- a/relayerfactory.go +++ b/relayerfactory.go @@ -8,6 +8,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v7/ibc" "github.com/strangelove-ventures/interchaintest/v7/label" "github.com/strangelove-ventures/interchaintest/v7/relayer" + "github.com/strangelove-ventures/interchaintest/v7/relayer/hermes" "github.com/strangelove-ventures/interchaintest/v7/relayer/rly" "go.uber.org/zap" ) @@ -65,6 +66,8 @@ func (f builtinRelayerFactory) Build( networkID, f.options..., ) + case ibc.Hermes: + return hermes.NewHermesRelayer(f.log, t.Name(), cli, networkID, f.options...) default: panic(fmt.Errorf("RelayerImplementation %v unknown", f.impl)) } @@ -83,6 +86,14 @@ func (f builtinRelayerFactory) Name() string { } } return "rly@" + rly.DefaultContainerVersion + case ibc.Hermes: + for _, opt := range f.options { + switch o := opt.(type) { + case relayer.RelayerOptionDockerImage: + return "hermes@" + o.DockerImage.Version + } + } + return "hermes@" + hermes.DefaultContainerVersion default: panic(fmt.Errorf("RelayerImplementation %v unknown", f.impl)) } @@ -92,6 +103,8 @@ func (f builtinRelayerFactory) Labels() []label.Relayer { switch f.impl { case ibc.CosmosRly: return []label.Relayer{label.Rly} + case ibc.Hermes: + return []label.Relayer{label.Hermes} default: panic(fmt.Errorf("RelayerImplementation %v unknown", f.impl)) } @@ -103,6 +116,9 @@ func (f builtinRelayerFactory) Capabilities() map[relayer.Capability]bool { switch f.impl { case ibc.CosmosRly: return rly.Capabilities() + case ibc.Hermes: + // TODO: specify capability for hermes. + return rly.Capabilities() default: panic(fmt.Errorf("RelayerImplementation %v unknown", f.impl)) } diff --git a/test_setup.go b/test_setup.go index 7fcda58cd..18298b532 100644 --- a/test_setup.go +++ b/test_setup.go @@ -128,10 +128,8 @@ func StopStartRelayerWithPreStartFuncs( return nil, fmt.Errorf("failed to start relayer: %w", err) } } else { - for _, path := range pathNames { - if err := relayerImpl.StartRelayer(ctx, eRep, path); err != nil { - return nil, fmt.Errorf("failed to start relayer: %w", err) - } + if err := relayerImpl.StartRelayer(ctx, eRep, pathNames...); err != nil { + return nil, fmt.Errorf("failed to start relayer: %w", err) } }