From 0072f44797399a9d4cfbf13d7ce42d097b0c9f3d Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 2 Feb 2023 16:47:35 +0000 Subject: [PATCH 01/32] adding some scaffolding for hermes relayer --- .gitignore | 4 +- relayer/hermes/hermes_relayer.go | 190 +++++++++++++++++++++++++++++++ relayer/hermes/wallet.go | 42 +++++++ 3 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 relayer/hermes/hermes_relayer.go create mode 100644 relayer/hermes/wallet.go diff --git a/.gitignore b/.gitignore index 0a14d388b..6c86de8c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Don't commit the interchaintest.test file, # regardless of where it was built. interchaintest.test - -/bin \ No newline at end of file +.idea +/bin diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go new file mode 100644 index 000000000..209ac14e9 --- /dev/null +++ b/relayer/hermes/hermes_relayer.go @@ -0,0 +1,190 @@ +package hermes + +import ( + "context" + + "github.com/strangelove-ventures/interchaintest/v6/ibc" + "github.com/strangelove-ventures/interchaintest/v6/relayer" + "go.uber.org/zap" +) + +const ( + name = "hermes" + defaultContainerImage = "foo" + defaultContainerVersion = "v1.0.0" + + // TODO: this was taken from RlyDefaultUidGid. Figure out what value should be used. + hermesDefaultUidGid = "100:1000" +) + +var _ ibc.Relayer = &Relayer{} + +// Relayer is the ibc.Relayer implementation for hermes. +type Relayer struct { + *relayer.DockerRelayer +} + +func (*Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { + // We need a new implementation of AddChainConfiguration because the go relayer supports writing multiple chain config files + // but hermes has them all in a single toml file. This function will get called once per chain and so will need to build up state somewhere and write + // the final file at the end. + return nil +} + +func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) error { + 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) GeneratePath(ctx context.Context, rep ibc.RelayerExecReporter, srcChainID, dstChainID, pathName string) error { + // generate path gets called in interchain.Build. Hermes doesn't have this concept so something will need to be changed here. + return nil +} + +var _ relayer.RelayerCommander = &commander{} + +type commander struct { + log *zap.Logger +} + +func (c commander) Name() string { + return name +} + +func (c commander) DefaultContainerImage() string { + return defaultContainerImage +} + +func (c commander) DefaultContainerVersion() string { + return defaultContainerVersion +} + +func (c commander) DockerUser() string { + return hermesDefaultUidGid +} + +func (c commander) ConfigContent(ctx context.Context, cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { + return nil, nil +} + +func (c commander) ParseAddKeyOutput(stdout, stderr string) (ibc.Wallet, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseRestoreKeyOutput(stdout, stderr string) string { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) Init(homeDir string) []string { + return []string{ + name, "config", "init", + "--home", homeDir, + } +} + +func (c commander) AddChainConfiguration(containerFilePath, homeDir string) []string { + //TODO implement me + panic("implement me") +} + +func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { + // hermes keys add --chain foo --key-file + panic("implement me") +} + +func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { + + // hermes create channel [OPTIONS] --a-chain --a-connection --a-port --b-port + // return []string{name, "create", "channel", "--a-chain", "CHAIN_ID", "--a-connection", "", "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName} + return []string{name, "create", "channel", "--a-chain", "?????", "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--new-client-connection"} + +} + +func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { + //TODO implement me + panic("implement me") +} + +func (c commander) CreateConnections(pathName, homeDir string) []string { + //TODO implement me + panic("implement me") +} + +func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { + //TODO implement me + panic("implement me") +} + +func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { + //TODO implement me + panic("implement me") +} + +func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { + //TODO implement me + panic("implement me") +} + +func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { + panic("implement me") +} + +func (c commander) GetChannels(chainID, homeDir string) []string { + return []string{name, "query", "channels", "--home", homeDir, "--chain", chainID} +} + +func (c commander) GetConnections(chainID, homeDir string) []string { + panic("implement me") +} + +func (c commander) GetClients(chainID, homeDir string) []string { + panic("implement me") +} + +func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { + panic("implement me") +} + +func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { + panic("implement me") +} + +func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { + return []string{"hermes", "start", "--full-scan", "--home", homeDir} +} + +func (c commander) UpdateClients(pathName, homeDir string) []string { + panic("implement me") +} + +func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { + return NewWallet(keyName, address, mnemonic) +} diff --git a/relayer/hermes/wallet.go b/relayer/hermes/wallet.go new file mode 100644 index 000000000..064231054 --- /dev/null +++ b/relayer/hermes/wallet.go @@ -0,0 +1,42 @@ +package hermes + +import "github.com/strangelove-ventures/interchaintest/v6/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) +} From fedafd62d296465a1a7b12796a6bf6b8dd0eb63d Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 9 Feb 2023 10:59:48 +0000 Subject: [PATCH 02/32] chore: updating interface to accept create connection options type --- conformance/relayersetup.go | 5 +- ibc/relayer.go | 17 ++-- interchain.go | 3 +- relayer/docker.go | 12 +-- relayer/hermes/hermes_relayer.go | 149 +++++++++++++++++++++++-------- relayer/rly/cosmos_relayer.go | 4 +- 6 files changed, 136 insertions(+), 54 deletions(-) diff --git a/conformance/relayersetup.go b/conformance/relayersetup.go index 3cd306a2f..e2b7b6743 100644 --- a/conformance/relayersetup.go +++ b/conformance/relayersetup.go @@ -5,13 +5,14 @@ import ( "fmt" "testing" - conntypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + conntypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" ) // TestRelayerSetup contains a series of subtests that configure a relayer step-by-step. @@ -92,7 +93,7 @@ func TestRelayerSetup(t *testing.T, ctx context.Context, cf interchaintest.Chain req := require.New(rep.TestifyT(t)) eRep := rep.RelayerExecReporter(t) - req.NoError(r.CreateConnections(ctx, eRep, pathName)) + req.NoError(r.CreateConnections(ctx, eRep, pathName, ibc.CreateConnectionOptions{})) // Assert against the singly created connections individually. conns0, err := r.GetConnections(ctx, eRep, c0.Config().ChainID) diff --git a/ibc/relayer.go b/ibc/relayer.go index 801524a66..bf467786c 100644 --- a/ibc/relayer.go +++ b/ibc/relayer.go @@ -39,7 +39,7 @@ type Relayer interface { GeneratePath(ctx context.Context, rep RelayerExecReporter, srcChainID, dstChainID, pathName string) error // setup channels, connections, and clients - LinkPath(ctx context.Context, rep RelayerExecReporter, pathName string, channelOpts CreateChannelOptions, clientOptions CreateClientOptions) error + LinkPath(ctx context.Context, rep RelayerExecReporter, pathName string, channelOpts CreateChannelOptions, clientOptions CreateClientOptions, connectionOpts CreateConnectionOptions) error // update path channel filter UpdatePath(ctx context.Context, rep RelayerExecReporter, pathName string, filter ChannelFilter) error @@ -76,7 +76,7 @@ type Relayer interface { // CreateConnections performs the connection handshake steps necessary for creating a connection // between the src and dst chains. - CreateConnections(ctx context.Context, rep RelayerExecReporter, pathName string) error + CreateConnections(ctx context.Context, rep RelayerExecReporter, pathName string, opts CreateConnectionOptions) error // CreateChannel creates a channel on the given path with the provided options. CreateChannel(ctx context.Context, rep RelayerExecReporter, pathName string, opts CreateChannelOptions) error @@ -198,10 +198,10 @@ type RelayerExecResult struct { // CreateChannelOptions contains the configuration for creating a channel. type CreateChannelOptions struct { - SourcePortName string - DestPortName string - - Order Order + SourcePortName string + DestPortName string + ChainAID, ChainBID string + Order Order Version string } @@ -260,6 +260,11 @@ func (o Order) Validate() error { return chantypes.ErrInvalidChannelOrdering } +// CreateConnectionOptions contains the configuration for creating a connection. +type CreateConnectionOptions struct { + ChainAID, ChainBID string +} + // CreateClientOptions contains the configuration for creating a client. type CreateClientOptions struct { TrustingPeriod string diff --git a/interchain.go b/interchain.go index e49b5cc97..f404e740a 100644 --- a/interchain.go +++ b/interchain.go @@ -307,7 +307,8 @@ func (ic *Interchain) Build(ctx context.Context, rep *testreporter.RelayerExecRe return err } - if err := rp.Relayer.LinkPath(ctx, rep, rp.Path, link.createChannelOpts, link.createClientOpts); err != nil { + // TODO: fix create connection options. + if err := rp.Relayer.LinkPath(ctx, rep, rp.Path, link.createChannelOpts, link.createClientOpts, ibc.CreateConnectionOptions{}); err != nil { return fmt.Errorf( "failed to link path %s on relayer %s between chains %s and %s: %w", rp.Path, rp.Relayer, ic.chains[c0], ic.chains[c1], err, diff --git a/relayer/docker.go b/relayer/docker.go index 5217d71b2..c13c55d78 100644 --- a/relayer/docker.go +++ b/relayer/docker.go @@ -188,8 +188,8 @@ func (r *DockerRelayer) CreateClients(ctx context.Context, rep ibc.RelayerExecRe return res.Err } -func (r *DockerRelayer) CreateConnections(ctx context.Context, rep ibc.RelayerExecReporter, pathName string) error { - cmd := r.c.CreateConnections(pathName, r.HomeDir()) +func (r *DockerRelayer) CreateConnections(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, opts ibc.CreateConnectionOptions) error { + cmd := r.c.CreateConnections(pathName, opts, r.HomeDir()) res := r.Exec(ctx, rep, cmd, nil) return res.Err } @@ -253,8 +253,8 @@ func (r *DockerRelayer) GetClients(ctx context.Context, rep ibc.RelayerExecRepor return r.c.ParseGetClientsOutput(string(res.Stdout), string(res.Stderr)) } -func (r *DockerRelayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) error { - cmd := r.c.LinkPath(pathName, r.HomeDir(), channelOpts, clientOpts) +func (r *DockerRelayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) error { + cmd := r.c.LinkPath(pathName, r.HomeDir(), channelOpts, clientOpts, connectionOpts) res := r.Exec(ctx, rep, cmd, nil) return res.Err } @@ -523,7 +523,7 @@ type RelayerCommander interface { AddKey(chainID, keyName, coinType, homeDir string) []string CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string - CreateConnections(pathName, homeDir string) []string + CreateConnections(pathName string, opts ibc.CreateConnectionOptions, homeDir string) []string FlushAcknowledgements(pathName, channelID, homeDir string) []string FlushPackets(pathName, channelID, homeDir string) []string GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string @@ -531,7 +531,7 @@ type RelayerCommander interface { GetChannels(chainID, homeDir string) []string GetConnections(chainID, homeDir string) []string GetClients(chainID, homeDir string) []string - LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string + LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) []string RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string StartRelayer(homeDir string, pathNames ...string) []string UpdateClients(pathName, homeDir string) []string diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index 209ac14e9..ea4713296 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -31,12 +31,12 @@ func (*Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecRe return nil } -func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) error { +func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) error { if err := r.CreateClients(ctx, rep, pathName, clientOpts); err != nil { return err } - if err := r.CreateConnections(ctx, rep, pathName); err != nil { + if err := r.CreateConnections(ctx, rep, pathName, connectionOpts); err != nil { return err } @@ -111,80 +111,155 @@ func (c commander) Init(homeDir string) []string { } func (c commander) AddChainConfiguration(containerFilePath, homeDir string) []string { - //TODO implement me - panic("implement me") + return nil } func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { // hermes keys add --chain foo --key-file - panic("implement me") + return nil } func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { - - // hermes create channel [OPTIONS] --a-chain --a-connection --a-port --b-port - // return []string{name, "create", "channel", "--a-chain", "CHAIN_ID", "--a-connection", "", "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName} - return []string{name, "create", "channel", "--a-chain", "?????", "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--new-client-connection"} - + // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) + return []string{name, "create", "channel", "--a-chain", opts.ChainAID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir} } func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { - //TODO implement me - panic("implement me") + // hermes create client [OPTIONS] --host-chain --reference-chain + return nil } -func (c commander) CreateConnections(pathName, homeDir string) []string { - //TODO implement me - panic("implement me") +func (c commander) CreateConnections(_ string, connectionOptions ibc.CreateConnectionOptions, homeDir string) []string { + //DESCRIPTION: + // Create a new connection between two chains + // + //USAGE: + // hermes create connection [OPTIONS] --a-chain --b-chain + // + // hermes create connection [OPTIONS] --a-chain --a-client --b-client + // + // OPTIONS: + // --delay Delay period parameter for the new connection (seconds) [default: 0] + // -h, --help Print help information + // + // FLAGS: + // --a-chain Identifier of the side `a` chain for the new connection + // --a-client Identifier of client hosted on chain `a`; default: None (creates + // a new client) + // --b-chain Identifier of the side `b` chain for the new connection + // --b-client Identifier of client hosted on chain `b`; default: None (creates + // a new client) + + //hermes create connection [OPTIONS] --a-chain --b-chain + //hermes create connection [OPTIONS] --a-chain --a-client --b-client + return []string{name, "create", "connection", "--a-chain", connectionOptions.ChainAID, "--b-chain", connectionOptions.ChainBID, "--home", homeDir} } func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { - //TODO implement me - panic("implement me") + return nil } func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { - //TODO implement me - panic("implement me") -} - -func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { - //TODO implement me - panic("implement me") -} - -func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { - panic("implement me") + return nil } func (c commander) GetChannels(chainID, homeDir string) []string { + //DESCRIPTION: + // Query the identifiers of all channels on a given chain + // + //USAGE: + // hermes query channels [OPTIONS] --chain + // + // OPTIONS: + // --counterparty-chain + // Filter the query response by the this counterparty chain + // + // -h, --help + // Print help information + // + // --show-counterparty + // Show the counterparty chain, port, and channel + // + // --verbose + // Enable verbose output, displaying the client and connection ids for each channel in the + // response + // + //REQUIRED: + // --chain Identifier of the chain to query return []string{name, "query", "channels", "--home", homeDir, "--chain", chainID} } func (c commander) GetConnections(chainID, homeDir string) []string { - panic("implement me") + //DESCRIPTION: + // Query the identifiers of all connections on a chain + // + //USAGE: + // hermes query connections [OPTIONS] --chain + // + // OPTIONS: + // --counterparty-chain + // Filter the query response by the counterparty chain + // + // -h, --help + // Print help information + // + // --verbose + // Enable verbose output, displaying the client for each connection in the response + // + //REQUIRED: + // --chain Identifier of the chain to query + return []string{name, "query", "connections", "--chain", chainID, "--home", homeDir} } func (c commander) GetClients(chainID, homeDir string) []string { - panic("implement me") -} - -func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { - panic("implement me") + //DESCRIPTION: + // Query the identifiers of all clients on a chain + // + //USAGE: + // hermes query clients [OPTIONS] --host-chain + // + // OPTIONS: + // -h, --help + // Print help information + // + // --omit-chain-ids + // Omit printing the reference (or target) chain for each client + // + // --reference-chain + // Filter for clients which target a specific chain id (implies '--omit-chain-ids') + // + //REQUIRED: + // --host-chain Identifier of the chain to query + return []string{name, "query", "clients", "--host-chain", chainID, "--home", homeDir} } func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { - panic("implement me") + return nil } func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { - return []string{"hermes", "start", "--full-scan", "--home", homeDir} + return []string{name, "start", "--full-scan", "--home", homeDir} } func (c commander) UpdateClients(pathName, homeDir string) []string { - panic("implement me") + return nil } func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { return NewWallet(keyName, address, mnemonic) } + +// Not in Hermes +func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { + return nil +} + +// Not in Hermes +func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { + return nil +} + +// Not in Hermes +func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) []string { + return nil +} diff --git a/relayer/rly/cosmos_relayer.go b/relayer/rly/cosmos_relayer.go index 643e60ac8..9dfd6d384 100644 --- a/relayer/rly/cosmos_relayer.go +++ b/relayer/rly/cosmos_relayer.go @@ -155,7 +155,7 @@ func (commander) CreateClient(pathName, homeDir, customeClientTrustingPeriod str } } -func (commander) CreateConnections(pathName, homeDir string) []string { +func (commander) CreateConnections(pathName string, opts ibc.CreateConnectionOptions, homeDir string) []string { return []string{ "rly", "tx", "connection", pathName, "--home", homeDir, @@ -213,7 +213,7 @@ func (commander) GetClients(chainID, homeDir string) []string { } } -func (commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpt ibc.CreateClientOptions) []string { +func (commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpt ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) []string { return []string{ "rly", "tx", "link", pathName, "--src-port", channelOpts.SourcePortName, From a35487e1aa9208681f3db209976b34cc8ec39a53 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 9 Feb 2023 12:32:01 +0000 Subject: [PATCH 03/32] chore: wip --- relayer/hermes/hermes_relayer.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index ea4713296..b5e5e089b 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -9,7 +9,7 @@ import ( ) const ( - name = "hermes" + hermes = "hermes" defaultContainerImage = "foo" defaultContainerVersion = "v1.0.0" @@ -47,11 +47,6 @@ func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pat return nil } -func (r *Relayer) GeneratePath(ctx context.Context, rep ibc.RelayerExecReporter, srcChainID, dstChainID, pathName string) error { - // generate path gets called in interchain.Build. Hermes doesn't have this concept so something will need to be changed here. - return nil -} - var _ relayer.RelayerCommander = &commander{} type commander struct { @@ -59,7 +54,7 @@ type commander struct { } func (c commander) Name() string { - return name + return hermes } func (c commander) DefaultContainerImage() string { @@ -105,7 +100,7 @@ func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutpu func (c commander) Init(homeDir string) []string { return []string{ - name, "config", "init", + hermes, "config", "init", "--home", homeDir, } } @@ -121,7 +116,7 @@ func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) - return []string{name, "create", "channel", "--a-chain", opts.ChainAID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir} + return []string{hermes, "create", "channel", "--a-chain", opts.ChainAID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir} } func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { @@ -152,7 +147,7 @@ func (c commander) CreateConnections(_ string, connectionOptions ibc.CreateConne //hermes create connection [OPTIONS] --a-chain --b-chain //hermes create connection [OPTIONS] --a-chain --a-client --b-client - return []string{name, "create", "connection", "--a-chain", connectionOptions.ChainAID, "--b-chain", connectionOptions.ChainBID, "--home", homeDir} + return []string{hermes, "create", "connection", "--a-chain", connectionOptions.ChainAID, "--b-chain", connectionOptions.ChainBID, "--home", homeDir} } func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { @@ -186,7 +181,7 @@ func (c commander) GetChannels(chainID, homeDir string) []string { // //REQUIRED: // --chain Identifier of the chain to query - return []string{name, "query", "channels", "--home", homeDir, "--chain", chainID} + return []string{hermes, "query", "channels", "--home", homeDir, "--chain", chainID} } func (c commander) GetConnections(chainID, homeDir string) []string { @@ -208,7 +203,7 @@ func (c commander) GetConnections(chainID, homeDir string) []string { // //REQUIRED: // --chain Identifier of the chain to query - return []string{name, "query", "connections", "--chain", chainID, "--home", homeDir} + return []string{hermes, "query", "connections", "--chain", chainID, "--home", homeDir} } func (c commander) GetClients(chainID, homeDir string) []string { @@ -230,7 +225,7 @@ func (c commander) GetClients(chainID, homeDir string) []string { // //REQUIRED: // --host-chain Identifier of the chain to query - return []string{name, "query", "clients", "--host-chain", chainID, "--home", homeDir} + return []string{hermes, "query", "clients", "--host-chain", chainID, "--home", homeDir} } func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { @@ -238,7 +233,7 @@ func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir stri } func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { - return []string{name, "start", "--full-scan", "--home", homeDir} + return []string{hermes, "start", "--full-scan", "--home", homeDir} } func (c commander) UpdateClients(pathName, homeDir string) []string { @@ -249,6 +244,12 @@ func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { return NewWallet(keyName, address, mnemonic) } +// Not in Hermes +func (r *Relayer) GeneratePath(ctx context.Context, rep ibc.RelayerExecReporter, srcChainID, dstChainID, pathName string) error { + // generate path gets called in interchain.Build. Hermes doesn't have this concept so something will need to be changed here. + return nil +} + // Not in Hermes func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { return nil From a18bf660068cab3bea1bdd7b88d9bc64d6e3b580 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 10 Feb 2023 12:46:33 +0000 Subject: [PATCH 04/32] wip: adding path map to hermes relayer type --- .gitignore | 1 + conformance/relayersetup.go | 2 +- ibc/relayer.go | 4 +- interchain.go | 2 +- relayer/docker.go | 12 +-- relayer/hermes/hermes_relayer.go | 123 ++++++++++++++++++++++++++----- relayer/rly/cosmos_relayer.go | 4 +- 7 files changed, 116 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 6c86de8c5..d39e39ff1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ interchaintest.test .idea /bin +vendor diff --git a/conformance/relayersetup.go b/conformance/relayersetup.go index e2b7b6743..a7b081afa 100644 --- a/conformance/relayersetup.go +++ b/conformance/relayersetup.go @@ -93,7 +93,7 @@ func TestRelayerSetup(t *testing.T, ctx context.Context, cf interchaintest.Chain req := require.New(rep.TestifyT(t)) eRep := rep.RelayerExecReporter(t) - req.NoError(r.CreateConnections(ctx, eRep, pathName, ibc.CreateConnectionOptions{})) + req.NoError(r.CreateConnections(ctx, eRep, pathName)) // Assert against the singly created connections individually. conns0, err := r.GetConnections(ctx, eRep, c0.Config().ChainID) diff --git a/ibc/relayer.go b/ibc/relayer.go index bf467786c..5687de859 100644 --- a/ibc/relayer.go +++ b/ibc/relayer.go @@ -39,7 +39,7 @@ type Relayer interface { GeneratePath(ctx context.Context, rep RelayerExecReporter, srcChainID, dstChainID, pathName string) error // setup channels, connections, and clients - LinkPath(ctx context.Context, rep RelayerExecReporter, pathName string, channelOpts CreateChannelOptions, clientOptions CreateClientOptions, connectionOpts CreateConnectionOptions) error + LinkPath(ctx context.Context, rep RelayerExecReporter, pathName string, channelOpts CreateChannelOptions, clientOptions CreateClientOptions) error // update path channel filter UpdatePath(ctx context.Context, rep RelayerExecReporter, pathName string, filter ChannelFilter) error @@ -76,7 +76,7 @@ type Relayer interface { // CreateConnections performs the connection handshake steps necessary for creating a connection // between the src and dst chains. - CreateConnections(ctx context.Context, rep RelayerExecReporter, pathName string, opts CreateConnectionOptions) error + CreateConnections(ctx context.Context, rep RelayerExecReporter, pathName string) error // CreateChannel creates a channel on the given path with the provided options. CreateChannel(ctx context.Context, rep RelayerExecReporter, pathName string, opts CreateChannelOptions) error diff --git a/interchain.go b/interchain.go index f404e740a..741050106 100644 --- a/interchain.go +++ b/interchain.go @@ -308,7 +308,7 @@ func (ic *Interchain) Build(ctx context.Context, rep *testreporter.RelayerExecRe } // TODO: fix create connection options. - if err := rp.Relayer.LinkPath(ctx, rep, rp.Path, link.createChannelOpts, link.createClientOpts, ibc.CreateConnectionOptions{}); err != nil { + if err := rp.Relayer.LinkPath(ctx, rep, rp.Path, link.createChannelOpts, link.createClientOpts); err != nil { return fmt.Errorf( "failed to link path %s on relayer %s between chains %s and %s: %w", rp.Path, rp.Relayer, ic.chains[c0], ic.chains[c1], err, diff --git a/relayer/docker.go b/relayer/docker.go index c13c55d78..ea0522a22 100644 --- a/relayer/docker.go +++ b/relayer/docker.go @@ -188,8 +188,8 @@ func (r *DockerRelayer) CreateClients(ctx context.Context, rep ibc.RelayerExecRe return res.Err } -func (r *DockerRelayer) CreateConnections(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, opts ibc.CreateConnectionOptions) error { - cmd := r.c.CreateConnections(pathName, opts, r.HomeDir()) +func (r *DockerRelayer) CreateConnections(ctx context.Context, rep ibc.RelayerExecReporter, pathName string) error { + cmd := r.c.CreateConnections(pathName, r.HomeDir()) res := r.Exec(ctx, rep, cmd, nil) return res.Err } @@ -253,8 +253,8 @@ func (r *DockerRelayer) GetClients(ctx context.Context, rep ibc.RelayerExecRepor return r.c.ParseGetClientsOutput(string(res.Stdout), string(res.Stderr)) } -func (r *DockerRelayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) error { - cmd := r.c.LinkPath(pathName, r.HomeDir(), channelOpts, clientOpts, connectionOpts) +func (r *DockerRelayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) error { + cmd := r.c.LinkPath(pathName, r.HomeDir(), channelOpts, clientOpts) res := r.Exec(ctx, rep, cmd, nil) return res.Err } @@ -523,7 +523,7 @@ type RelayerCommander interface { AddKey(chainID, keyName, coinType, homeDir string) []string CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string - CreateConnections(pathName string, opts ibc.CreateConnectionOptions, homeDir string) []string + CreateConnections(pathName string, homeDir string) []string FlushAcknowledgements(pathName, channelID, homeDir string) []string FlushPackets(pathName, channelID, homeDir string) []string GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string @@ -531,7 +531,7 @@ type RelayerCommander interface { GetChannels(chainID, homeDir string) []string GetConnections(chainID, homeDir string) []string GetClients(chainID, homeDir string) []string - LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) []string + LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string StartRelayer(homeDir string, pathNames ...string) []string UpdateClients(pathName, homeDir string) []string diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index b5e5e089b..20bdcf737 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -2,6 +2,7 @@ package hermes import ( "context" + "fmt" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" @@ -22,6 +23,16 @@ var _ ibc.Relayer = &Relayer{} // Relayer is the ibc.Relayer implementation for hermes. type Relayer struct { *relayer.DockerRelayer + paths map[string]*pathConfiguration +} + +type pathConfiguration struct { + chainA, chainB pathChainConfig +} + +type pathChainConfig struct { + chainID string + clientID string } func (*Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { @@ -31,12 +42,17 @@ func (*Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecRe return nil } -func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pathName string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) error { - if err := r.CreateClients(ctx, rep, pathName, clientOpts); err != nil { - return err +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.CreateConnections(ctx, rep, pathName, connectionOpts); err != nil { + //if err := r.CreateClients(ctx, rep, pathName, clientOpts); err != nil { + // return err + //} + + if err := r.CreateConnections(ctx, rep, pathName); err != nil { return err } @@ -47,10 +63,45 @@ func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pat return nil } +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, "--home", r.HomeDir()} + 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, "--home", r.HomeDir()} + return r.Exec(ctx, rep, updateChainBCmd, nil).Err +} + +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 + } + + // TODO: parse res and update pathConfig? + + 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 + } + // TODO: parse res and update pathConfig? + + return res.Err +} + var _ relayer.RelayerCommander = &commander{} type commander struct { - log *zap.Logger + log *zap.Logger + paths map[string]pathConfiguration } func (c commander) Name() string { @@ -116,15 +167,17 @@ func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) - return []string{hermes, "create", "channel", "--a-chain", opts.ChainAID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir} + return []string{hermes, "--json", "create", "channel", "--a-chain", opts.ChainAID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir} } func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { + pathConfig := c.paths[pathName] + // hermes create client [OPTIONS] --host-chain --reference-chain - return nil + return []string{hermes, "--json", "create", "client", "--host-chain", pathConfig.chainA.chainID, "--reference-chain", ""} } -func (c commander) CreateConnections(_ string, connectionOptions ibc.CreateConnectionOptions, homeDir string) []string { +func (c commander) CreateConnections(pathName string, homeDir string) []string { //DESCRIPTION: // Create a new connection between two chains // @@ -145,9 +198,8 @@ func (c commander) CreateConnections(_ string, connectionOptions ibc.CreateConne // --b-client Identifier of client hosted on chain `b`; default: None (creates // a new client) - //hermes create connection [OPTIONS] --a-chain --b-chain - //hermes create connection [OPTIONS] --a-chain --a-client --b-client - return []string{hermes, "create", "connection", "--a-chain", connectionOptions.ChainAID, "--b-chain", connectionOptions.ChainBID, "--home", homeDir} + pathConfig := c.paths[pathName] + return []string{hermes, "--json", "create", "connection", "--a-chain", pathConfig.chainA.chainID, "--b-chain", pathConfig.chainB.chainID, "--home", homeDir} } func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { @@ -181,7 +233,7 @@ func (c commander) GetChannels(chainID, homeDir string) []string { // //REQUIRED: // --chain Identifier of the chain to query - return []string{hermes, "query", "channels", "--home", homeDir, "--chain", chainID} + return []string{hermes, "--json", "query", "channels", "--home", homeDir, "--chain", chainID} } func (c commander) GetConnections(chainID, homeDir string) []string { @@ -203,7 +255,7 @@ func (c commander) GetConnections(chainID, homeDir string) []string { // //REQUIRED: // --chain Identifier of the chain to query - return []string{hermes, "query", "connections", "--chain", chainID, "--home", homeDir} + return []string{hermes, "--json", "query", "connections", "--chain", chainID, "--home", homeDir} } func (c commander) GetClients(chainID, homeDir string) []string { @@ -225,7 +277,7 @@ func (c commander) GetClients(chainID, homeDir string) []string { // //REQUIRED: // --host-chain Identifier of the chain to query - return []string{hermes, "query", "clients", "--host-chain", chainID, "--home", homeDir} + return []string{hermes, "--json", "query", "clients", "--host-chain", chainID, "--home", homeDir} } func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { @@ -233,10 +285,29 @@ func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir stri } func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { - return []string{hermes, "start", "--full-scan", "--home", homeDir} + return []string{hermes, "--json", "start", "--full-scan", "--home", homeDir} } func (c commander) UpdateClients(pathName, homeDir string) []string { + //DESCRIPTION: + // Update an IBC client + // + //USAGE: + // hermes update client [OPTIONS] --host-chain --client + // + // OPTIONS: + // -h, --help + // Print help information + // + // --height + // The target height of the client update. Leave unspecified for latest height. + // + // --trusted-height + // The trusted height of the client update. Leave unspecified for latest height. + // + // REQUIRED: + // --client Identifier of the chain targeted by the client + // --host-chain Identifier of the chain that hosts the client return nil } @@ -246,21 +317,33 @@ func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { // Not in Hermes func (r *Relayer) GeneratePath(ctx context.Context, rep ibc.RelayerExecReporter, srcChainID, dstChainID, pathName string) error { - // generate path gets called in interchain.Build. Hermes doesn't have this concept so something will need to be changed here. + if r.paths == nil { + r.paths = map[string]*pathConfiguration{} + } + r.paths[pathName] = &pathConfiguration{ + chainA: pathChainConfig{ + chainID: srcChainID, + clientID: "", + }, + chainB: pathChainConfig{ + chainID: dstChainID, + clientID: "", + }, + } return nil } // Not in Hermes func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { - return nil + panic("path does not exist in hermes") } // Not in Hermes func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { - return nil + panic("path does not exist in hermes") } // Not in Hermes -func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) []string { - return nil +func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { + panic("path does not exist in hermes") } diff --git a/relayer/rly/cosmos_relayer.go b/relayer/rly/cosmos_relayer.go index 9dfd6d384..454a33fcd 100644 --- a/relayer/rly/cosmos_relayer.go +++ b/relayer/rly/cosmos_relayer.go @@ -155,7 +155,7 @@ func (commander) CreateClient(pathName, homeDir, customeClientTrustingPeriod str } } -func (commander) CreateConnections(pathName string, opts ibc.CreateConnectionOptions, homeDir string) []string { +func (commander) CreateConnections(pathName string, homeDir string) []string { return []string{ "rly", "tx", "connection", pathName, "--home", homeDir, @@ -213,7 +213,7 @@ func (commander) GetClients(chainID, homeDir string) []string { } } -func (commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpt ibc.CreateClientOptions, connectionOpts ibc.CreateConnectionOptions) []string { +func (commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpt ibc.CreateClientOptions) []string { return []string{ "rly", "tx", "link", pathName, "--src-port", channelOpts.SourcePortName, From 19930b063a1d4ac3a68d42949e345344e3e2cf30 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 10 Feb 2023 13:24:47 +0000 Subject: [PATCH 05/32] writing mnemonic file --- ibc/relayer.go | 7 +- relayer/docker.go | 16 +++++ relayer/hermes/hermes_relayer.go | 107 +++++++++++++++++++++++++++++-- 3 files changed, 119 insertions(+), 11 deletions(-) diff --git a/ibc/relayer.go b/ibc/relayer.go index 5687de859..84a763c05 100644 --- a/ibc/relayer.go +++ b/ibc/relayer.go @@ -198,10 +198,9 @@ type RelayerExecResult struct { // CreateChannelOptions contains the configuration for creating a channel. type CreateChannelOptions struct { - SourcePortName string - DestPortName string - ChainAID, ChainBID string - Order Order + SourcePortName string + DestPortName string + Order Order Version string } diff --git a/relayer/docker.go b/relayer/docker.go index ea0522a22..533951cf4 100644 --- a/relayer/docker.go +++ b/relayer/docker.go @@ -122,6 +122,22 @@ func NewDockerRelayer(ctx context.Context, log *zap.Logger, testName string, cli return &r, nil } +func (r *DockerRelayer) Logger() *zap.Logger { + return r.log +} + +func (r *DockerRelayer) TestName() string { + return r.testName +} + +func (r *DockerRelayer) VolumeName() string { + return r.volumeName +} + +func (r *DockerRelayer) Client() *client.Client { + return r.client +} + 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. diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index 20bdcf737..da91def60 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -3,8 +3,11 @@ package hermes import ( "context" "fmt" + "time" + "github.com/docker/docker/client" "github.com/strangelove-ventures/interchaintest/v6/ibc" + "github.com/strangelove-ventures/interchaintest/v6/internal/dockerutil" "github.com/strangelove-ventures/interchaintest/v6/relayer" "go.uber.org/zap" ) @@ -35,6 +38,31 @@ type pathChainConfig struct { clientID string } +func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, networkID string, options ...relayer.RelayerOption) (*Relayer, error) { + c := commander{log: log} + //for _, opt := range options { + // switch o := opt.(type) { + // case relayer.RelayerOptionExtraStartFlags: + //c.extraStartFlags = o.Flags + //} + //} + dr, err := relayer.NewDockerRelayer(context.TODO(), log, testName, cli, networkID, c, options...) + if err != nil { + return nil, err + } + + return &Relayer{ + DockerRelayer: dr, + }, nil +} + +func (r *Relayer) populatePathConfig(pathName string) error { + //, ok := r.paths[pathName] + + // Query things + return nil +} + func (*Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { // We need a new implementation of AddChainConfiguration because the go relayer supports writing multiple chain config files // but hermes has them all in a single toml file. This function will get called once per chain and so will need to build up state somewhere and write @@ -52,15 +80,15 @@ func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pat // return err //} - if err := r.CreateConnections(ctx, rep, pathName); 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 + return r.populatePathConfig(pathName) } func (r *Relayer) UpdateClients(ctx context.Context, rep ibc.RelayerExecReporter, pathName string) error { @@ -97,6 +125,51 @@ func (r *Relayer) CreateClients(ctx context.Context, rep ibc.RelayerExecReporter return res.Err } +func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, chainID, keyName, coinType, mnemonic string) error { + //DESCRIPTION: + // Adds key to a configured chain or restores a key to a configured chain using a mnemonic + // + //USAGE: + // hermes keys add [OPTIONS] --chain --key-file + // + // hermes keys add [OPTIONS] --chain --mnemonic-file + // + // OPTIONS: + // -h, --help Print help information + // --hd-path Derivation path for this key [default: m/44'/118'/0'/0/0] + // --key-name Name of the key (defaults to the `key_name` defined in the config) + // --overwrite Overwrite the key if there is already one with the same key name + // + // FLAGS: + // --chain Identifier of the chain + // --key-file Path to the key file + // --mnemonic-file Path to file containing mnemonic to restore the key from + + relativeMnemonicFilePath := "mnemonic.txt" + fw := dockerutil.NewFileWriter(r.Logger(), r.Client(), r.TestName()) + if err := fw.WriteFile(ctx, r.VolumeName(), relativeMnemonicFilePath, []byte(mnemonic)); err != nil { + return fmt.Errorf("failed to write mnemoic file: %w", err) + } + + cmd := []string{hermes, "keys", "add", "--chain", chainID, "--hd-path", coinType, "--mnemonic-file", fmt.Sprintf("/mnt/dockervolume/%s", relativeMnemonicFilePath)} + + // 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 := commander{}.ParseRestoreKeyOutput(string(res.Stdout), string(res.Stderr)) + + r.wallets[chainID] = commander{}.CreateWallet("", addrBytes, mnemonic) + + return nil +} + var _ relayer.RelayerCommander = &commander{} type commander struct { @@ -166,8 +239,9 @@ func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { } func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { + pathConfig := c.paths[pathName] // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) - return []string{hermes, "--json", "create", "channel", "--a-chain", opts.ChainAID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir} + return []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir, "--new-client-connection"} } func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { @@ -281,11 +355,30 @@ func (c commander) GetClients(chainID, homeDir string) []string { } func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { - return nil + //DESCRIPTION: + // Adds key to a configured chain or restores a key to a configured chain using a mnemonic + // + //USAGE: + // hermes keys add [OPTIONS] --chain --key-file + // + // hermes keys add [OPTIONS] --chain --mnemonic-file + // + // OPTIONS: + // -h, --help Print help information + // --hd-path Derivation path for this key [default: m/44'/118'/0'/0/0] + // --key-name Name of the key (defaults to the `key_name` defined in the config) + // --overwrite Overwrite the key if there is already one with the same key name + // + // FLAGS: + // --chain Identifier of the chain + // --key-file Path to the key file + // --mnemonic-file Path to file containing mnemonic to restore the key from + + return []string{hermes, "keys", "add", "--chain", chainID, "--hd-path", coinType} } func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { - return []string{hermes, "--json", "start", "--full-scan", "--home", homeDir} + return []string{hermes, "start", "--full-scan", "--home", homeDir} } func (c commander) UpdateClients(pathName, homeDir string) []string { From e78de86ecea474d53d2ff6bb78d3d949947b8e75 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 13 Feb 2023 11:29:27 +0000 Subject: [PATCH 06/32] correctly reading toml config --- interchain_test.go | 2 +- relayer/docker.go | 20 +- relayer/hermes/hermes_commander.go | 283 +++++++++++++++ relayer/hermes/hermes_config.go | 246 ++++++++++++++ relayer/hermes/hermes_relayer.go | 321 ++---------------- .../hermes/{wallet.go => hermes_wallet.go} | 0 relayer/options.go | 10 + relayerfactory.go | 5 + 8 files changed, 594 insertions(+), 293 deletions(-) create mode 100644 relayer/hermes/hermes_commander.go create mode 100644 relayer/hermes/hermes_config.go rename relayer/hermes/{wallet.go => hermes_wallet.go} (100%) diff --git a/interchain_test.go b/interchain_test.go index 28720037a..c30879204 100644 --- a/interchain_test.go +++ b/interchain_test.go @@ -46,7 +46,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(ibc.Hermes, zaptest.NewLogger(t)).Build( t, client, network, ) diff --git a/relayer/docker.go b/relayer/docker.go index 533951cf4..8594f777a 100644 --- a/relayer/docker.go +++ b/relayer/docker.go @@ -40,8 +40,10 @@ type DockerRelayer struct { // The ID of the container created by StartRelayer. containerID string - // wallets contains a mapping of chainID to relayer wallet - wallets map[string]ibc.Wallet + // Wallets contains a mapping of chainID to relayer wallet + Wallets map[string]ibc.Wallet + + homeDir string } var _ ibc.Relayer = (*DockerRelayer)(nil) @@ -61,15 +63,19 @@ func NewDockerRelayer(ctx context.Context, log *zap.Logger, testName string, cli testName: testName, - wallets: map[string]ibc.Wallet{}, + Wallets: map[string]ibc.Wallet{}, } + r.homeDir = "/home/relayer" + 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 } } @@ -183,12 +189,12 @@ func (r *DockerRelayer) AddKey(ctx context.Context, rep ibc.RelayerExecReporter, if err != nil { return nil, err } - r.wallets[chainID] = wallet + r.Wallets[chainID] = wallet return wallet, nil } func (r *DockerRelayer) GetWallet(chainID string) (ibc.Wallet, bool) { - wallet, ok := r.wallets[chainID] + wallet, ok := r.Wallets[chainID] return wallet, ok } @@ -319,7 +325,7 @@ func (r *DockerRelayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecRepor addrBytes := r.c.ParseRestoreKeyOutput(string(res.Stdout), string(res.Stderr)) - r.wallets[chainID] = r.c.CreateWallet("", addrBytes, mnemonic) + r.Wallets[chainID] = r.c.CreateWallet("", addrBytes, mnemonic) return nil } @@ -485,7 +491,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..8275d7363 --- /dev/null +++ b/relayer/hermes/hermes_commander.go @@ -0,0 +1,283 @@ +package hermes + +import ( + "context" + + "github.com/pelletier/go-toml" + "github.com/strangelove-ventures/interchaintest/v6/ibc" + "github.com/strangelove-ventures/interchaintest/v6/relayer" + "go.uber.org/zap" +) + +var _ relayer.RelayerCommander = &commander{} + +type commander struct { + log *zap.Logger + paths map[string]pathConfiguration +} + +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) ConfigContent(ctx context.Context, cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { + hermesConfig := NewConfig(keyName, rpcAddr, grpcAddr, cfg) + bz, err := toml.Marshal(hermesConfig) + if err != nil { + return nil, err + } + return bz, nil +} + +func (c commander) ParseAddKeyOutput(stdout, stderr string) (ibc.Wallet, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseRestoreKeyOutput(stdout, stderr string) string { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { + //TODO implement me + panic("implement me") +} + +func (c commander) Init(homeDir string) []string { + return nil +} + +func (c commander) AddChainConfiguration(containerFilePath, homeDir string) []string { + return nil +} + +func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { + // hermes keys add --chain foo --key-file + return nil +} + +func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { + pathConfig := c.paths[pathName] + // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) + return []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir, "--new-client-connection"} +} + +func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { + pathConfig := c.paths[pathName] + + // hermes create client [OPTIONS] --host-chain --reference-chain + return []string{hermes, "--json", "create", "client", "--host-chain", pathConfig.chainA.chainID, "--reference-chain", ""} +} + +func (c commander) CreateConnections(pathName string, homeDir string) []string { + //DESCRIPTION: + // Create a new connection between two chains + // + //USAGE: + // hermes create connection [OPTIONS] --a-chain --b-chain + // + // hermes create connection [OPTIONS] --a-chain --a-client --b-client + // + // OPTIONS: + // --delay Delay period parameter for the new connection (seconds) [default: 0] + // -h, --help Print help information + // + // FLAGS: + // --a-chain Identifier of the side `a` chain for the new connection + // --a-client Identifier of client hosted on chain `a`; default: None (creates + // a new client) + // --b-chain Identifier of the side `b` chain for the new connection + // --b-client Identifier of client hosted on chain `b`; default: None (creates + // a new client) + + pathConfig := c.paths[pathName] + return []string{hermes, "--json", "create", "connection", "--a-chain", pathConfig.chainA.chainID, "--b-chain", pathConfig.chainB.chainID, "--home", homeDir} +} + +func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { + return nil +} + +func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { + return nil +} + +func (c commander) GetChannels(chainID, homeDir string) []string { + //DESCRIPTION: + // Query the identifiers of all channels on a given chain + // + //USAGE: + // hermes query channels [OPTIONS] --chain + // + // OPTIONS: + // --counterparty-chain + // Filter the query response by the this counterparty chain + // + // -h, --help + // Print help information + // + // --show-counterparty + // Show the counterparty chain, port, and channel + // + // --verbose + // Enable verbose output, displaying the client and connection ids for each channel in the + // response + // + //REQUIRED: + // --chain Identifier of the chain to query + return []string{hermes, "--json", "query", "channels", "--home", homeDir, "--chain", chainID} +} + +func (c commander) GetConnections(chainID, homeDir string) []string { + //DESCRIPTION: + // Query the identifiers of all connections on a chain + // + //USAGE: + // hermes query connections [OPTIONS] --chain + // + // OPTIONS: + // --counterparty-chain + // Filter the query response by the counterparty chain + // + // -h, --help + // Print help information + // + // --verbose + // Enable verbose output, displaying the client for each connection in the response + // + //REQUIRED: + // --chain Identifier of the chain to query + return []string{hermes, "--json", "query", "connections", "--chain", chainID, "--home", homeDir} +} + +func (c commander) GetClients(chainID, homeDir string) []string { + //DESCRIPTION: + // Query the identifiers of all clients on a chain + // + //USAGE: + // hermes query clients [OPTIONS] --host-chain + // + // OPTIONS: + // -h, --help + // Print help information + // + // --omit-chain-ids + // Omit printing the reference (or target) chain for each client + // + // --reference-chain + // Filter for clients which target a specific chain id (implies '--omit-chain-ids') + // + //REQUIRED: + // --host-chain Identifier of the chain to query + return []string{hermes, "--json", "query", "clients", "--host-chain", chainID, "--home", homeDir} +} + +func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { + //DESCRIPTION: + // Adds key to a configured chain or restores a key to a configured chain using a mnemonic + // + //USAGE: + // hermes keys add [OPTIONS] --chain --key-file + // + // hermes keys add [OPTIONS] --chain --mnemonic-file + // + // OPTIONS: + // -h, --help Print help information + // --hd-path Derivation path for this key [default: m/44'/118'/0'/0/0] + // --key-name Name of the key (defaults to the `key_name` defined in the config) + // --overwrite Overwrite the key if there is already one with the same key name + // + // FLAGS: + // --chain Identifier of the chain + // --key-file Path to the key file + // --mnemonic-file Path to file containing mnemonic to restore the key from + + return []string{hermes, "keys", "add", "--chain", chainID} +} + +func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { + return []string{hermes, "--json", "start", "--full-scan"} +} + +func (c commander) UpdateClients(pathName, homeDir string) []string { + //DESCRIPTION: + // Update an IBC client + // + //USAGE: + // hermes update client [OPTIONS] --host-chain --client + // + // OPTIONS: + // -h, --help + // Print help information + // + // --height + // The target height of the client update. Leave unspecified for latest height. + // + // --trusted-height + // The trusted height of the client update. Leave unspecified for latest height. + // + // REQUIRED: + // --client Identifier of the chain targeted by the client + // --host-chain Identifier of the chain that hosts the client + return nil +} + +func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { + return NewWallet(keyName, address, mnemonic) +} + +// Not in Hermes +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, + clientID: "", + }, + chainB: pathChainConfig{ + chainID: dstChainID, + clientID: "", + }, + } + return nil +} + +// Not in Hermes +func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { + panic("path does not exist in hermes") +} + +// Not in Hermes +func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { + panic("path does not exist in hermes") +} + +// Not in Hermes +func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { + panic("path does not exist in hermes") +} diff --git a/relayer/hermes/hermes_config.go b/relayer/hermes/hermes_config.go new file mode 100644 index 000000000..48a1f11bd --- /dev/null +++ b/relayer/hermes/hermes_config.go @@ -0,0 +1,246 @@ +package hermes + +import ( + "fmt" + "strings" + + "github.com/strangelove-ventures/interchaintest/v6/ibc" +) + +// https://github.com/informalsystems/hermes/blob/master/config.toml + +func NewConfig(keyName, rpcAddr, grpcAddr string, chainConfigs ...ibc.ChainConfig) Config { + var chains []Chain + for _, chainCfg := range chainConfigs { + chains = append(chains, Chain{ + ID: chainCfg.ChainID, + RPCAddr: rpcAddr, + GrpcAddr: fmt.Sprintf("http://%s", grpcAddr), + WebsocketAddr: strings.ReplaceAll(fmt.Sprintf("%s/websocket", rpcAddr), "http", "ws"), + RPCTimeout: "10s", + AccountPrefix: chainCfg.Bech32Prefix, + KeyName: keyName, + AddressType: AddressType{ + Derivation: "cosmos", + }, + StorePrefix: "ibc", + DefaultGas: 100000, + MaxGas: 400000, + GasPrice: GasPrice{ + Price: 0.001, + Denom: "stake", + }, + GasMultiplier: 1.1, + 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: false, + 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"` +// AutoRegisterCounterpartyPayee bool `toml:"auto_register_counterparty_payee"` +//} +// +//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"` +//} + +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 index da91def60..a58adf2fb 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -14,11 +14,12 @@ import ( const ( hermes = "hermes" - defaultContainerImage = "foo" - defaultContainerVersion = "v1.0.0" + defaultContainerImage = "docker.io/informalsystems/hermes" + DefaultContainerVersion = "1.0.0" // TODO: this was taken from RlyDefaultUidGid. Figure out what value should be used. - hermesDefaultUidGid = "100:1000" + hermesDefaultUidGid = "1000:1000" + hermesConfigPath = ".hermes/config.toml" ) var _ ibc.Relayer = &Relayer{} @@ -38,7 +39,7 @@ type pathChainConfig struct { clientID string } -func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, networkID string, options ...relayer.RelayerOption) (*Relayer, error) { +func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, networkID string, options ...relayer.RelayerOption) *Relayer { c := commander{log: log} //for _, opt := range options { // switch o := opt.(type) { @@ -46,14 +47,15 @@ func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, netw //c.extraStartFlags = o.Flags //} //} + options = append(options, relayer.RelayerHomeDir("/home/hermes")) dr, err := relayer.NewDockerRelayer(context.TODO(), log, testName, cli, networkID, c, options...) if err != nil { - return nil, err + panic(err) } return &Relayer{ DockerRelayer: dr, - }, nil + } } func (r *Relayer) populatePathConfig(pathName string) error { @@ -63,13 +65,32 @@ func (r *Relayer) populatePathConfig(pathName string) error { return nil } -func (*Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { - // We need a new implementation of AddChainConfiguration because the go relayer supports writing multiple chain config files - // but hermes has them all in a single toml file. This function will get called once per chain and so will need to build up state somewhere and write - // the final file at the end. +func (r *Relayer) validateConfig(ctx context.Context, rep ibc.RelayerExecReporter) error { + cmd := []string{hermes, "--config", fmt.Sprintf("%s/%s", r.HomeDir(), hermesConfigPath), "config", "validate"} + //cmd := []string{hermes, "config", "validate"} + res := r.Exec(ctx, rep, cmd, nil) + if res.Err != nil { + return res.Err + } return nil } +func (r *Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { + configContent, err := commander{}.ConfigContent(ctx, chainConfig, keyName, rpcAddr, grpcAddr) + if err != nil { + return fmt.Errorf("failed to generate config content: %w", err) + } + + r.Logger().Info(string(configContent)) + + fw := dockerutil.NewFileWriter(r.Logger(), r.Client(), r.TestName()) + if err := fw.WriteFile(ctx, r.VolumeName(), hermesConfigPath, configContent); err != nil { + return fmt.Errorf("failed to rly config: %w", err) + } + + return r.validateConfig(ctx, rep) +} + 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 { @@ -96,12 +117,12 @@ func (r *Relayer) UpdateClients(ctx context.Context, rep ibc.RelayerExecReporter 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, "--home", r.HomeDir()} + 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, "--home", r.HomeDir()} + updateChainBCmd := []string{hermes, "--json", "update", "client", "--host-chain", pathConfig.chainB.chainID, "--client", pathConfig.chainB.clientID} return r.Exec(ctx, rep, updateChainBCmd, nil).Err } @@ -151,7 +172,8 @@ func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, c return fmt.Errorf("failed to write mnemoic file: %w", err) } - cmd := []string{hermes, "keys", "add", "--chain", chainID, "--hd-path", coinType, "--mnemonic-file", fmt.Sprintf("/mnt/dockervolume/%s", relativeMnemonicFilePath)} + //cmd := []string{hermes, "--config", fmt.Sprintf("%s/%s", r.HomeDir(), hermesConfigPath), "keys", "add", "--chain", chainID, "--mnemonic-file", fmt.Sprintf("%s/%s", r.HomeDir(), relativeMnemonicFilePath)} + cmd := []string{hermes, "keys", "add", "--chain", chainID, "--mnemonic-file", fmt.Sprintf("%s/%s", r.HomeDir(), relativeMnemonicFilePath)} // Restoring a key should be near-instantaneous, so add a 1-minute timeout // to detect if Docker has hung. @@ -165,278 +187,7 @@ func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, c addrBytes := commander{}.ParseRestoreKeyOutput(string(res.Stdout), string(res.Stderr)) - r.wallets[chainID] = commander{}.CreateWallet("", addrBytes, mnemonic) + r.Wallets[chainID] = commander{}.CreateWallet("", addrBytes, mnemonic) return nil } - -var _ relayer.RelayerCommander = &commander{} - -type commander struct { - log *zap.Logger - paths map[string]pathConfiguration -} - -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) ConfigContent(ctx context.Context, cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { - return nil, nil -} - -func (c commander) ParseAddKeyOutput(stdout, stderr string) (ibc.Wallet, error) { - //TODO implement me - panic("implement me") -} - -func (c commander) ParseRestoreKeyOutput(stdout, stderr string) string { - //TODO implement me - panic("implement me") -} - -func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { - //TODO implement me - panic("implement me") -} - -func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { - //TODO implement me - panic("implement me") -} - -func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { - //TODO implement me - panic("implement me") -} - -func (c commander) Init(homeDir string) []string { - return []string{ - hermes, "config", "init", - "--home", homeDir, - } -} - -func (c commander) AddChainConfiguration(containerFilePath, homeDir string) []string { - return nil -} - -func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { - // hermes keys add --chain foo --key-file - return nil -} - -func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { - pathConfig := c.paths[pathName] - // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) - return []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir, "--new-client-connection"} -} - -func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { - pathConfig := c.paths[pathName] - - // hermes create client [OPTIONS] --host-chain --reference-chain - return []string{hermes, "--json", "create", "client", "--host-chain", pathConfig.chainA.chainID, "--reference-chain", ""} -} - -func (c commander) CreateConnections(pathName string, homeDir string) []string { - //DESCRIPTION: - // Create a new connection between two chains - // - //USAGE: - // hermes create connection [OPTIONS] --a-chain --b-chain - // - // hermes create connection [OPTIONS] --a-chain --a-client --b-client - // - // OPTIONS: - // --delay Delay period parameter for the new connection (seconds) [default: 0] - // -h, --help Print help information - // - // FLAGS: - // --a-chain Identifier of the side `a` chain for the new connection - // --a-client Identifier of client hosted on chain `a`; default: None (creates - // a new client) - // --b-chain Identifier of the side `b` chain for the new connection - // --b-client Identifier of client hosted on chain `b`; default: None (creates - // a new client) - - pathConfig := c.paths[pathName] - return []string{hermes, "--json", "create", "connection", "--a-chain", pathConfig.chainA.chainID, "--b-chain", pathConfig.chainB.chainID, "--home", homeDir} -} - -func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { - return nil -} - -func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { - return nil -} - -func (c commander) GetChannels(chainID, homeDir string) []string { - //DESCRIPTION: - // Query the identifiers of all channels on a given chain - // - //USAGE: - // hermes query channels [OPTIONS] --chain - // - // OPTIONS: - // --counterparty-chain - // Filter the query response by the this counterparty chain - // - // -h, --help - // Print help information - // - // --show-counterparty - // Show the counterparty chain, port, and channel - // - // --verbose - // Enable verbose output, displaying the client and connection ids for each channel in the - // response - // - //REQUIRED: - // --chain Identifier of the chain to query - return []string{hermes, "--json", "query", "channels", "--home", homeDir, "--chain", chainID} -} - -func (c commander) GetConnections(chainID, homeDir string) []string { - //DESCRIPTION: - // Query the identifiers of all connections on a chain - // - //USAGE: - // hermes query connections [OPTIONS] --chain - // - // OPTIONS: - // --counterparty-chain - // Filter the query response by the counterparty chain - // - // -h, --help - // Print help information - // - // --verbose - // Enable verbose output, displaying the client for each connection in the response - // - //REQUIRED: - // --chain Identifier of the chain to query - return []string{hermes, "--json", "query", "connections", "--chain", chainID, "--home", homeDir} -} - -func (c commander) GetClients(chainID, homeDir string) []string { - //DESCRIPTION: - // Query the identifiers of all clients on a chain - // - //USAGE: - // hermes query clients [OPTIONS] --host-chain - // - // OPTIONS: - // -h, --help - // Print help information - // - // --omit-chain-ids - // Omit printing the reference (or target) chain for each client - // - // --reference-chain - // Filter for clients which target a specific chain id (implies '--omit-chain-ids') - // - //REQUIRED: - // --host-chain Identifier of the chain to query - return []string{hermes, "--json", "query", "clients", "--host-chain", chainID, "--home", homeDir} -} - -func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { - //DESCRIPTION: - // Adds key to a configured chain or restores a key to a configured chain using a mnemonic - // - //USAGE: - // hermes keys add [OPTIONS] --chain --key-file - // - // hermes keys add [OPTIONS] --chain --mnemonic-file - // - // OPTIONS: - // -h, --help Print help information - // --hd-path Derivation path for this key [default: m/44'/118'/0'/0/0] - // --key-name Name of the key (defaults to the `key_name` defined in the config) - // --overwrite Overwrite the key if there is already one with the same key name - // - // FLAGS: - // --chain Identifier of the chain - // --key-file Path to the key file - // --mnemonic-file Path to file containing mnemonic to restore the key from - - return []string{hermes, "keys", "add", "--chain", chainID, "--hd-path", coinType} -} - -func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { - return []string{hermes, "start", "--full-scan", "--home", homeDir} -} - -func (c commander) UpdateClients(pathName, homeDir string) []string { - //DESCRIPTION: - // Update an IBC client - // - //USAGE: - // hermes update client [OPTIONS] --host-chain --client - // - // OPTIONS: - // -h, --help - // Print help information - // - // --height - // The target height of the client update. Leave unspecified for latest height. - // - // --trusted-height - // The trusted height of the client update. Leave unspecified for latest height. - // - // REQUIRED: - // --client Identifier of the chain targeted by the client - // --host-chain Identifier of the chain that hosts the client - return nil -} - -func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { - return NewWallet(keyName, address, mnemonic) -} - -// Not in Hermes -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, - clientID: "", - }, - chainB: pathChainConfig{ - chainID: dstChainID, - clientID: "", - }, - } - return nil -} - -// Not in Hermes -func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { - panic("path does not exist in hermes") -} - -// Not in Hermes -func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { - panic("path does not exist in hermes") -} - -// Not in Hermes -func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { - panic("path does not exist in hermes") -} diff --git a/relayer/hermes/wallet.go b/relayer/hermes/hermes_wallet.go similarity index 100% rename from relayer/hermes/wallet.go rename to relayer/hermes/hermes_wallet.go diff --git a/relayer/options.go b/relayer/options.go index 7ae065f56..e1c0cbc2e 100644 --- a/relayer/options.go +++ b/relayer/options.go @@ -16,6 +16,12 @@ type RelayerOptionDockerImage struct { DockerImage ibc.DockerImage } +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 +35,10 @@ func CustomDockerImage(repository string, version string, uidGid string) Relayer } } +func RelayerHomeDir(homeDir string) RelayerOption { + return RelayerOptionHomeDir{HomeDir: homeDir} +} + func (opt RelayerOptionDockerImage) relayerOption() {} type RelayerOptionImagePull struct { diff --git a/relayerfactory.go b/relayerfactory.go index 3a7a940e5..e0066b940 100644 --- a/relayerfactory.go +++ b/relayerfactory.go @@ -8,6 +8,7 @@ import ( "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/label" "github.com/strangelove-ventures/interchaintest/v6/relayer" + "github.com/strangelove-ventures/interchaintest/v6/relayer/hermes" "github.com/strangelove-ventures/interchaintest/v6/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,8 @@ func (f builtinRelayerFactory) Name() string { } } return "rly@" + rly.DefaultContainerVersion + case ibc.Hermes: + return "hermes@" + hermes.DefaultContainerVersion default: panic(fmt.Errorf("RelayerImplementation %v unknown", f.impl)) } From 3def8ebf824467ee36c519bf668860c365787b0d Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Mon, 13 Feb 2023 17:35:34 +0000 Subject: [PATCH 07/32] ibc test passing with hermes relayer --- examples/ibc/learn_ibc_test.go | 5 +- interchain_test.go | 2 +- internal/dockerutil/filewriter.go | 5 +- relayer/hermes/hermes_commander.go | 103 +++++++++++++++++------ relayer/hermes/hermes_config.go | 130 +++++++---------------------- relayer/hermes/hermes_relayer.go | 93 +++++++++++++++------ relayer/options.go | 2 +- 7 files changed, 183 insertions(+), 157 deletions(-) diff --git a/examples/ibc/learn_ibc_test.go b/examples/ibc/learn_ibc_test.go index 9f8287803..f95714162 100644 --- a/examples/ibc/learn_ibc_test.go +++ b/examples/ibc/learn_ibc_test.go @@ -6,12 +6,13 @@ import ( "testing" "time" - transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" + + transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" ) // This test is meant to be used as a basic interchaintest tutorial. @@ -39,7 +40,7 @@ func TestLearn(t *testing.T) { // Relayer Factory client, network := interchaintest.DockerSetup(t) - r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( + r := interchaintest.NewBuiltinRelayerFactory(ibc.Hermes, zaptest.NewLogger(t)).Build( t, client, network) // Prep Interchain diff --git a/interchain_test.go b/interchain_test.go index c30879204..28720037a 100644 --- a/interchain_test.go +++ b/interchain_test.go @@ -46,7 +46,7 @@ func TestInterchain_DuplicateChain(t *testing.T) { gaia0, gaia1 := chains[0], chains[1] - r := interchaintest.NewBuiltinRelayerFactory(ibc.Hermes, zaptest.NewLogger(t)).Build( + r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, 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/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index 8275d7363..3b3ab52cb 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -2,8 +2,10 @@ package hermes import ( "context" + "encoding/json" + "fmt" + "regexp" - "github.com/pelletier/go-toml" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" "go.uber.org/zap" @@ -11,9 +13,16 @@ import ( var _ relayer.RelayerCommander = &commander{} +var ( + + // parseRestoreKeyOutput extracts the address from the hermes output. + // SUCCESS Restored key 'g2-2' (cosmos1czklnpzwaq3hfxtv6ne4vas2p9m5q3p3fgkz8e) on chain g2-2 + parseRestoreKeyOutput = regexp.MustCompile("\\((.*)\\)") +) + type commander struct { log *zap.Logger - paths map[string]pathConfiguration + paths map[string]*pathConfiguration } func (c commander) Name() string { @@ -33,36 +42,79 @@ func (c commander) DockerUser() string { } func (c commander) ConfigContent(ctx context.Context, cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { - hermesConfig := NewConfig(keyName, rpcAddr, grpcAddr, cfg) - bz, err := toml.Marshal(hermesConfig) - if err != nil { - return nil, err - } - return bz, nil + panic("shouldn't be called::ConfigContent") } func (c commander) ParseAddKeyOutput(stdout, stderr string) (ibc.Wallet, error) { + fmt.Println(stdout) + fmt.Println(stderr) //TODO implement me panic("implement me") } +// ParseRestoreKeyOutput extracts the address from the hermes output. func (c commander) ParseRestoreKeyOutput(stdout, stderr string) string { - //TODO implement me - panic("implement me") + fullMatchIdx, addressGroupIdx := 0, 1 + return parseRestoreKeyOutput.FindAllStringSubmatch(stdout, -1)[fullMatchIdx][addressGroupIdx] +} + +type ChannelOutputResult struct { + Result []ChannelResult `json:"result"` + Status string `json:"status"` +} + +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"` } func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { - //TODO implement me - panic("implement me") + var result ChannelOutputResult + if err := json.Unmarshal([]byte(stdout), &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) { - //TODO implement me + fmt.Println(stdout) + fmt.Println(stderr) panic("implement me") } func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { - //TODO implement me + fmt.Println(stdout) + fmt.Println(stderr) panic("implement me") } @@ -80,9 +132,10 @@ func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { } func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { - pathConfig := c.paths[pathName] - // hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) - return []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--home", homeDir, "--new-client-connection"} + panic("don't call me") + //pathConfig := c.paths[pathName] + //// hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) + //return []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--b-chain", pathConfig.chainB.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--new-client-connection"} } func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { @@ -118,11 +171,11 @@ func (c commander) CreateConnections(pathName string, homeDir string) []string { } func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { - return nil + panic("implemented one layer above") } func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { - return nil + panic("implemented one layer above") } func (c commander) GetChannels(chainID, homeDir string) []string { @@ -148,7 +201,7 @@ func (c commander) GetChannels(chainID, homeDir string) []string { // //REQUIRED: // --chain Identifier of the chain to query - return []string{hermes, "--json", "query", "channels", "--home", homeDir, "--chain", chainID} + return []string{hermes, "--json", "query", "channels", "--chain", chainID, "--show-counterparty", "--verbose"} } func (c commander) GetConnections(chainID, homeDir string) []string { @@ -170,7 +223,7 @@ func (c commander) GetConnections(chainID, homeDir string) []string { // //REQUIRED: // --chain Identifier of the chain to query - return []string{hermes, "--json", "query", "connections", "--chain", chainID, "--home", homeDir} + return []string{hermes, "--json", "query", "connections", "--chain", chainID} } func (c commander) GetClients(chainID, homeDir string) []string { @@ -192,7 +245,7 @@ func (c commander) GetClients(chainID, homeDir string) []string { // //REQUIRED: // --host-chain Identifier of the chain to query - return []string{hermes, "--json", "query", "clients", "--host-chain", chainID, "--home", homeDir} + return []string{hermes, "--json", "query", "clients", "--host-chain", chainID} } func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { @@ -269,15 +322,15 @@ func (r *Relayer) GeneratePath(ctx context.Context, rep ibc.RelayerExecReporter, // Not in Hermes func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { - panic("path does not exist in hermes") + panic("path does not exist in hermes::GeneratePath") } // Not in Hermes func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { - panic("path does not exist in hermes") + panic("path does not exist in hermes::UpdatePath") } // Not in Hermes func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { - panic("path does not exist in hermes") + panic("path does not exist in hermes::LinkPath") } diff --git a/relayer/hermes/hermes_config.go b/relayer/hermes/hermes_config.go index 48a1f11bd..722ca1333 100644 --- a/relayer/hermes/hermes_config.go +++ b/relayer/hermes/hermes_config.go @@ -2,24 +2,30 @@ package hermes import ( "fmt" + "strconv" "strings" - - "github.com/strangelove-ventures/interchaintest/v6/ibc" ) // https://github.com/informalsystems/hermes/blob/master/config.toml -func NewConfig(keyName, rpcAddr, grpcAddr string, chainConfigs ...ibc.ChainConfig) Config { +func NewConfig(chainConfigs ...ChainConfig) Config { var chains []Chain - for _, chainCfg := range chainConfigs { + 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: rpcAddr, - GrpcAddr: fmt.Sprintf("http://%s", grpcAddr), - WebsocketAddr: strings.ReplaceAll(fmt.Sprintf("%s/websocket", rpcAddr), "http", "ws"), + 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: keyName, + KeyName: hermesCfg.keyName, AddressType: AddressType{ Derivation: "cosmos", }, @@ -27,10 +33,10 @@ func NewConfig(keyName, rpcAddr, grpcAddr string, chainConfigs ...ibc.ChainConfi DefaultGas: 100000, MaxGas: 400000, GasPrice: GasPrice{ - Price: 0.001, - Denom: "stake", + Price: gasPricesStr, + Denom: chainCfg.Denom, }, - GasMultiplier: 1.1, + GasMultiplier: chainCfg.GasAdjustment, MaxMsgNum: 30, MaxTxSize: 2097152, ClockDrift: "5s", @@ -78,96 +84,6 @@ func NewConfig(keyName, rpcAddr, grpcAddr string, chainConfigs ...ibc.ChainConfi } } -//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"` -// AutoRegisterCounterpartyPayee bool `toml:"auto_register_counterparty_payee"` -//} -// -//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"` -//} - type Config struct { Global Global `toml:"global"` Mode Mode `toml:"mode"` @@ -175,53 +91,65 @@ type Config struct { 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"` diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index a58adf2fb..3d9deb080 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -6,6 +6,7 @@ import ( "time" "github.com/docker/docker/client" + "github.com/pelletier/go-toml" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/internal/dockerutil" "github.com/strangelove-ventures/interchaintest/v6/relayer" @@ -17,8 +18,8 @@ const ( defaultContainerImage = "docker.io/informalsystems/hermes" DefaultContainerVersion = "1.0.0" - // TODO: this was taken from RlyDefaultUidGid. Figure out what value should be used. hermesDefaultUidGid = "1000:1000" + hermesHome = "/home/hermes" hermesConfigPath = ".hermes/config.toml" ) @@ -27,7 +28,13 @@ var _ ibc.Relayer = &Relayer{} // Relayer is the ibc.Relayer implementation for hermes. type Relayer struct { *relayer.DockerRelayer - paths map[string]*pathConfiguration + paths map[string]*pathConfiguration + chainConfigs []ChainConfig +} + +type ChainConfig struct { + cfg ibc.ChainConfig + keyName, rpcAddr, grpcAddr string } type pathConfiguration struct { @@ -41,13 +48,7 @@ type pathChainConfig struct { func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, networkID string, options ...relayer.RelayerOption) *Relayer { c := commander{log: log} - //for _, opt := range options { - // switch o := opt.(type) { - // case relayer.RelayerOptionExtraStartFlags: - //c.extraStartFlags = o.Flags - //} - //} - options = append(options, relayer.RelayerHomeDir("/home/hermes")) + options = append(options, relayer.HomeDir(hermesHome)) dr, err := relayer.NewDockerRelayer(context.TODO(), log, testName, cli, networkID, c, options...) if err != nil { panic(err) @@ -67,7 +68,6 @@ func (r *Relayer) populatePathConfig(pathName string) error { func (r *Relayer) validateConfig(ctx context.Context, rep ibc.RelayerExecReporter) error { cmd := []string{hermes, "--config", fmt.Sprintf("%s/%s", r.HomeDir(), hermesConfigPath), "config", "validate"} - //cmd := []string{hermes, "config", "validate"} res := r.Exec(ctx, rep, cmd, nil) if res.Err != nil { return res.Err @@ -75,14 +75,27 @@ func (r *Relayer) validateConfig(ctx context.Context, rep ibc.RelayerExecReporte return nil } +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 +} + func (r *Relayer) AddChainConfiguration(ctx context.Context, rep ibc.RelayerExecReporter, chainConfig ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) error { - configContent, err := commander{}.ConfigContent(ctx, chainConfig, keyName, rpcAddr, grpcAddr) + configContent, err := r.configContent(chainConfig, keyName, rpcAddr, grpcAddr) if err != nil { return fmt.Errorf("failed to generate config content: %w", err) } - r.Logger().Info(string(configContent)) - fw := dockerutil.NewFileWriter(r.Logger(), r.Client(), r.TestName()) if err := fw.WriteFile(ctx, r.VolumeName(), hermesConfigPath, configContent); err != nil { return fmt.Errorf("failed to rly config: %w", err) @@ -97,14 +110,6 @@ func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pat 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 } @@ -112,6 +117,13 @@ func (r *Relayer) LinkPath(ctx context.Context, rep ibc.RelayerExecReporter, pat return r.populatePathConfig(pathName) } +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, "--b-chain", pathConfig.chainB.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--new-client-connection", "--yes"} + res := r.Exec(ctx, rep, cmd, nil) + return res.Err +} + func (r *Relayer) UpdateClients(ctx context.Context, rep ibc.RelayerExecReporter, pathName string) error { pathConfig, ok := r.paths[pathName] if !ok { @@ -169,10 +181,9 @@ func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, c relativeMnemonicFilePath := "mnemonic.txt" fw := dockerutil.NewFileWriter(r.Logger(), r.Client(), r.TestName()) if err := fw.WriteFile(ctx, r.VolumeName(), relativeMnemonicFilePath, []byte(mnemonic)); err != nil { - return fmt.Errorf("failed to write mnemoic file: %w", err) + return fmt.Errorf("failed to write mnemonic file: %w", err) } - //cmd := []string{hermes, "--config", fmt.Sprintf("%s/%s", r.HomeDir(), hermesConfigPath), "keys", "add", "--chain", chainID, "--mnemonic-file", fmt.Sprintf("%s/%s", r.HomeDir(), relativeMnemonicFilePath)} cmd := []string{hermes, "keys", "add", "--chain", chainID, "--mnemonic-file", fmt.Sprintf("%s/%s", r.HomeDir(), relativeMnemonicFilePath)} // Restoring a key should be near-instantaneous, so add a 1-minute timeout @@ -187,7 +198,41 @@ func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, c addrBytes := commander{}.ParseRestoreKeyOutput(string(res.Stdout), string(res.Stderr)) - r.Wallets[chainID] = commander{}.CreateWallet("", addrBytes, mnemonic) + r.Wallets[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 { + //DESCRIPTION: + // Clear outstanding packets (i.e., packet-recv and packet-ack) on a given channel in both directions. + // The channel is identified by the chain, port, and channel IDs at one of its ends + // + //USAGE: + // hermes clear packets [OPTIONS] --chain --port --channel + // + // OPTIONS: + // --counterparty-key-name + // use the given signing key for the counterparty chain (default: `counterparty_key_name` + // config) + // + // -h, --help + // Print help information + // + // --key-name + // use the given signing key for the specified chain (default: `key_name` config) + // + // REQUIRED: + // --chain Identifier of the chain + // --channel Identifier of the channel + // --port Identifier of the port + + path := r.paths[pathName] + cmd := []string{hermes, "clear", "packets", "--chain", path.chainA.chainID, "--channel", channelID, "--port", "transfer"} + res := r.Exec(ctx, rep, cmd, nil) + return res.Err +} diff --git a/relayer/options.go b/relayer/options.go index e1c0cbc2e..59b65a327 100644 --- a/relayer/options.go +++ b/relayer/options.go @@ -35,7 +35,7 @@ func CustomDockerImage(repository string, version string, uidGid string) Relayer } } -func RelayerHomeDir(homeDir string) RelayerOption { +func HomeDir(homeDir string) RelayerOption { return RelayerOptionHomeDir{HomeDir: homeDir} } From be8adbfb958f802b1f988f79a240ced032eaf930 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 10:46:06 +0000 Subject: [PATCH 08/32] learn ibc test passing with hermes relayer --- examples/ibc/learn_ibc_test.go | 2 +- relayer/docker.go | 41 ++--- relayer/hermes/hermes_commander.go | 252 +++++++---------------------- relayer/hermes/hermes_relayer.go | 239 ++++++++++++++++----------- relayer/hermes/hermes_types.go | 51 ++++++ relayer/options.go | 1 + relayer/rly/cosmos_relayer.go | 4 +- 7 files changed, 281 insertions(+), 309 deletions(-) create mode 100644 relayer/hermes/hermes_types.go diff --git a/examples/ibc/learn_ibc_test.go b/examples/ibc/learn_ibc_test.go index f95714162..60da6490c 100644 --- a/examples/ibc/learn_ibc_test.go +++ b/examples/ibc/learn_ibc_test.go @@ -40,7 +40,7 @@ func TestLearn(t *testing.T) { // Relayer Factory client, network := interchaintest.DockerSetup(t) - r := interchaintest.NewBuiltinRelayerFactory(ibc.Hermes, zaptest.NewLogger(t)).Build( + r := interchaintest.NewBuiltinRelayerFactory(ibc.CosmosRly, zaptest.NewLogger(t)).Build( t, client, network) // Prep Interchain diff --git a/relayer/docker.go b/relayer/docker.go index 8594f777a..294130375 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 { @@ -40,8 +44,8 @@ type DockerRelayer struct { // The ID of the container created by StartRelayer. containerID string - // Wallets contains a mapping of chainID to relayer wallet - Wallets map[string]ibc.Wallet + // wallets contains a mapping of chainID to relayer wallet + wallets map[string]ibc.Wallet homeDir string } @@ -63,10 +67,10 @@ func NewDockerRelayer(ctx context.Context, log *zap.Logger, testName string, cli testName: testName, - Wallets: map[string]ibc.Wallet{}, + wallets: map[string]ibc.Wallet{}, } - r.homeDir = "/home/relayer" + r.homeDir = defaultRlyHomeDirectory for _, opt := range options { switch o := opt.(type) { @@ -128,20 +132,19 @@ func NewDockerRelayer(ctx context.Context, log *zap.Logger, testName string, cli return &r, nil } -func (r *DockerRelayer) Logger() *zap.Logger { - return r.log -} - -func (r *DockerRelayer) TestName() string { - return r.testName -} - -func (r *DockerRelayer) VolumeName() string { - return r.volumeName +// 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 } -func (r *DockerRelayer) Client() *client.Client { - return r.client +// 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 { @@ -189,12 +192,12 @@ func (r *DockerRelayer) AddKey(ctx context.Context, rep ibc.RelayerExecReporter, if err != nil { return nil, err } - r.Wallets[chainID] = wallet + r.wallets[chainID] = wallet return wallet, nil } func (r *DockerRelayer) GetWallet(chainID string) (ibc.Wallet, bool) { - wallet, ok := r.Wallets[chainID] + wallet, ok := r.wallets[chainID] return wallet, ok } @@ -325,7 +328,7 @@ func (r *DockerRelayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecRepor addrBytes := r.c.ParseRestoreKeyOutput(string(res.Stdout), string(res.Stderr)) - r.Wallets[chainID] = r.c.CreateWallet("", addrBytes, mnemonic) + r.wallets[chainID] = r.c.CreateWallet("", addrBytes, mnemonic) return nil } diff --git a/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index 3b3ab52cb..1fcf6ab7e 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -3,8 +3,6 @@ package hermes import ( "context" "encoding/json" - "fmt" - "regexp" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" @@ -13,13 +11,6 @@ import ( var _ relayer.RelayerCommander = &commander{} -var ( - - // parseRestoreKeyOutput extracts the address from the hermes output. - // SUCCESS Restored key 'g2-2' (cosmos1czklnpzwaq3hfxtv6ne4vas2p9m5q3p3fgkz8e) on chain g2-2 - parseRestoreKeyOutput = regexp.MustCompile("\\((.*)\\)") -) - type commander struct { log *zap.Logger paths map[string]*pathConfiguration @@ -41,46 +32,6 @@ func (c commander) DockerUser() string { return hermesDefaultUidGid } -func (c commander) ConfigContent(ctx context.Context, cfg ibc.ChainConfig, keyName, rpcAddr, grpcAddr string) ([]byte, error) { - panic("shouldn't be called::ConfigContent") -} - -func (c commander) ParseAddKeyOutput(stdout, stderr string) (ibc.Wallet, error) { - fmt.Println(stdout) - fmt.Println(stderr) - //TODO implement me - panic("implement me") -} - -// ParseRestoreKeyOutput extracts the address from the hermes output. -func (c commander) ParseRestoreKeyOutput(stdout, stderr string) string { - fullMatchIdx, addressGroupIdx := 0, 1 - return parseRestoreKeyOutput.FindAllStringSubmatch(stdout, -1)[fullMatchIdx][addressGroupIdx] -} - -type ChannelOutputResult struct { - Result []ChannelResult `json:"result"` - Status string `json:"status"` -} - -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"` -} - func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { var result ChannelOutputResult if err := json.Unmarshal([]byte(stdout), &result); err != nil { @@ -107,14 +58,10 @@ func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelO } func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { - fmt.Println(stdout) - fmt.Println(stderr) panic("implement me") } func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { - fmt.Println(stdout) - fmt.Println(stderr) panic("implement me") } @@ -122,107 +69,13 @@ func (c commander) Init(homeDir string) []string { return nil } -func (c commander) AddChainConfiguration(containerFilePath, homeDir string) []string { - return nil -} - -func (c commander) AddKey(chainID, keyName, coinType, homeDir string) []string { - // hermes keys add --chain foo --key-file - return nil -} - -func (c commander) CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string { - panic("don't call me") - //pathConfig := c.paths[pathName] - //// hermes create channel [OPTIONS] --a-chain --b-chain --a-port --b-port (--new-client-connection) - //return []string{hermes, "--json", "create", "channel", "--a-chain", pathConfig.chainA.chainID, "--b-chain", pathConfig.chainB.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--new-client-connection"} -} - -func (c commander) CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string { - pathConfig := c.paths[pathName] - - // hermes create client [OPTIONS] --host-chain --reference-chain - return []string{hermes, "--json", "create", "client", "--host-chain", pathConfig.chainA.chainID, "--reference-chain", ""} -} - -func (c commander) CreateConnections(pathName string, homeDir string) []string { - //DESCRIPTION: - // Create a new connection between two chains - // - //USAGE: - // hermes create connection [OPTIONS] --a-chain --b-chain - // - // hermes create connection [OPTIONS] --a-chain --a-client --b-client - // - // OPTIONS: - // --delay Delay period parameter for the new connection (seconds) [default: 0] - // -h, --help Print help information - // - // FLAGS: - // --a-chain Identifier of the side `a` chain for the new connection - // --a-client Identifier of client hosted on chain `a`; default: None (creates - // a new client) - // --b-chain Identifier of the side `b` chain for the new connection - // --b-client Identifier of client hosted on chain `b`; default: None (creates - // a new client) - - pathConfig := c.paths[pathName] - return []string{hermes, "--json", "create", "connection", "--a-chain", pathConfig.chainA.chainID, "--b-chain", pathConfig.chainB.chainID, "--home", homeDir} -} - -func (c commander) FlushAcknowledgements(pathName, channelID, homeDir string) []string { - panic("implemented one layer above") -} - -func (c commander) FlushPackets(pathName, channelID, homeDir string) []string { - panic("implemented one layer above") -} - func (c commander) GetChannels(chainID, homeDir string) []string { - //DESCRIPTION: - // Query the identifiers of all channels on a given chain - // - //USAGE: - // hermes query channels [OPTIONS] --chain - // - // OPTIONS: - // --counterparty-chain - // Filter the query response by the this counterparty chain - // - // -h, --help - // Print help information - // - // --show-counterparty - // Show the counterparty chain, port, and channel - // - // --verbose - // Enable verbose output, displaying the client and connection ids for each channel in the - // response - // - //REQUIRED: - // --chain Identifier of the chain to query + // 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 { - //DESCRIPTION: - // Query the identifiers of all connections on a chain - // - //USAGE: - // hermes query connections [OPTIONS] --chain - // - // OPTIONS: - // --counterparty-chain - // Filter the query response by the counterparty chain - // - // -h, --help - // Print help information - // - // --verbose - // Enable verbose output, displaying the client for each connection in the response - // - //REQUIRED: - // --chain Identifier of the chain to query return []string{hermes, "--json", "query", "connections", "--chain", chainID} } @@ -248,30 +101,8 @@ func (c commander) GetClients(chainID, homeDir string) []string { return []string{hermes, "--json", "query", "clients", "--host-chain", chainID} } -func (c commander) RestoreKey(chainID, keyName, coinType, mnemonic, homeDir string) []string { - //DESCRIPTION: - // Adds key to a configured chain or restores a key to a configured chain using a mnemonic - // - //USAGE: - // hermes keys add [OPTIONS] --chain --key-file - // - // hermes keys add [OPTIONS] --chain --mnemonic-file - // - // OPTIONS: - // -h, --help Print help information - // --hd-path Derivation path for this key [default: m/44'/118'/0'/0/0] - // --key-name Name of the key (defaults to the `key_name` defined in the config) - // --overwrite Overwrite the key if there is already one with the same key name - // - // FLAGS: - // --chain Identifier of the chain - // --key-file Path to the key file - // --mnemonic-file Path to file containing mnemonic to restore the key from - - return []string{hermes, "keys", "add", "--chain", chainID} -} - func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { + // TODO: only look at paths (probably needs to happen at relayer level not commander) return []string{hermes, "--json", "start", "--full-scan"} } @@ -295,6 +126,7 @@ func (c commander) UpdateClients(pathName, homeDir string) []string { // REQUIRED: // --client Identifier of the chain targeted by the client // --host-chain Identifier of the chain that hosts the client + // TODO: implement return nil } @@ -302,35 +134,63 @@ func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { return NewWallet(keyName, address, mnemonic) } -// Not in Hermes -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, - clientID: "", - }, - chainB: pathChainConfig{ - chainID: dstChainID, - clientID: "", - }, - } - return nil +func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { + // TODO: figure out how to implement this. + panic("implement me") } -// Not in Hermes func (c commander) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { - panic("path does not exist in hermes::GeneratePath") + panic("generate path implemented in hermes relayer not the commander") } -// Not in Hermes -func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter) []string { - panic("path does not exist in hermes::UpdatePath") -} +// 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. -// Not in Hermes func (c commander) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { - panic("path does not exist in hermes::LinkPath") + 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_relayer.go b/relayer/hermes/hermes_relayer.go index 3d9deb080..fd65bfb5a 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -2,13 +2,14 @@ package hermes import ( "context" + "encoding/json" "fmt" + "regexp" "time" "github.com/docker/docker/client" "github.com/pelletier/go-toml" "github.com/strangelove-ventures/interchaintest/v6/ibc" - "github.com/strangelove-ventures/interchaintest/v6/internal/dockerutil" "github.com/strangelove-ventures/interchaintest/v6/relayer" "go.uber.org/zap" ) @@ -23,7 +24,12 @@ const ( hermesConfigPath = ".hermes/config.toml" ) -var _ ibc.Relayer = &Relayer{} +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 { @@ -32,20 +38,27 @@ type Relayer struct { 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 + 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)) @@ -59,68 +72,71 @@ func NewHermesRelayer(log *zap.Logger, testName string, cli *client.Client, netw } } -func (r *Relayer) populatePathConfig(pathName string) error { - //, ok := r.paths[pathName] - - // Query things - return nil -} - -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 -} - -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 -} - +// 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) } - fw := dockerutil.NewFileWriter(r.Logger(), r.Client(), r.TestName()) - if err := fw.WriteFile(ctx, r.VolumeName(), hermesConfigPath, configContent); err != nil { - return fmt.Errorf("failed to rly config: %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 r.populatePathConfig(pathName) + 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, "--b-chain", pathConfig.chainB.chainID, "--a-port", opts.SourcePortName, "--b-port", opts.DestPortName, "--new-client-connection", "--yes"} + 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 } @@ -138,6 +154,9 @@ func (r *Relayer) UpdateClients(ctx context.Context, rep ibc.RelayerExecReporter 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} @@ -146,41 +165,33 @@ func (r *Relayer) CreateClients(ctx context.Context, rep ibc.RelayerExecReporter return res.Err } - // TODO: parse res and update pathConfig? + 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 } - // TODO: parse res and update pathConfig? + + 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 { - //DESCRIPTION: - // Adds key to a configured chain or restores a key to a configured chain using a mnemonic - // - //USAGE: - // hermes keys add [OPTIONS] --chain --key-file - // - // hermes keys add [OPTIONS] --chain --mnemonic-file - // - // OPTIONS: - // -h, --help Print help information - // --hd-path Derivation path for this key [default: m/44'/118'/0'/0/0] - // --key-name Name of the key (defaults to the `key_name` defined in the config) - // --overwrite Overwrite the key if there is already one with the same key name - // - // FLAGS: - // --chain Identifier of the chain - // --key-file Path to the key file - // --mnemonic-file Path to file containing mnemonic to restore the key from - - relativeMnemonicFilePath := "mnemonic.txt" - fw := dockerutil.NewFileWriter(r.Logger(), r.Client(), r.TestName()) - if err := fw.WriteFile(ctx, r.VolumeName(), relativeMnemonicFilePath, []byte(mnemonic)); err != nil { + + 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) } @@ -196,10 +207,8 @@ func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, c return res.Err } - addrBytes := commander{}.ParseRestoreKeyOutput(string(res.Stdout), string(res.Stderr)) - - r.Wallets[chainID] = NewWallet(chainID, addrBytes, mnemonic) - + addrBytes := parseRestoreKeyOutput(string(res.Stdout)) + r.AddWallet(chainID, NewWallet(chainID, addrBytes, mnemonic)) return nil } @@ -208,31 +217,77 @@ func (r *Relayer) FlushAcknowledgements(ctx context.Context, rep ibc.RelayerExec } func (r *Relayer) FlushPackets(ctx context.Context, rep ibc.RelayerExecReporter, pathName, channelID string) error { - //DESCRIPTION: - // Clear outstanding packets (i.e., packet-recv and packet-ack) on a given channel in both directions. - // The channel is identified by the chain, port, and channel IDs at one of its ends - // - //USAGE: - // hermes clear packets [OPTIONS] --chain --port --channel - // - // OPTIONS: - // --counterparty-key-name - // use the given signing key for the counterparty chain (default: `counterparty_key_name` - // config) - // - // -h, --help - // Print help information - // - // --key-name - // use the given signing key for the specified chain (default: `key_name` config) - // - // REQUIRED: - // --chain Identifier of the chain - // --channel Identifier of the channel - // --port Identifier of the port - path := r.paths[pathName] - cmd := []string{hermes, "clear", "packets", "--chain", path.chainA.chainID, "--channel", channelID, "--port", "transfer"} + 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 +} + +// getClientIdFromStdout extracts the client ID from stdout. +func getClientIdFromStdout(stdout []byte) (string, error) { + var clientCreationResult ClientCreationResponse + if err := json.Unmarshal(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(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..29c148dae --- /dev/null +++ b/relayer/hermes/hermes_types.go @@ -0,0 +1,51 @@ +package hermes + +// ClientCreationResponse contains the minimum required values to extract the client id from the hermes response. +type ClientCreationResponse struct { + Result ClientResult `json:"result"` +} + +type CreateClient struct { + ClientID string `json:"client_id"` +} + +type ClientResult 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"` +} diff --git a/relayer/options.go b/relayer/options.go index 59b65a327..83ec1bace 100644 --- a/relayer/options.go +++ b/relayer/options.go @@ -16,6 +16,7 @@ type RelayerOptionDockerImage struct { DockerImage ibc.DockerImage } +// RelayerOptionHomeDir allows the configuration of the relayer home directory. type RelayerOptionHomeDir struct { HomeDir string } diff --git a/relayer/rly/cosmos_relayer.go b/relayer/rly/cosmos_relayer.go index 454a33fcd..18d393d9e 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 { From 7a5160e26c79ea602d70dea4e1f8efc444e84e69 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 11:54:16 +0000 Subject: [PATCH 09/32] adding parse client and connection output --- examples/ibc/interchain_accounts_test.go | 8 +-- relayer/hermes/hermes_commander.go | 79 ++++++++++++++++-------- relayer/hermes/hermes_types.go | 47 +++++++++++++- 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/examples/ibc/interchain_accounts_test.go b/examples/ibc/interchain_accounts_test.go index 26f05ca8f..c341ba0de 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/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index 1fcf6ab7e..8d609a9df 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -7,6 +7,9 @@ import ( "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" "go.uber.org/zap" + + ibcexported "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" + "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types" ) var _ relayer.RelayerCommander = &commander{} @@ -58,11 +61,56 @@ func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelO } func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { - panic("implement me") + var queryResult ConnectionQueryResult + if err := json.Unmarshal([]byte(stdout), &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) { - panic("implement me") + var queryResult ClientQueryResult + if err := json.Unmarshal([]byte(stdout), &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 { @@ -76,33 +124,14 @@ func (c commander) GetChannels(chainID, homeDir string) []string { } func (c commander) GetConnections(chainID, homeDir string) []string { - return []string{hermes, "--json", "query", "connections", "--chain", chainID} + return []string{hermes, "--json", "query", "connections", "--chain", chainID, "--verbose"} } func (c commander) GetClients(chainID, homeDir string) []string { - //DESCRIPTION: - // Query the identifiers of all clients on a chain - // - //USAGE: - // hermes query clients [OPTIONS] --host-chain - // - // OPTIONS: - // -h, --help - // Print help information - // - // --omit-chain-ids - // Omit printing the reference (or target) chain for each client - // - // --reference-chain - // Filter for clients which target a specific chain id (implies '--omit-chain-ids') - // - //REQUIRED: - // --host-chain Identifier of the chain to query return []string{hermes, "--json", "query", "clients", "--host-chain", chainID} } func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { - // TODO: only look at paths (probably needs to happen at relayer level not commander) return []string{hermes, "--json", "start", "--full-scan"} } @@ -139,13 +168,13 @@ func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter 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) GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string { panic("generate path implemented in hermes relayer not the commander") } -// 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) LinkPath(pathName, homeDir string, channelOpts ibc.CreateChannelOptions, clientOpts ibc.CreateClientOptions) []string { panic("link path implemented in hermes relayer not the commander") } diff --git a/relayer/hermes/hermes_types.go b/relayer/hermes/hermes_types.go index 29c148dae..35444cb8f 100644 --- a/relayer/hermes/hermes_types.go +++ b/relayer/hermes/hermes_types.go @@ -2,14 +2,14 @@ package hermes // ClientCreationResponse contains the minimum required values to extract the client id from the hermes response. type ClientCreationResponse struct { - Result ClientResult `json:"result"` + Result CreateClientResult `json:"result"` } type CreateClient struct { ClientID string `json:"client_id"` } -type ClientResult struct { +type CreateClientResult struct { CreateClient CreateClient `json:"CreateClient"` } @@ -49,3 +49,46 @@ 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"` +} From f7ca9840fad1a15c04c2b34c0c61f9bff60c1a4a Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 12:11:49 +0000 Subject: [PATCH 10/32] adding hermes test cases --- interchain_test.go | 36 +++++++++++++++++++++++++----- relayer/hermes/hermes_commander.go | 31 +++++-------------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/interchain_test.go b/interchain_test.go index 28720037a..74b6f1300 100644 --- a/interchain_test.go +++ b/interchain_test.go @@ -26,7 +26,15 @@ import ( clienttypes "github.com/cosmos/ibc-go/v6/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") } @@ -46,7 +54,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, ) @@ -74,7 +82,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") } @@ -94,7 +110,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, ) @@ -230,7 +246,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") } @@ -250,7 +274,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/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index 8d609a9df..8d27dd2c4 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -15,8 +15,7 @@ import ( var _ relayer.RelayerCommander = &commander{} type commander struct { - log *zap.Logger - paths map[string]*pathConfiguration + log *zap.Logger } func (c commander) Name() string { @@ -135,30 +134,6 @@ func (c commander) StartRelayer(homeDir string, pathNames ...string) []string { return []string{hermes, "--json", "start", "--full-scan"} } -func (c commander) UpdateClients(pathName, homeDir string) []string { - //DESCRIPTION: - // Update an IBC client - // - //USAGE: - // hermes update client [OPTIONS] --host-chain --client - // - // OPTIONS: - // -h, --help - // Print help information - // - // --height - // The target height of the client update. Leave unspecified for latest height. - // - // --trusted-height - // The trusted height of the client update. Leave unspecified for latest height. - // - // REQUIRED: - // --client Identifier of the chain targeted by the client - // --host-chain Identifier of the chain that hosts the client - // TODO: implement - return nil -} - func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { return NewWallet(keyName, address, mnemonic) } @@ -171,6 +146,10 @@ func (c commander) UpdatePath(pathName, homeDir string, filter ibc.ChannelFilter // 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") } From 5bbefaa545c5e82c2412e1feeb1830694aacc754 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 12:35:05 +0000 Subject: [PATCH 11/32] remove unused types --- ibc/relayer.go | 5 ----- interchain.go | 1 - 2 files changed, 6 deletions(-) diff --git a/ibc/relayer.go b/ibc/relayer.go index 84a763c05..c7ab78abc 100644 --- a/ibc/relayer.go +++ b/ibc/relayer.go @@ -259,11 +259,6 @@ func (o Order) Validate() error { return chantypes.ErrInvalidChannelOrdering } -// CreateConnectionOptions contains the configuration for creating a connection. -type CreateConnectionOptions struct { - ChainAID, ChainBID string -} - // CreateClientOptions contains the configuration for creating a client. type CreateClientOptions struct { TrustingPeriod string diff --git a/interchain.go b/interchain.go index 741050106..e49b5cc97 100644 --- a/interchain.go +++ b/interchain.go @@ -307,7 +307,6 @@ func (ic *Interchain) Build(ctx context.Context, rep *testreporter.RelayerExecRe return err } - // TODO: fix create connection options. if err := rp.Relayer.LinkPath(ctx, rep, rp.Path, link.createChannelOpts, link.createClientOpts); err != nil { return fmt.Errorf( "failed to link path %s on relayer %s between chains %s and %s: %w", From a1e091ee179dce4c9a44e00ddd8b310403131051 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 12:36:22 +0000 Subject: [PATCH 12/32] undid import change --- conformance/relayersetup.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/conformance/relayersetup.go b/conformance/relayersetup.go index a7b081afa..3cd306a2f 100644 --- a/conformance/relayersetup.go +++ b/conformance/relayersetup.go @@ -5,14 +5,13 @@ import ( "fmt" "testing" + conntypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" - - conntypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" ) // TestRelayerSetup contains a series of subtests that configure a relayer step-by-step. From 5ce3a8d8da1623bd51a6fe7febc1e569f07e29d5 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 12:38:06 +0000 Subject: [PATCH 13/32] reverted import change --- examples/ibc/learn_ibc_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/ibc/learn_ibc_test.go b/examples/ibc/learn_ibc_test.go index 60da6490c..9f8287803 100644 --- a/examples/ibc/learn_ibc_test.go +++ b/examples/ibc/learn_ibc_test.go @@ -6,13 +6,12 @@ import ( "testing" "time" + transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" - - transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" ) // This test is meant to be used as a basic interchaintest tutorial. From 5f573c9f63a76de925747ed023793d640ab19a07 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 12:41:31 +0000 Subject: [PATCH 14/32] reverted some unintentional changes --- ibc/relayer.go | 3 ++- relayer/docker.go | 2 +- relayer/hermes/hermes_config.go | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ibc/relayer.go b/ibc/relayer.go index c7ab78abc..801524a66 100644 --- a/ibc/relayer.go +++ b/ibc/relayer.go @@ -200,7 +200,8 @@ type RelayerExecResult struct { type CreateChannelOptions struct { SourcePortName string DestPortName string - Order Order + + Order Order Version string } diff --git a/relayer/docker.go b/relayer/docker.go index 294130375..c528f6f3d 100644 --- a/relayer/docker.go +++ b/relayer/docker.go @@ -548,7 +548,7 @@ type RelayerCommander interface { AddKey(chainID, keyName, coinType, homeDir string) []string CreateChannel(pathName string, opts ibc.CreateChannelOptions, homeDir string) []string CreateClients(pathName string, opts ibc.CreateClientOptions, homeDir string) []string - CreateConnections(pathName string, homeDir string) []string + CreateConnections(pathName, homeDir string) []string FlushAcknowledgements(pathName, channelID, homeDir string) []string FlushPackets(pathName, channelID, homeDir string) []string GeneratePath(srcChainID, dstChainID, pathName, homeDir string) []string diff --git a/relayer/hermes/hermes_config.go b/relayer/hermes/hermes_config.go index 722ca1333..1a73ff6fa 100644 --- a/relayer/hermes/hermes_config.go +++ b/relayer/hermes/hermes_config.go @@ -6,8 +6,9 @@ import ( "strings" ) -// https://github.com/informalsystems/hermes/blob/master/config.toml +// 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 { From 48d85623ef333bfc35afa05aadfb3ad47ea59c9e Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Tue, 14 Feb 2023 13:36:31 +0000 Subject: [PATCH 15/32] fix linting error --- relayer/hermes/hermes_relayer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index fd65bfb5a..1efcbaa07 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -28,7 +28,7 @@ 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("\\((.*)\\)") + parseRestoreKeyOutputPattern = regexp.MustCompile(`\((.*)\)`) ) // Relayer is the ibc.Relayer implementation for hermes. From 47af05d641ad6aae4ab6adf4a3acd37c9f1c0487 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Wed, 15 Feb 2023 17:26:56 +0000 Subject: [PATCH 16/32] adding hermes to conformance matrix --- cmd/interchaintest/example_matrix.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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": [ [ From 6783734d038f877ed8dc128c73efa7e2dda61f62 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 09:51:40 +0000 Subject: [PATCH 17/32] adding default value for matrix file for CI --- cmd/interchaintest/interchaintest_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 4be2e24db..4ccd017f4 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -9,6 +9,7 @@ import ( "math/rand" "os" "path/filepath" + "strings" "testing" "time" @@ -24,6 +25,10 @@ import ( "go.uber.org/zap" ) +const ( + defaultCIMatrixFile = "cmd/interchaintest/example_matrix.json" +) + func init() { // Because we use the test binary, we use this hack to customize the help usage. flag.Usage = func() { @@ -94,12 +99,22 @@ func TestMain(m *testing.M) { os.Exit(code) } +// isRunningInCI returns true if this test is running in CI. +// Note: On Github, all runners are passed an environment variable of "CI" with a value of "true". +func isRunningInCI() bool { + return strings.ToLower(os.Getenv("CI")) == "true" +} + var extraFlags mainFlags // setUpTestMatrix populates the testMatrix singleton with // the parsed contents of the file referenced by the matrix flag, // or with a small reasonable default of rly against one gaia-osmosis set. func setUpTestMatrix() error { + if extraFlags.MatrixFile == "" && isRunningInCI() { + extraFlags.MatrixFile = defaultCIMatrixFile + } + if extraFlags.MatrixFile == "" { fmt.Fprintln(os.Stderr, "No matrix file provided, falling back to rly with gaia and osmosis") From 1976b22b01496af58799f3f2e3fddb8e447a04c0 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 10:01:44 +0000 Subject: [PATCH 18/32] pass absolute value for matrix file --- cmd/interchaintest/interchaintest_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 4ccd017f4..f9fe43bc5 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -8,6 +8,7 @@ import ( "fmt" "math/rand" "os" + "path" "path/filepath" "strings" "testing" @@ -26,7 +27,7 @@ import ( ) const ( - defaultCIMatrixFile = "cmd/interchaintest/example_matrix.json" + defaultRelativeCIMatrixFile = "cmd/interchaintest/example_matrix.json" ) func init() { @@ -112,7 +113,11 @@ var extraFlags mainFlags // or with a small reasonable default of rly against one gaia-osmosis set. func setUpTestMatrix() error { if extraFlags.MatrixFile == "" && isRunningInCI() { - extraFlags.MatrixFile = defaultCIMatrixFile + currentDir, err := os.Getwd() + if err != nil { + return err + } + extraFlags.MatrixFile = path.Join(currentDir, defaultRelativeCIMatrixFile) } if extraFlags.MatrixFile == "" { From a40eff682b3cdb4331032a692e371bde98ec1f74 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 10:03:32 +0000 Subject: [PATCH 19/32] removed extra part of path --- cmd/interchaintest/interchaintest_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index f9fe43bc5..f65cf9882 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -27,7 +27,7 @@ import ( ) const ( - defaultRelativeCIMatrixFile = "cmd/interchaintest/example_matrix.json" + defaultRelativeCIMatrixFile = "example_matrix.json" ) func init() { From 8194b65f9caee3bbcfed9d39e2b3da314953cf57 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 10:11:43 +0000 Subject: [PATCH 20/32] adding hermes to Labels function --- relayerfactory.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/relayerfactory.go b/relayerfactory.go index e0066b940..e27df6eea 100644 --- a/relayerfactory.go +++ b/relayerfactory.go @@ -97,6 +97,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)) } From cde37cbceb6839978522956e5b9b712642a4edb9 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 10:12:47 +0000 Subject: [PATCH 21/32] add capabilities for hermes --- relayerfactory.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/relayerfactory.go b/relayerfactory.go index e27df6eea..d281b363e 100644 --- a/relayerfactory.go +++ b/relayerfactory.go @@ -110,6 +110,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)) } From 4dfd2a1bfb37596d6770050b1b9230bc1408a9eb Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 10:49:54 +0000 Subject: [PATCH 22/32] temporarily strip down number of tests to verify hermes relayer --- cmd/interchaintest/interchaintest_test.go | 17 ++++++++--------- relayerfactory.go | 6 ++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index f65cf9882..6726b5fae 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -8,7 +8,6 @@ import ( "fmt" "math/rand" "os" - "path" "path/filepath" "strings" "testing" @@ -112,18 +111,18 @@ var extraFlags mainFlags // the parsed contents of the file referenced by the matrix flag, // or with a small reasonable default of rly against one gaia-osmosis set. func setUpTestMatrix() error { - if extraFlags.MatrixFile == "" && isRunningInCI() { - currentDir, err := os.Getwd() - if err != nil { - return err - } - extraFlags.MatrixFile = path.Join(currentDir, defaultRelativeCIMatrixFile) - } + //if extraFlags.MatrixFile == "" && isRunningInCI() { + // currentDir, err := os.Getwd() + // if err != nil { + // return err + // } + // extraFlags.MatrixFile = path.Join(currentDir, defaultRelativeCIMatrixFile) + //} 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{"hermes"} testMatrix.ChainSets = [][]*interchaintest.ChainSpec{ { {Name: "gaia", Version: "v7.0.1"}, diff --git a/relayerfactory.go b/relayerfactory.go index d281b363e..c2e5c4b57 100644 --- a/relayerfactory.go +++ b/relayerfactory.go @@ -87,6 +87,12 @@ 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)) From 9adbf3bb4d3a9373dd45751c6d938dc245ca4cba Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 11:57:59 +0000 Subject: [PATCH 23/32] fixing conformance tests --- conformance/flush.go | 8 +++++++- conformance/relayersetup.go | 7 ++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/conformance/flush.go b/conformance/flush.go index 1aaf21d2e..29b48fb07 100644 --- a/conformance/flush.go +++ b/conformance/flush.go @@ -9,6 +9,7 @@ import ( interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" + "github.com/strangelove-ventures/interchaintest/v6/relayer/hermes" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/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 3cd306a2f..d8bc1eca3 100644 --- a/conformance/relayersetup.go +++ b/conformance/relayersetup.go @@ -5,13 +5,14 @@ import ( "fmt" "testing" - conntypes "github.com/cosmos/ibc-go/v6/modules/core/03-connection/types" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/testreporter" "github.com/strangelove-ventures/interchaintest/v6/testutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + conntypes "github.com/cosmos/ibc-go/v6/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) From 83b0c5acd5e163c26070bcebba0ee9e06f68f5db Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 12:37:59 +0000 Subject: [PATCH 24/32] fixing channel tests in TestRelayerSetup --- conformance/relayersetup.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/conformance/relayersetup.go b/conformance/relayersetup.go index d8bc1eca3..6e1300134 100644 --- a/conformance/relayersetup.go +++ b/conformance/relayersetup.go @@ -161,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") From 5ad0cec216e712fd3832ca2e65fc7a5cc1b45b8f Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 13:42:55 +0000 Subject: [PATCH 25/32] revert to go rly to test --- cmd/interchaintest/interchaintest_test.go | 2 +- test_setup.go | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 6726b5fae..91f3d797d 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -122,7 +122,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{"hermes"} + testMatrix.Relayers = []string{"rly"} testMatrix.ChainSets = [][]*interchaintest.ChainSpec{ { {Name: "gaia", Version: "v7.0.1"}, diff --git a/test_setup.go b/test_setup.go index 00815b4e1..045bc9ed7 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) } } From b26c8c2ade92415627e4a05fe3db7d8826655000 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 14:02:16 +0000 Subject: [PATCH 26/32] bump hermes version --- cmd/interchaintest/interchaintest_test.go | 2 +- relayer/hermes/hermes_relayer.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 91f3d797d..6726b5fae 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -122,7 +122,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{"hermes"} testMatrix.ChainSets = [][]*interchaintest.ChainSpec{ { {Name: "gaia", Version: "v7.0.1"}, diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index 1efcbaa07..f88b1dd72 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -17,7 +17,7 @@ import ( const ( hermes = "hermes" defaultContainerImage = "docker.io/informalsystems/hermes" - DefaultContainerVersion = "1.0.0" + DefaultContainerVersion = "1.2.0" hermesDefaultUidGid = "1000:1000" hermesHome = "/home/hermes" From 0c9e8f823f384ef828e56aa31135a67d82be9228 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 14:39:18 +0000 Subject: [PATCH 27/32] extract json response correctly --- relayer/hermes/hermes_relayer.go | 18 ++++++++++++++++-- relayer/hermes/hermes_types.go | 3 ++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index f88b1dd72..a37e61bbf 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "regexp" + "strings" "time" "github.com/docker/docker/client" @@ -268,10 +269,23 @@ func (r *Relayer) validateConfig(ctx context.Context, rep ibc.RelayerExecReporte 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(stdout, &clientCreationResult); err != nil { + if err := json.Unmarshal(extractJsonResult(stdout), &clientCreationResult); err != nil { return "", err } return clientCreationResult.Result.CreateClient.ClientID, nil @@ -280,7 +294,7 @@ func getClientIdFromStdout(stdout []byte) (string, error) { // getConnectionIDsFromStdout extracts the connectionIDs on both ends from the stdout. func getConnectionIDsFromStdout(stdout []byte) (string, string, error) { var connectionResponse ConnectionResponse - if err := json.Unmarshal(stdout, &connectionResponse); err != nil { + if err := json.Unmarshal(extractJsonResult(stdout), &connectionResponse); err != nil { return "", "", err } return connectionResponse.Result.ASide.ConnectionID, connectionResponse.Result.BSide.ConnectionID, nil diff --git a/relayer/hermes/hermes_types.go b/relayer/hermes/hermes_types.go index 35444cb8f..0a13c286c 100644 --- a/relayer/hermes/hermes_types.go +++ b/relayer/hermes/hermes_types.go @@ -6,7 +6,8 @@ type ClientCreationResponse struct { } type CreateClient struct { - ClientID string `json:"client_id"` + ClientID string `json:"client_id"` + ClientType string `json:"client_type"` } type CreateClientResult struct { From 385369d24278ca8a068d36c4b980d307a348f10e Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 14:52:22 +0000 Subject: [PATCH 28/32] extract json result from stdout --- relayer/hermes/hermes_commander.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index 8d27dd2c4..25f52a848 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -60,8 +60,9 @@ func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelO } func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.ConnectionOutputs, error) { + jsonBz := extractJsonResult([]byte(stdout)) var queryResult ConnectionQueryResult - if err := json.Unmarshal([]byte(stdout), &queryResult); err != nil { + if err := json.Unmarshal(jsonBz, &queryResult); err != nil { return ibc.ConnectionOutputs{}, err } @@ -94,8 +95,9 @@ func (c commander) ParseGetConnectionsOutput(stdout, stderr string) (ibc.Connect } func (c commander) ParseGetClientsOutput(stdout, stderr string) (ibc.ClientOutputs, error) { + jsonBz := extractJsonResult([]byte(stdout)) var queryResult ClientQueryResult - if err := json.Unmarshal([]byte(stdout), &queryResult); err != nil { + if err := json.Unmarshal(jsonBz, &queryResult); err != nil { return ibc.ClientOutputs{}, err } From 136f35f31cb1fd00695d2ef0fcb94f4a75d27193 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Thu, 16 Feb 2023 15:11:33 +0000 Subject: [PATCH 29/32] correct channel parsing json stdout --- relayer/hermes/hermes_commander.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index 25f52a848..fb06f6aaa 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -35,8 +35,9 @@ func (c commander) DockerUser() string { } func (c commander) ParseGetChannelsOutput(stdout, stderr string) ([]ibc.ChannelOutput, error) { + jsonBz := extractJsonResult([]byte(stdout)) var result ChannelOutputResult - if err := json.Unmarshal([]byte(stdout), &result); err != nil { + if err := json.Unmarshal(jsonBz, &result); err != nil { return nil, err } From 5366e6ffe8cc68a6a0da33ab030d440c6f8f6c17 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 17 Feb 2023 13:27:24 +0000 Subject: [PATCH 30/32] set field ClearOnStart to true --- conformance/test.go | 4 +++- relayer/hermes/hermes_commander.go | 7 ++++--- relayer/hermes/hermes_config.go | 2 +- relayer/hermes/hermes_relayer.go | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/conformance/test.go b/conformance/test.go index 5b0f794c8..216df3b7e 100644 --- a/conformance/test.go +++ b/conformance/test.go @@ -35,7 +35,6 @@ import ( "testing" "time" - transfertypes "github.com/cosmos/ibc-go/v6/modules/apps/transfer/types" "github.com/docker/docker/client" interchaintest "github.com/strangelove-ventures/interchaintest/v6" "github.com/strangelove-ventures/interchaintest/v6/chain/cosmos" @@ -47,6 +46,8 @@ import ( "github.com/strangelove-ventures/interchaintest/v6/testutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" + + transfertypes "github.com/cosmos/ibc-go/v6/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/relayer/hermes/hermes_commander.go b/relayer/hermes/hermes_commander.go index fb06f6aaa..38e3b95b9 100644 --- a/relayer/hermes/hermes_commander.go +++ b/relayer/hermes/hermes_commander.go @@ -3,6 +3,7 @@ package hermes import ( "context" "encoding/json" + "fmt" "github.com/strangelove-ventures/interchaintest/v6/ibc" "github.com/strangelove-ventures/interchaintest/v6/relayer" @@ -126,15 +127,15 @@ func (c commander) GetChannels(chainID, homeDir string) []string { } func (c commander) GetConnections(chainID, homeDir string) []string { - return []string{hermes, "--json", "query", "connections", "--chain", chainID, "--verbose"} + 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, "--json", "query", "clients", "--host-chain", chainID} + 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, "--json", "start", "--full-scan"} + return []string{hermes, "--config", fmt.Sprintf("%s/%s", homeDir, hermesConfigPath), "start", "--full-scan"} } func (c commander) CreateWallet(keyName, address, mnemonic string) ibc.Wallet { diff --git a/relayer/hermes/hermes_config.go b/relayer/hermes/hermes_config.go index 1a73ff6fa..50e1b9695 100644 --- a/relayer/hermes/hermes_config.go +++ b/relayer/hermes/hermes_config.go @@ -71,7 +71,7 @@ func NewConfig(chainConfigs ...ChainConfig) Config { Packets: Packets{ Enabled: true, ClearInterval: 0, - ClearOnStart: false, + ClearOnStart: true, TxConfirmation: false, }, }, diff --git a/relayer/hermes/hermes_relayer.go b/relayer/hermes/hermes_relayer.go index a37e61bbf..4525ee4b6 100644 --- a/relayer/hermes/hermes_relayer.go +++ b/relayer/hermes/hermes_relayer.go @@ -196,7 +196,7 @@ func (r *Relayer) RestoreKey(ctx context.Context, rep ibc.RelayerExecReporter, c 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)} + 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. From e066e9039b129ae57ac73d80a69bd37d06277272 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 17 Feb 2023 13:36:56 +0000 Subject: [PATCH 31/32] switch back to go relayer as default --- cmd/interchaintest/interchaintest_test.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index 6726b5fae..f65cf9882 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -8,6 +8,7 @@ import ( "fmt" "math/rand" "os" + "path" "path/filepath" "strings" "testing" @@ -111,18 +112,18 @@ var extraFlags mainFlags // the parsed contents of the file referenced by the matrix flag, // or with a small reasonable default of rly against one gaia-osmosis set. func setUpTestMatrix() error { - //if extraFlags.MatrixFile == "" && isRunningInCI() { - // currentDir, err := os.Getwd() - // if err != nil { - // return err - // } - // extraFlags.MatrixFile = path.Join(currentDir, defaultRelativeCIMatrixFile) - //} + if extraFlags.MatrixFile == "" && isRunningInCI() { + currentDir, err := os.Getwd() + if err != nil { + return err + } + extraFlags.MatrixFile = path.Join(currentDir, defaultRelativeCIMatrixFile) + } if extraFlags.MatrixFile == "" { fmt.Fprintln(os.Stderr, "No matrix file provided, falling back to rly with gaia and osmosis") - testMatrix.Relayers = []string{"hermes"} + testMatrix.Relayers = []string{"rly"} testMatrix.ChainSets = [][]*interchaintest.ChainSpec{ { {Name: "gaia", Version: "v7.0.1"}, From b7797b15b09c8c6920d624e83ef38d7c02a230d6 Mon Sep 17 00:00:00 2001 From: Cian Hatton Date: Fri, 17 Feb 2023 14:21:02 +0000 Subject: [PATCH 32/32] add hermes to the default relayers list --- cmd/interchaintest/interchaintest_test.go | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/cmd/interchaintest/interchaintest_test.go b/cmd/interchaintest/interchaintest_test.go index f65cf9882..16a2ee13e 100644 --- a/cmd/interchaintest/interchaintest_test.go +++ b/cmd/interchaintest/interchaintest_test.go @@ -8,9 +8,7 @@ import ( "fmt" "math/rand" "os" - "path" "path/filepath" - "strings" "testing" "time" @@ -26,10 +24,6 @@ import ( "go.uber.org/zap" ) -const ( - defaultRelativeCIMatrixFile = "example_matrix.json" -) - func init() { // Because we use the test binary, we use this hack to customize the help usage. flag.Usage = func() { @@ -100,30 +94,16 @@ func TestMain(m *testing.M) { os.Exit(code) } -// isRunningInCI returns true if this test is running in CI. -// Note: On Github, all runners are passed an environment variable of "CI" with a value of "true". -func isRunningInCI() bool { - return strings.ToLower(os.Getenv("CI")) == "true" -} - var extraFlags mainFlags // setUpTestMatrix populates the testMatrix singleton with // the parsed contents of the file referenced by the matrix flag, // or with a small reasonable default of rly against one gaia-osmosis set. func setUpTestMatrix() error { - if extraFlags.MatrixFile == "" && isRunningInCI() { - currentDir, err := os.Getwd() - if err != nil { - return err - } - extraFlags.MatrixFile = path.Join(currentDir, defaultRelativeCIMatrixFile) - } - 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"},