From 8201ef9b18e5ffbd66f2f5131be088582d88126a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?colin=20axn=C3=A9r?= <25233464+colin-axner@users.noreply.github.com> Date: Mon, 15 Aug 2022 12:59:10 +0200 Subject: [PATCH] feat(test): add successful e2e transfer test (#1973) ## Description closes: #1963 --- Before we can merge this PR, please make sure that all the following items have been checked off. If any of the checklist items are not applicable, please leave them but write a little note why. - [x] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/ibc-go/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [ ] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes --- .github/workflows/e2e-manual.yaml | 1 + e2e/testsuite/testsuite.go | 7 ++ e2e/transfer_test.go | 129 ++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 e2e/transfer_test.go diff --git a/.github/workflows/e2e-manual.yaml b/.github/workflows/e2e-manual.yaml index 9aa3679fc09..519c3b5d4f4 100644 --- a/.github/workflows/e2e-manual.yaml +++ b/.github/workflows/e2e-manual.yaml @@ -10,6 +10,7 @@ on: required: true type: choice options: + - TestTransferTestSuite - TestFeeMiddlewareTestSuite chain-a-image: description: 'The image to use for chain A' diff --git a/e2e/testsuite/testsuite.go b/e2e/testsuite/testsuite.go index 979dcb617ce..16132793578 100644 --- a/e2e/testsuite/testsuite.go +++ b/e2e/testsuite/testsuite.go @@ -292,6 +292,13 @@ func (s *E2ETestSuite) AssertValidTxResponse(resp sdk.TxResponse) { s.Require().NotEmpty(resp.Data, respLogsMsg) } +// AssertPacketRelayed asserts that the packet commitment does not exist on the sending chain. +// The packet commitment will be deleted upon a packet acknowledgement or timeout. +func (s *E2ETestSuite) AssertPacketRelayed(ctx context.Context, chain *cosmos.CosmosChain, portID, channelID string, sequence uint64) { + commitment, _ := s.QueryPacketCommitment(ctx, chain, portID, channelID, sequence) + s.Require().Empty(commitment) +} + // createCosmosChains creates two separate chains in docker containers. // test and can be retrieved with GetChains. func (s *E2ETestSuite) createCosmosChains(chainOptions testconfig.ChainOptions) (*cosmos.CosmosChain, *cosmos.CosmosChain) { diff --git a/e2e/transfer_test.go b/e2e/transfer_test.go new file mode 100644 index 00000000000..01c112cb66f --- /dev/null +++ b/e2e/transfer_test.go @@ -0,0 +1,129 @@ +package e2e + +/* +The TransferTestSuite assumes both chainA and chainB support version ics20-1. +*/ + +import ( + "context" + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "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" + + "github.com/cosmos/ibc-go/e2e/testsuite" + "github.com/cosmos/ibc-go/e2e/testvalues" + transfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v5/modules/core/02-client/types" +) + +func TestTransferTestSuite(t *testing.T) { + suite.Run(t, new(TransferTestSuite)) +} + +type TransferTestSuite struct { + testsuite.E2ETestSuite +} + +// Transfer broadcasts a MsgTransfer message. +func (s *TransferTestSuite) Transfer(ctx context.Context, chain *cosmos.CosmosChain, user *ibctest.User, + portID, channelID string, token sdk.Coin, sender, receiver string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, +) (sdk.TxResponse, error) { + msg := transfertypes.NewMsgTransfer(portID, channelID, token, sender, receiver, timeoutHeight, timeoutTimestamp) + return s.BroadcastMessages(ctx, chain, user, msg) +} + +// TestMsgTransfer_Succeeds_Nonincentivized will test sending successful IBC transfers from chainA to chainB. +// The transfer will occur over a basic transfer channel (non incentivized) and both native and non-native tokens +// will be sent forwards and backwards in the IBC transfer timeline (both chains will act as source and receiver chains). +func (s *TransferTestSuite) TestMsgTransfer_Succeeds_Nonincentivized() { + t := s.T() + ctx := context.TODO() + + relayer, channelA := s.SetupChainsRelayerAndChannel(ctx, transferChannelOptions()) + chainA, chainB := s.GetChains() + + chainADenom := chainA.Config().Denom + + chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount) + chainAAddress := chainAWallet.Bech32Address(chainA.Config().Bech32Prefix) + + chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount) + chainBAddress := chainBWallet.Bech32Address(chainB.Config().Bech32Prefix) + + s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks") + + t.Run("native IBC token transfer from chainA to chainB, sender is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0) + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount - testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("start relayer", func(t *testing.T) { + s.StartRelayer(relayer) + }) + + chainBIBCToken := s.getIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID) + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1) + + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + expected := testvalues.IBCTransferAmount + s.Require().Equal(expected, actualBalance) + }) + + t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) { + transferTxResp, err := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0) + s.Require().NoError(err) + s.AssertValidTxResponse(transferTxResp) + }) + + t.Run("tokens are escrowed", func(t *testing.T) { + actualBalance, err := chainB.GetBalance(ctx, chainBAddress, chainBIBCToken.IBCDenom()) + s.Require().NoError(err) + + s.Require().Equal(int64(0), actualBalance) + }) + + s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks") + + t.Run("packets are relayed", func(t *testing.T) { + s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1) + + actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet) + s.Require().NoError(err) + + expected := testvalues.StartingTokenAmount + s.Require().Equal(expected, actualBalance) + }) +} + +// transferChannelOptions configures both of the chains to have non-incentivized transfer channels. +func transferChannelOptions() func(options *ibc.CreateChannelOptions) { + return func(opts *ibc.CreateChannelOptions) { + opts.Version = transfertypes.Version + opts.SourcePortName = transfertypes.PortID + opts.DestPortName = transfertypes.PortID + } +} + +// getIBCToken returns the denomination of the full token denom sent to the receiving channel +func (s *TransferTestSuite) getIBCToken(fullTokenDenom string, portID, channelID string) transfertypes.DenomTrace { + return transfertypes.ParseDenomTrace(fmt.Sprintf("%s/%s/%s", portID, channelID, fullTokenDenom)) +}