Skip to content

Commit

Permalink
E2E: Add ICA MsgSubmitTx tests (success + failure) (#2021)
Browse files Browse the repository at this point in the history
  • Loading branch information
chatton authored Aug 17, 2022
1 parent 0a887e4 commit a9bc591
Show file tree
Hide file tree
Showing 7 changed files with 283 additions and 14 deletions.
1 change: 1 addition & 0 deletions e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alp
require (
github.com/cosmos/cosmos-sdk v0.46.0
github.com/cosmos/ibc-go/v5 v5.0.0-beta1
github.com/cosmos/interchain-accounts v0.3.1-0.20220816085955-393d8444c111
github.com/docker/docker v20.10.17+incompatible
github.com/strangelove-ventures/ibctest v0.0.0-20220808203516-6cbd3743756d
github.com/stretchr/testify v1.8.0
Expand Down
4 changes: 4 additions & 0 deletions e2e/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ github.com/cosmos/iavl v0.19.0 h1:sgyrjqOkycXiN7Tuupuo4QAldKFg7Sipyfeg/IL7cps=
github.com/cosmos/iavl v0.19.0/go.mod h1:l5h9pAB3m5fihB3pXVgwYqdY8aBsMagqz7T0MUjxZeA=
github.com/cosmos/ibc-go/v5 v5.0.0-beta1 h1:YqC9giQlZId8Wui8xpaUFI+TpVmEupQZSoDlmxAu6yI=
github.com/cosmos/ibc-go/v5 v5.0.0-beta1/go.mod h1:9mmcbzuidgX7nhafIKng/XhXAHDEnRqDjGy/60W1cvg=
github.com/cosmos/interchain-accounts v0.3.0 h1:Zu9372ze/a6HhUy5Z4Mu+C+FcnNLGrQ15aPwr6lRTeI=
github.com/cosmos/interchain-accounts v0.3.0/go.mod h1:FYF1IiAz6M/LC1Gb3Jch042IaL0AXOfU+s1ZGvR4IqI=
github.com/cosmos/interchain-accounts v0.3.1-0.20220816085955-393d8444c111 h1:5Tm0jHmyh2XDc/XlIvaFJbIvVQyRMH4EtoLzSJM/MAY=
github.com/cosmos/interchain-accounts v0.3.1-0.20220816085955-393d8444c111/go.mod h1:vNWr9YxBrI5c74jBwk9ooiUCIDvdOJeF8MQEA/z0IEk=
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
Expand Down
225 changes: 225 additions & 0 deletions e2e/interchain_accounts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package e2e

import (
"context"
"testing"

ibctest "github.com/strangelove-ventures/ibctest"
"github.com/strangelove-ventures/ibctest/chain/cosmos"
"github.com/strangelove-ventures/ibctest/ibc"
"github.com/strangelove-ventures/ibctest/test"
"github.com/stretchr/testify/suite"

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types"

"github.com/cosmos/ibc-go/e2e/testconfig"
"github.com/cosmos/ibc-go/e2e/testsuite"
"github.com/cosmos/ibc-go/e2e/testvalues"
ibctesting "github.com/cosmos/ibc-go/v5/testing"
)

func TestInterchainAccountsTestSuite(t *testing.T) {
// NOTE: this is a temporary mechanism to enable this test to run alongside the simd tests.
// This will be removed in a follow up PR and properly parameterized in a github workflow.
testconfig.SetChainBinaryVersions(
"ghcr.io/cosmos/ibc-go-icad", "master", "icad", "ghcr.io/cosmos/ibc-go-icad", "master",
)
suite.Run(t, new(InterchainAccountsTestSuite))
}

type InterchainAccountsTestSuite struct {
testsuite.E2ETestSuite
}

// RegisterInterchainAccount will attempt to register an interchain account on the counterparty chain.
func (s *InterchainAccountsTestSuite) RegisterInterchainAccount(ctx context.Context, chain *cosmos.CosmosChain, user *ibctest.User, msgRegisterAccount *intertxtypes.MsgRegisterAccount) error {
txResp, err := s.BroadcastMessages(ctx, chain, user, msgRegisterAccount)
s.AssertValidTxResponse(txResp)
return err
}

func (s *InterchainAccountsTestSuite) TestMsgSubmitTx_SuccessfulTransfer() {
t := s.T()
ctx := context.TODO()

// setup relayers and connection-0 between two chains
// channel-0 is a transfer channel but it will not be used in this test case
relayer, _ := s.SetupChainsRelayerAndChannel(ctx)
chainA, chainB := s.GetChains()

// setup 2 accounts: controller account on chain A, a second chain B account.
// host account will be created when the ICA is registered
controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
var hostAccount string

t.Run("register interchain account", func(t *testing.T) {
version := "" // allow app to handle the version as appropriate.
msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.Bech32Address(chainA.Config().Bech32Prefix), ibctesting.FirstConnectionID, version)
err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount)
s.Require().NoError(err)
})

t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer)
})

