Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
406 lines
16 KiB
Go
406 lines
16 KiB
Go
//go:build !test_e2e
|
|
|
|
package transfer
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cosmos/interchaintest/v10/ibc"
|
|
test "github.com/cosmos/interchaintest/v10/testutil"
|
|
testifysuite "github.com/stretchr/testify/suite"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
|
|
"github.com/cosmos/ibc-go/e2e/testsuite"
|
|
"github.com/cosmos/ibc-go/e2e/testsuite/query"
|
|
"github.com/cosmos/ibc-go/e2e/testvalues"
|
|
transfertypes "git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
|
|
)
|
|
|
|
// compatibility:from_version: v7.10.0
|
|
func TestTransferTestSuite(t *testing.T) {
|
|
testifysuite.Run(t, new(TransferTestSuite))
|
|
}
|
|
|
|
type TransferTestSuite struct {
|
|
transferTester
|
|
}
|
|
|
|
// SetupSuite sets up chains for the current test suite
|
|
func (s *TransferTestSuite) SetupSuite() {
|
|
s.SetupChains(context.TODO(), 2, nil)
|
|
}
|
|
|
|
// transferTester defines some helper functions that can be used in various test suites
|
|
// that test transfer functionality.
|
|
type transferTester struct {
|
|
testsuite.E2ETestSuite
|
|
}
|
|
|
|
// QueryTransferParams queries the on-chain send enabled param for the transfer module
|
|
func (s *transferTester) QueryTransferParams(ctx context.Context, chain ibc.Chain) transfertypes.Params {
|
|
res, err := query.GRPCQuery[transfertypes.QueryParamsResponse](ctx, chain, &transfertypes.QueryParamsRequest{})
|
|
s.Require().NoError(err)
|
|
return *res.Params
|
|
}
|
|
|
|
// CreateTransferPath sets up a path between chainA and chainB with a transfer channel and returns the relayer wired
|
|
// up to watch the channel and port IDs created.
|
|
func (s *transferTester) CreateTransferPath(testName string) (ibc.Relayer, ibc.ChannelOutput) {
|
|
relayer, channel := s.CreatePaths(ibc.DefaultClientOpts(), s.TransferChannelOptions(), testName), s.GetChainAChannelForTest(testName)
|
|
s.T().Logf("test %s running on portID %s channelID %s", testName, channel.PortID, channel.ChannelID)
|
|
return relayer, channel
|
|
}
|
|
|
|
// 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()
|
|
|
|
testName := t.Name()
|
|
|
|
// NOTE: t.Parallel() should be called before SetupPath in all tests.
|
|
// t.Name() must be stored in a variable before t.Parallel() otherwise t.Name() is not
|
|
// deterministic.
|
|
t.Parallel()
|
|
|
|
relayer, channelA := s.CreateTransferPath(testName)
|
|
|
|
chainA, chainB := s.GetChains()
|
|
|
|
chainBVersion := chainB.Config().Images[0].Version
|
|
chainADenom := chainA.Config().Denom
|
|
|
|
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
chainAAddress := chainAWallet.FormattedAddress()
|
|
|
|
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
chainBAddress := chainBWallet.FormattedAddress()
|
|
|
|
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 := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
|
|
s.AssertTxSuccess(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)
|
|
|
|
// TODO: cannot query total escrow if tests in parallel are using the same denom.
|
|
// if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) {
|
|
// actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom)
|
|
// s.Require().NoError(err)
|
|
//
|
|
// expectedTotalEscrow := sdk.NewCoin(chainADenom, sdkmath.NewInt(testvalues.IBCTransferAmount))
|
|
// s.Require().Equal(expectedTotalEscrow, actualTotalEscrow)
|
|
// }
|
|
})
|
|
|
|
t.Run("start relayer", func(t *testing.T) {
|
|
s.StartRelayer(relayer, testName)
|
|
})
|
|
|
|
chainBIBCToken := testsuite.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 := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
|
|
s.Require().NoError(err)
|
|
|
|
expected := testvalues.IBCTransferAmount
|
|
s.Require().Equal(expected, actualBalance.Int64())
|
|
})
|
|
|
|
if testvalues.TokenMetadataFeatureReleases.IsSupported(chainBVersion) {
|
|
t.Run("metadata for IBC denomination exists on chainB", func(t *testing.T) {
|
|
s.AssertHumanReadableDenom(ctx, chainB, chainADenom, channelA)
|
|
})
|
|
}
|
|
|
|
t.Run("non-native IBC token transfer from chainB to chainA, receiver is source of tokens", func(t *testing.T) {
|
|
transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, testvalues.DefaultTransferAmount(chainBIBCToken.IBCDenom()), chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainA), 0, "")
|
|
s.AssertTxSuccess(transferTxResp)
|
|
})
|
|
|
|
t.Run("tokens are escrowed", func(t *testing.T) {
|
|
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal(sdkmath.ZeroInt(), actualBalance)
|
|
|
|
// https://github.com/cosmos/ibc-go/issues/6742
|
|
// if testvalues.TotalEscrowFeatureReleases.IsSupported(chainBVersion) {
|
|
// actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainB, chainBIBCToken.IBCDenom())
|
|
// s.Require().NoError(err)
|
|
// s.Require().Equal(sdk.NewCoin(chainBIBCToken.IBCDenom(), sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because sending chain is not source for tokens
|
|
// }
|
|
})
|
|
|
|
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)
|
|
})
|
|
|
|
// https://github.com/cosmos/ibc-go/issues/6742
|
|
// if testvalues.TotalEscrowFeatureReleases.IsSupported(chainAVersion) {
|
|
// t.Run("tokens are un-escrowed", func(t *testing.T) {
|
|
// actualTotalEscrow, err := query.TotalEscrowForDenom(ctx, chainA, chainADenom)
|
|
// s.Require().NoError(err)
|
|
// s.Require().Equal(sdk.NewCoin(chainADenom, sdkmath.NewInt(0)), actualTotalEscrow) // total escrow is zero because tokens have come back
|
|
// })
|
|
// }
|
|
}
|
|
|
|
// TestMsgTransfer_Fails_InvalidAddress attempts to send an IBC transfer to an invalid address and ensures
|
|
// that the tokens on the sending chain are unescrowed.
|
|
func (s *TransferTestSuite) TestMsgTransfer_Fails_InvalidAddress() {
|
|
t := s.T()
|
|
ctx := context.TODO()
|
|
|
|
testName := t.Name()
|
|
t.Parallel()
|
|
relayer, channelA := s.CreateTransferPath(testName)
|
|
|
|
chainA, chainB := s.GetChains()
|
|
|
|
chainADenom := chainA.Config().Denom
|
|
|
|
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
chainAAddress := chainAWallet.FormattedAddress()
|
|
|
|
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
|
|
|
|
t.Run("native IBC token transfer from chainA to invalid address", func(t *testing.T) {
|
|
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, testvalues.InvalidAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
|
|
s.AssertTxSuccess(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, testName)
|
|
})
|
|
|
|
t.Run("packets are relayed", func(t *testing.T) {
|
|
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
|
|
})
|
|
|
|
t.Run("token transfer amount unescrowed", func(t *testing.T) {
|
|
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
|
|
s.Require().NoError(err)
|
|
|
|
expected := testvalues.StartingTokenAmount
|
|
s.Require().Equal(expected, actualBalance)
|
|
})
|
|
}
|
|
|
|
func (s *TransferTestSuite) TestMsgTransfer_Timeout_Nonincentivized() {
|
|
t := s.T()
|
|
ctx := context.TODO()
|
|
|
|
testName := t.Name()
|
|
t.Parallel()
|
|
relayer, channelA := s.CreateTransferPath(testName)
|
|
|
|
chainA, _ := s.GetChains()
|
|
|
|
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
|
|
chainBWalletAmount := ibc.WalletAmount{
|
|
Address: chainBWallet.FormattedAddress(), // destination address
|
|
Denom: chainA.Config().Denom,
|
|
Amount: sdkmath.NewInt(testvalues.IBCTransferAmount),
|
|
}
|
|
|
|
t.Run("IBC transfer packet timesout", func(t *testing.T) {
|
|
tx, err := chainA.SendIBCTransfer(ctx, channelA.ChannelID, chainAWallet.KeyName(), chainBWalletAmount, ibc.TransferOptions{Timeout: testvalues.ImmediatelyTimeout()})
|
|
s.Require().NoError(err)
|
|
s.Require().NoError(tx.Validate(), "source ibc transfer tx is invalid")
|
|
time.Sleep(time.Nanosecond * 1) // want it to timeout immediately
|
|
})
|
|
|
|
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, testName)
|
|
})
|
|
|
|
t.Run("ensure escrowed tokens have been refunded to sender due to timeout", func(t *testing.T) {
|
|
// ensure destination address did not receive any tokens
|
|
bal, err := s.GetChainBNativeBalance(ctx, chainBWallet)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.StartingTokenAmount, bal)
|
|
|
|
// ensure that the sender address has been successfully refunded the full amount
|
|
bal, err = s.GetChainANativeBalance(ctx, chainAWallet)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.StartingTokenAmount, bal)
|
|
})
|
|
}
|
|
|
|
// This can be used to test sending with a transfer packet with a memo given different combinations of
|
|
// ibc-go versions.
|
|
//
|
|
// TestMsgTransfer_WithMemo will test sending IBC transfers from chainA to chainB
|
|
// If the chains contain a version of FungibleTokenPacketData with memo, both send and receive should succeed.
|
|
// If one of the chains contains a version of FungibleTokenPacketData without memo, then receiving a packet with
|
|
// memo should fail in that chain
|
|
func (s *TransferTestSuite) TestMsgTransfer_WithMemo() {
|
|
t := s.T()
|
|
ctx := context.TODO()
|
|
|
|
testName := t.Name()
|
|
t.Parallel()
|
|
relayer, channelA := s.CreateTransferPath(testName)
|
|
|
|
chainA, chainB := s.GetChains()
|
|
|
|
chainADenom := chainA.Config().Denom
|
|
|
|
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
chainAAddress := chainAWallet.FormattedAddress()
|
|
|
|
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
chainBAddress := chainBWallet.FormattedAddress()
|
|
|
|
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
|
|
|
|
t.Run("IBC token transfer with memo from chainA to chainB", func(t *testing.T) {
|
|
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, testvalues.DefaultTransferAmount(chainADenom), chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainB), 0, "memo")
|
|
s.AssertTxSuccess(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, testName)
|
|
})
|
|
|
|
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
|
|
|
|
t.Run("packets relayed", func(t *testing.T) {
|
|
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
|
|
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.IBCTransferAmount, actualBalance.Int64())
|
|
})
|
|
}
|
|
|
|
// TestMsgTransfer_EntireBalance tests that it is possible to transfer the entire balance
|
|
// of a given denom by using types.UnboundedSpendLimit as the amount.
|
|
// compatibility:TestMsgTransfer_EntireBalance:from_versions: v7.10.0,v8.7.0,v10.0.0
|
|
func (s *TransferTestSuite) TestMsgTransfer_EntireBalance() {
|
|
t := s.T()
|
|
ctx := context.TODO()
|
|
|
|
testName := t.Name()
|
|
t.Parallel()
|
|
relayer, channelA := s.CreateTransferPath(testName)
|
|
|
|
chainA, chainB := s.GetChains()
|
|
|
|
chainADenom := chainA.Config().Denom
|
|
|
|
chainAWallet := s.CreateUserOnChainA(ctx, testvalues.StartingTokenAmount)
|
|
chainAAddress := chainAWallet.FormattedAddress()
|
|
|
|
chainBWallet := s.CreateUserOnChainB(ctx, testvalues.StartingTokenAmount)
|
|
chainBAddress := chainBWallet.FormattedAddress()
|
|
|
|
coinFromA := testvalues.DefaultTransferAmount(chainADenom)
|
|
|
|
s.Require().NoError(test.WaitForBlocks(ctx, 1, chainA, chainB), "failed to wait for blocks")
|
|
|
|
t.Run("IBC token transfer from chainA to chainB", func(t *testing.T) {
|
|
transferTxResp := s.Transfer(ctx, chainA, chainAWallet, channelA.PortID, channelA.ChannelID, coinFromA, chainAAddress, chainBAddress, s.GetTimeoutHeight(ctx, chainA), 0, "")
|
|
s.AssertTxSuccess(transferTxResp)
|
|
})
|
|
|
|
t.Run("tokens are escrowed", func(t *testing.T) {
|
|
actualBalance, err := s.GetChainANativeBalance(ctx, chainAWallet)
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance)
|
|
})
|
|
|
|
t.Run("start relayer", func(t *testing.T) {
|
|
s.StartRelayer(relayer, testName)
|
|
})
|
|
|
|
chainBIBCToken := testsuite.GetIBCToken(chainADenom, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID)
|
|
t.Run("packets relayed", func(t *testing.T) {
|
|
s.AssertPacketRelayed(ctx, chainA, channelA.PortID, channelA.ChannelID, 1)
|
|
actualBalance, err := query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(coinFromA.Amount.Int64(), actualBalance.Int64())
|
|
|
|
actualBalance, err = query.Balance(ctx, chainA, chainAAddress, chainADenom)
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.StartingTokenAmount-coinFromA.Amount.Int64(), actualBalance.Int64())
|
|
})
|
|
|
|
t.Run("send entire balance from B to A", func(t *testing.T) {
|
|
transferCoin := sdk.NewCoin(chainBIBCToken.IBCDenom(), transfertypes.UnboundedSpendLimit())
|
|
|
|
transferTxResp := s.Transfer(ctx, chainB, chainBWallet, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, transferCoin, chainBAddress, chainAAddress, s.GetTimeoutHeight(ctx, chainB), 0, "")
|
|
s.AssertTxSuccess(transferTxResp)
|
|
})
|
|
|
|
s.Require().NoError(test.WaitForBlocks(ctx, 5, chainA, chainB), "failed to wait for blocks")
|
|
|
|
t.Run("packets relayed", func(t *testing.T) {
|
|
// test that chainA has the entire balance back of its native token.
|
|
s.AssertPacketRelayed(ctx, chainB, channelA.Counterparty.PortID, channelA.Counterparty.ChannelID, 1)
|
|
actualBalance, err := query.Balance(ctx, chainA, chainAAddress, chainADenom)
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(testvalues.StartingTokenAmount, actualBalance.Int64())
|
|
|
|
// test that chainB has a zero balance of chainA's IBC token denom.
|
|
actualBalance, err = query.Balance(ctx, chainB, chainBAddress, chainBIBCToken.IBCDenom())
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Zero(actualBalance.Int64())
|
|
})
|
|
}
|