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
201 lines
8.5 KiB
Go
201 lines
8.5 KiB
Go
package transfer_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
testifysuite "github.com/stretchr/testify/suite"
|
|
|
|
sdkmath "cosmossdk.io/math"
|
|
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/apps/transfer/types"
|
|
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
|
|
ibctesting "git.cw.tr/mukan-network/mukan-ibc/testing"
|
|
)
|
|
|
|
type TransferTestSuite struct {
|
|
testifysuite.Suite
|
|
|
|
coordinator *ibctesting.Coordinator
|
|
|
|
// testing chains used for convenience and readability
|
|
chainA *ibctesting.TestChain
|
|
chainB *ibctesting.TestChain
|
|
chainC *ibctesting.TestChain
|
|
}
|
|
|
|
func (suite *TransferTestSuite) SetupTest() {
|
|
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 3)
|
|
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(1))
|
|
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(2))
|
|
suite.chainC = suite.coordinator.GetChain(ibctesting.GetChainID(3))
|
|
}
|
|
|
|
// Constructs the following sends based on the established channels/connections
|
|
// 1 - from chainA to chainB
|
|
// 2 - from chainB to chainC
|
|
// 3 - from chainC to chainB
|
|
func (suite *TransferTestSuite) TestHandleMsgTransfer() {
|
|
var (
|
|
sourceDenomToTransfer string
|
|
msgAmount sdkmath.Int
|
|
)
|
|
|
|
testCases := []struct {
|
|
name string
|
|
malleate func()
|
|
}{
|
|
{
|
|
"transfer single denom",
|
|
func() {},
|
|
},
|
|
{
|
|
"transfer amount larger than int64",
|
|
func() {
|
|
var ok bool
|
|
msgAmount, ok = sdkmath.NewIntFromString("9223372036854775808") // 2^63 (one above int64)
|
|
suite.Require().True(ok)
|
|
},
|
|
},
|
|
{
|
|
"transfer entire balance",
|
|
func() {
|
|
msgAmount = types.UnboundedSpendLimit()
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
suite.Run(tc.name, func() {
|
|
suite.SetupTest() // reset
|
|
|
|
// setup between chainA and chainB
|
|
// NOTE:
|
|
// pathAToB.EndpointA = endpoint on chainA
|
|
// pathAToB.EndpointB = endpoint on chainB
|
|
pathAToB := ibctesting.NewTransferPath(suite.chainA, suite.chainB)
|
|
pathAToB.Setup()
|
|
traceAToB := types.NewHop(pathAToB.EndpointB.ChannelConfig.PortID, pathAToB.EndpointB.ChannelID)
|
|
|
|
sourceDenomToTransfer = sdk.DefaultBondDenom
|
|
msgAmount = ibctesting.DefaultCoinAmount
|
|
|
|
tc.malleate()
|
|
|
|
originalBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), sourceDenomToTransfer)
|
|
|
|
timeoutHeight := clienttypes.NewHeight(1, 110)
|
|
|
|
originalCoin := sdk.NewCoin(sourceDenomToTransfer, msgAmount)
|
|
|
|
// send from chainA to chainB
|
|
msg := types.NewMsgTransfer(pathAToB.EndpointA.ChannelConfig.PortID, pathAToB.EndpointA.ChannelID, originalCoin, suite.chainA.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
|
|
res, err := suite.chainA.SendMsgs(msg)
|
|
suite.Require().NoError(err) // message committed
|
|
|
|
packet, err := ibctesting.ParseV1PacketFromEvents(res.Events)
|
|
suite.Require().NoError(err)
|
|
|
|
// Get the packet data to determine the amount of tokens being transferred (needed for sending entire balance)
|
|
packetData, err := types.UnmarshalPacketData(packet.GetData(), pathAToB.EndpointA.GetChannel().Version, "")
|
|
suite.Require().NoError(err)
|
|
transferAmount, ok := sdkmath.NewIntFromString(packetData.Token.Amount)
|
|
suite.Require().True(ok)
|
|
|
|
// relay send
|
|
err = pathAToB.RelayPacket(packet)
|
|
suite.Require().NoError(err) // relay committed
|
|
|
|
escrowAddress := types.GetEscrowAddress(packet.GetSourcePort(), packet.GetSourceChannel())
|
|
// check that the balance for chainA is updated
|
|
chainABalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), originalCoin.Denom)
|
|
suite.Require().True(originalBalance.Amount.Sub(transferAmount).Equal(chainABalance.Amount))
|
|
|
|
// check that module account escrow address has locked the tokens
|
|
chainAEscrowBalance := suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, originalCoin.Denom)
|
|
suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount))
|
|
|
|
// check that voucher exists on chain B
|
|
chainBDenom := types.NewDenom(originalCoin.Denom, traceAToB)
|
|
chainBBalance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), chainBDenom.IBCDenom())
|
|
coinSentFromAToB := sdk.NewCoin(chainBDenom.IBCDenom(), transferAmount)
|
|
suite.Require().Equal(coinSentFromAToB, chainBBalance)
|
|
|
|
// setup between chainB to chainC
|
|
// NOTE:
|
|
// pathBToC.EndpointA = endpoint on chainB
|
|
// pathBToC.EndpointB = endpoint on chainC
|
|
pathBToC := ibctesting.NewTransferPath(suite.chainB, suite.chainC)
|
|
pathBToC.Setup()
|
|
traceBToC := types.NewHop(pathBToC.EndpointB.ChannelConfig.PortID, pathBToC.EndpointB.ChannelID)
|
|
|
|
// send from chainB to chainC
|
|
msg = types.NewMsgTransfer(pathBToC.EndpointA.ChannelConfig.PortID, pathBToC.EndpointA.ChannelID, coinSentFromAToB, suite.chainB.SenderAccount.GetAddress().String(), suite.chainC.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
|
|
res, err = suite.chainB.SendMsgs(msg)
|
|
suite.Require().NoError(err) // message committed
|
|
|
|
packet, err = ibctesting.ParseV1PacketFromEvents(res.Events)
|
|
suite.Require().NoError(err)
|
|
|
|
err = pathBToC.RelayPacket(packet)
|
|
suite.Require().NoError(err) // relay committed
|
|
|
|
coinsSentFromBToC := sdk.NewCoins()
|
|
// check balances for chainB and chainC after transfer from chainB to chainC
|
|
// NOTE: fungible token is prefixed with the full trace in order to verify the packet commitment
|
|
chainCDenom := types.NewDenom(originalCoin.Denom, traceBToC, traceAToB)
|
|
|
|
// check that the balance is updated on chainC
|
|
coinSentFromBToC := sdk.NewCoin(chainCDenom.IBCDenom(), transferAmount)
|
|
chainCBalance := suite.chainC.GetSimApp().BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coinSentFromBToC.Denom)
|
|
suite.Require().Equal(coinSentFromBToC, chainCBalance)
|
|
|
|
// check that balance on chain B is empty
|
|
chainBBalance = suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), coinSentFromBToC.Denom)
|
|
suite.Require().Zero(chainBBalance.Amount.Int64())
|
|
|
|
// send from chainC back to chainB
|
|
msg = types.NewMsgTransfer(pathBToC.EndpointB.ChannelConfig.PortID, pathBToC.EndpointB.ChannelID, coinSentFromBToC, suite.chainC.SenderAccount.GetAddress().String(), suite.chainB.SenderAccount.GetAddress().String(), timeoutHeight, 0, "")
|
|
res, err = suite.chainC.SendMsgs(msg)
|
|
suite.Require().NoError(err) // message committed
|
|
|
|
packet, err = ibctesting.ParseV1PacketFromEvents(res.Events)
|
|
suite.Require().NoError(err)
|
|
|
|
err = pathBToC.RelayPacket(packet)
|
|
suite.Require().NoError(err) // relay committed
|
|
|
|
// check balances for chainC are empty after transfer from chainC to chainB
|
|
for _, coin := range coinsSentFromBToC {
|
|
// check that balance on chain C is empty
|
|
chainCBalance := suite.chainC.GetSimApp().BankKeeper.GetBalance(suite.chainC.GetContext(), suite.chainC.SenderAccount.GetAddress(), coin.Denom)
|
|
suite.Require().Zero(chainCBalance.Amount.Int64())
|
|
}
|
|
|
|
// check balances for chainB after transfer from chainC to chainB
|
|
// check that balance on chain B has the transferred amount
|
|
chainBBalance = suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), suite.chainB.SenderAccount.GetAddress(), coinSentFromAToB.Denom)
|
|
suite.Require().Equal(coinSentFromAToB, chainBBalance)
|
|
|
|
// check that module account escrow address is empty
|
|
escrowAddress = types.GetEscrowAddress(traceBToC.PortId, traceBToC.ChannelId)
|
|
chainBEscrowBalance := suite.chainB.GetSimApp().BankKeeper.GetBalance(suite.chainB.GetContext(), escrowAddress, coinSentFromAToB.Denom)
|
|
suite.Require().Zero(chainBEscrowBalance.Amount.Int64())
|
|
|
|
// check balances for chainA after transfer from chainC to chainB
|
|
// check that the balance is unchanged
|
|
chainABalance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), suite.chainA.SenderAccount.GetAddress(), originalCoin.Denom)
|
|
suite.Require().True(originalBalance.Amount.Sub(transferAmount).Equal(chainABalance.Amount))
|
|
|
|
// check that module account escrow address is unchanged
|
|
escrowAddress = types.GetEscrowAddress(pathAToB.EndpointA.ChannelConfig.PortID, pathAToB.EndpointA.ChannelID)
|
|
chainAEscrowBalance = suite.chainA.GetSimApp().BankKeeper.GetBalance(suite.chainA.GetContext(), escrowAddress, originalCoin.Denom)
|
|
suite.Require().True(transferAmount.Equal(chainAEscrowBalance.Amount))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTransferTestSuite(t *testing.T) {
|
|
testifysuite.Run(t, new(TransferTestSuite))
|
|
}
|