t.Run("verify interchain account", func(t *testing.T) {
var err error
hostAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAccount.Bech32Address(chainA.Config().Bech32Prefix), ibctesting.FirstConnectionID)
s.Require().NoError(err)
s.Require().NotZero(len(hostAccount))

channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID)
s.Require().NoError(err)
s.Require().Equal(len(channels), 2)
})

t.Run("interchain account executes a bank transfer on behalf of the corresponding owner account", func(t *testing.T) {

t.Run("fund interchain account wallet", func(t *testing.T) {
// fund the host account account so it has some $$ to send
err := chainB.SendFunds(ctx, ibctest.FaucetAccountKeyName, ibc.WalletAmount{
Address: hostAccount,
Amount: testvalues.StartingTokenAmount,
Denom: chainB.Config().Denom,
})
s.Require().NoError(err)
})

t.Run("broadcast MsgSubmitTx", func(t *testing.T) {
// assemble bank transfer message from host account to user account on host chain
msgSend := &banktypes.MsgSend{
FromAddress: hostAccount,
ToAddress: chainBAccount.Bech32Address(chainB.Config().Bech32Prefix),
Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)),
}

// assemble submitMessage tx for intertx
msgSubmitTx, err := intertxtypes.NewMsgSubmitTx(
msgSend,
ibctesting.FirstConnectionID,
controllerAccount.Bech32Address(chainA.Config().Bech32Prefix),
)
s.Require().NoError(err)

// broadcast submitMessage tx from controller account on chain A
// this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host)
// this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain.
resp, err := s.BroadcastMessages(
ctx,
chainA,
controllerAccount,
msgSubmitTx,
)

s.AssertValidTxResponse(resp)
s.Require().NoError(err)

s.Require().NoError(test.WaitForBlocks(ctx, 10, chainA, chainB))
})

t.Run("verify tokens transferred", func(t *testing.T) {
balance, err := chainB.GetBalance(ctx, chainBAccount.Bech32Address(chainB.Config().Bech32Prefix), chainB.Config().Denom)
s.Require().NoError(err)

_, err = chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom)
s.Require().NoError(err)

expected := testvalues.IBCTransferAmount + testvalues.StartingTokenAmount
s.Require().Equal(expected, balance)
})
})
}

func (s *InterchainAccountsTestSuite) TestMsgSubmitTx_FailedTransfer_InsufficientFunds() {
t := s.T()
ctx := context.TODO()

// setup relayers and connection-0 between two chains
// channel-0 is a transfer channel but it will not be used in this test case
relayer, _ := s.SetupChainsRelayerAndChannel(ctx)
chainA, chainB := s.GetChains()

// setup 2 accounts: controller account on chain A, a second chain B account.
// host account will be created when the ICA is registered
controllerAccount := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
chainBAccount := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
var hostAccount string

t.Run("register interchain account", func(t *testing.T) {
version := "" // allow app to handle the version as appropriate.
msgRegisterAccount := intertxtypes.NewMsgRegisterAccount(controllerAccount.Bech32Address(chainA.Config().Bech32Prefix), ibctesting.FirstConnectionID, version)
err := s.RegisterInterchainAccount(ctx, chainA, controllerAccount, msgRegisterAccount)
s.Require().NoError(err)
})

t.Run("start relayer", func(t *testing.T) {
s.StartRelayer(relayer)
})

t.Run("verify interchain account", func(t *testing.T) {
var err error
hostAccount, err = s.QueryInterchainAccount(ctx, chainA, controllerAccount.Bech32Address(chainA.Config().Bech32Prefix), ibctesting.FirstConnectionID)
s.Require().NoError(err)
s.Require().NotZero(len(hostAccount))

channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID)
s.Require().NoError(err)
s.Require().Equal(len(channels), 2)
})

t.Run("fail to execute bank transfer over ICA", func(t *testing.T) {
t.Run("verify empty host wallet", func(t *testing.T) {
hostAccountBalance, err := chainB.GetBalance(ctx, hostAccount, chainB.Config().Denom)
s.Require().NoError(err)
s.Require().Zero(hostAccountBalance)
})

t.Run("broadcast MsgSubmitTx", func(t *testing.T) {
// assemble bank transfer message from host account to user account on host chain
transferMsg := &banktypes.MsgSend{
FromAddress: hostAccount,
ToAddress: chainBAccount.Bech32Address(chainB.Config().Bech32Prefix),
Amount: sdk.NewCoins(testvalues.DefaultTransferAmount(chainB.Config().Denom)),
}

// assemble submitMessage tx for intertx
submitMsg, err := intertxtypes.NewMsgSubmitTx(
transferMsg,
ibctesting.FirstConnectionID,
controllerAccount.Bech32Address(chainA.Config().Bech32Prefix),
)
s.Require().NoError(err)

// broadcast submitMessage tx from controller account on chain A
// this message should trigger the sending of an ICA packet over channel-1 (channel created between controller and host)
// this ICA packet contains the assembled bank transfer message from above, which will be executed by the host account on the host chain.
resp, err := s.BroadcastMessages(
ctx,
chainA,
controllerAccount,
submitMsg,
)

s.AssertValidTxResponse(resp)
s.Require().NoError(err)
})

t.Run("packets are relayed", func(t *testing.T) {
channels, err := relayer.GetChannels(ctx, s.GetRelayerExecReporter(), chainA.Config().ChainID)
s.Require().NoError(err)
s.AssertPacketRelayed(ctx, chainA, channels[1].PortID, channels[1].ChannelID, 1)
})

t.Run("verify balance is the same", func(t *testing.T) {
balance, err := chainB.GetBalance(ctx, chainBAccount.Bech32Address(chainB.Config().Bech32Prefix), chainB.Config().Denom)
s.Require().NoError(err)

expected := testvalues.StartingTokenAmount
s.Require().Equal(expected, balance)
})
})
}
42 changes: 32 additions & 10 deletions e2e/testconfig/testconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@ const (
// ChainBSimdTagEnv specifies the tag that Chain B will use. If unspecified
// the value will default to the same value as Chain A.
ChainBSimdTagEnv = "CHAIN_B_SIMD_TAG"
GoRelayerTagEnv = "RLY_TAG"

// GoRelayerTagEnv specifies the go relayer version. Defaults to "main"
GoRelayerTagEnv = "RLY_TAG"
// ChainBinary binary is the binary that will be used for the chains.
ChainBinary = "CHAIN_BINARY"
// defaultBinary is the default binary that will be used by the chains.
defaultBinary = "simd"
// defaultSimdImage is the default image that will be used for the chain if none are specified.
defaultSimdImage = "ghcr.io/cosmos/ibc-go-simd"
defaultRlyTag = "main"
)
Expand All @@ -32,20 +37,26 @@ type TestConfig struct {
}

type ChainConfig struct {
Image string
Tag string
Image string
Tag string
Binary string
}

// FromEnv returns a TestConfig constructed from environment variables.
func FromEnv() TestConfig {
chainBinary, ok := os.LookupEnv(ChainBinary)
if !ok {
chainBinary = defaultBinary
}

chainASimdImage, ok := os.LookupEnv(ChainASimdImageEnv)
if !ok {
chainASimdImage = defaultSimdImage
}

chainASimdTag, ok := os.LookupEnv(ChainASimdTagEnv)
if !ok {
panic(fmt.Sprintf("must specify simd version for test with environment variable [%s]", ChainASimdTagEnv))
panic(fmt.Sprintf("must specify %s version for test with environment variable [%s]", chainBinary, ChainASimdTagEnv))
}

chainBSimdImage, ok := os.LookupEnv(ChainBSimdImageEnv)
Expand All @@ -65,12 +76,14 @@ func FromEnv() TestConfig {

return TestConfig{
ChainAConfig: ChainConfig{
Image: chainASimdImage,
Tag: chainASimdTag,
Image: chainASimdImage,
Tag: chainASimdTag,
Binary: chainBinary,
},
ChainBConfig: ChainConfig{
Image: chainBSimdImage,
Tag: chainBSimdTag,
Image: chainBSimdImage,
Tag: chainBSimdTag,
Binary: chainBinary,
},
RlyTag: rlyTag,
}
Expand Down Expand Up @@ -111,7 +124,7 @@ func newDefaultSimappConfig(cc ChainConfig, name, chainID, denom string) ibc.Cha
Version: cc.Tag,
},
},
Bin: "simd",
Bin: cc.Binary,
Bech32Prefix: "cosmos",
Denom: denom,
GasPrices: fmt.Sprintf("0.00%s", denom),
Expand All @@ -120,3 +133,12 @@ func newDefaultSimappConfig(cc ChainConfig, name, chainID, denom string) ibc.Cha
NoHostMount: false,
}
}

// SetChainBinaryVersions is a helper function for local cross-version testing
func SetChainBinaryVersions(chainaSimdImg, chainaSimdTag, chainBinary, chainbSimdImg, chainbSimdTag string) {
os.Setenv("CHAIN_A_SIMD_IMAGE", chainaSimdImg)
os.Setenv("CHAIN_A_SIMD_TAG", chainaSimdTag)
os.Setenv("CHAIN_B_SIMD_IMAGE", chainbSimdImg)
os.Setenv("CHAIN_B_SIMD_TAG", chainbSimdTag)
os.Setenv("CHAIN_BINARY", chainBinary)
}
14 changes: 14 additions & 0 deletions e2e/testsuite/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package testsuite
import (
"context"

intertxtypes "github.com/cosmos/interchain-accounts/x/inter-tx/types"
"github.com/strangelove-ventures/ibctest/ibc"

clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types"
Expand Down Expand Up @@ -41,3 +42,16 @@ func (s *E2ETestSuite) QueryPacketCommitment(ctx context.Context, chain ibc.Chai
}
return res.Commitment, nil
}

// QueryInterchainAccount queries the interchain account for the given owner and connectionId.
func (s *E2ETestSuite) QueryInterchainAccount(ctx context.Context, chain ibc.Chain, owner, connectionId string) (string, error) {
queryClient := s.GetChainGRCPClients(chain).ICAQueryClient
res, err := queryClient.InterchainAccount(ctx, &intertxtypes.QueryInterchainAccountRequest{
Owner: owner,
ConnectionId: connectionId,
})
if err != nil {
return "", err
}
return res.InterchainAccountAddress, nil
}
2 changes: 1 addition & 1 deletion e2e/testsuite/relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const (

// newCosmosRelayer returns an instance of the go relayer.
func newCosmosRelayer(t *testing.T, tc testconfig.TestConfig, logger *zap.Logger, dockerClient *dockerclient.Client, network string) ibc.Relayer {
return ibctest.NewBuiltinRelayerFactory(ibc.CosmosRly, logger, relayer.CustomDockerImage(cosmosRelayerRepository, tc.RlyTag)).Build(
return ibctest.NewBuiltinRelayerFactory(ibc.CosmosRly, logger, relayer.CustomDockerImage(cosmosRelayerRepository, tc.RlyTag), relayer.StartupFlags("-p", "events")).Build(
t, dockerClient, network,
)
}
Loading

0 comments on commit a9bc591

Please sign in to comment.