301 lines
11 KiB
Go
301 lines
11 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
|
"github.com/cosmos/cosmos-sdk/x/ibc-transfer/types"
|
|
connection "github.com/cosmos/cosmos-sdk/x/ibc/03-connection"
|
|
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
|
|
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
|
|
)
|
|
|
|
func (suite *KeeperTestSuite) TestSendTransfer() {
|
|
testCoins2 := sdk.NewCoins(sdk.NewCoin("testportid/secondchannel/atom", sdk.NewInt(100)))
|
|
capName := host.ChannelCapabilityPath(testPort1, testChannel1)
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
amount sdk.Coins
|
|
malleate func()
|
|
isSourceChain bool
|
|
expPass bool
|
|
}{
|
|
{"successful transfer from source chain", testCoins2,
|
|
func() {
|
|
suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), testAddr1, testCoins)
|
|
suite.chainA.CreateClient(suite.chainB)
|
|
suite.chainA.createConnection(testConnection, testConnection, testClientIDB, testClientIDA, connection.OPEN)
|
|
suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, channeltypes.OPEN, channeltypes.ORDERED, testConnection)
|
|
suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort1, testChannel1, 1)
|
|
}, true, true},
|
|
{"successful transfer from external chain", prefixCoins,
|
|
func() {
|
|
suite.chainA.App.BankKeeper.SetSupply(suite.chainA.GetContext(), bank.NewSupply(prefixCoins))
|
|
_, err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), testAddr1, prefixCoins)
|
|
suite.Require().NoError(err)
|
|
suite.chainA.CreateClient(suite.chainB)
|
|
suite.chainA.createConnection(testConnection, testConnection, testClientIDB, testClientIDA, connection.OPEN)
|
|
suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, channeltypes.OPEN, channeltypes.ORDERED, testConnection)
|
|
suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort1, testChannel1, 1)
|
|
}, false, true},
|
|
{"source channel not found", testCoins,
|
|
func() {}, true, false},
|
|
{"next seq send not found", testCoins,
|
|
func() {
|
|
suite.chainA.CreateClient(suite.chainB)
|
|
suite.chainA.createConnection(testConnection, testConnection, testClientIDB, testClientIDA, connection.OPEN)
|
|
suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, channeltypes.OPEN, channeltypes.ORDERED, testConnection)
|
|
}, true, false},
|
|
// createOutgoingPacket tests
|
|
// - source chain
|
|
{"send coins failed", testCoins,
|
|
func() {
|
|
suite.chainA.CreateClient(suite.chainB)
|
|
suite.chainA.createConnection(testConnection, testConnection, testClientIDB, testClientIDA, connection.OPEN)
|
|
suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, channeltypes.OPEN, channeltypes.ORDERED, testConnection)
|
|
suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort1, testChannel1, 1)
|
|
}, true, false},
|
|
// - receiving chain
|
|
{"send from module account failed", testCoins,
|
|
func() {
|
|
suite.chainA.CreateClient(suite.chainB)
|
|
suite.chainA.createConnection(testConnection, testConnection, testClientIDB, testClientIDA, connection.OPEN)
|
|
suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, channeltypes.OPEN, channeltypes.ORDERED, testConnection)
|
|
suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort1, testChannel1, 1)
|
|
}, false, false},
|
|
{"channel capability not found", testCoins,
|
|
func() {
|
|
suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), testAddr1, testCoins)
|
|
suite.chainA.CreateClient(suite.chainB)
|
|
suite.chainA.createConnection(testConnection, testConnection, testClientIDB, testClientIDA, connection.OPEN)
|
|
suite.chainA.createChannel(testPort1, testChannel1, testPort2, testChannel2, channeltypes.OPEN, channeltypes.ORDERED, testConnection)
|
|
suite.chainA.App.IBCKeeper.ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), testPort1, testChannel1, 1)
|
|
// Release channel capability
|
|
cap, _ := suite.chainA.App.ScopedTransferKeeper.GetCapability(suite.chainA.GetContext(), capName)
|
|
suite.chainA.App.ScopedTransferKeeper.ReleaseCapability(suite.chainA.GetContext(), cap)
|
|
}, true, false},
|
|
}
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
i := i
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
// create channel capability from ibc scoped keeper and claim with transfer scoped keeper
|
|
cap, err := suite.chainA.App.ScopedIBCKeeper.NewCapability(suite.chainA.GetContext(), capName)
|
|
suite.Require().Nil(err, "could not create capability")
|
|
err = suite.chainA.App.ScopedTransferKeeper.ClaimCapability(suite.chainA.GetContext(), cap, capName)
|
|
suite.Require().Nil(err, "transfer module could not claim capability")
|
|
|
|
tc.malleate()
|
|
|
|
err = suite.chainA.App.TransferKeeper.SendTransfer(
|
|
suite.chainA.GetContext(), testPort1, testChannel1, tc.amount, testAddr1, testAddr2.String(), 110, 0,
|
|
)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg)
|
|
} else {
|
|
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *KeeperTestSuite) TestOnRecvPacket() {
|
|
data := types.NewFungibleTokenPacketData(prefixCoins2, testAddr1.String(), testAddr2.String())
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
expPass bool
|
|
}{
|
|
{"success receive from source chain",
|
|
func() {}, true},
|
|
// onRecvPacket
|
|
// - source chain
|
|
{"no dest prefix on coin denom",
|
|
func() {
|
|
data.Amount = testCoins
|
|
}, false},
|
|
{"mint failed",
|
|
func() {
|
|
data.Amount = prefixCoins2
|
|
data.Amount[0].Amount = sdk.ZeroInt()
|
|
}, false},
|
|
// - receiving chain
|
|
{"incorrect dest prefix on coin denom",
|
|
func() {
|
|
data.Amount = prefixCoins
|
|
}, false},
|
|
{"success receive from external chain",
|
|
func() {
|
|
data.Amount = prefixCoins
|
|
escrow := types.GetEscrowAddress(testPort2, testChannel2)
|
|
_, err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, testCoins)
|
|
suite.Require().NoError(err)
|
|
}, true},
|
|
}
|
|
|
|
packet := channeltypes.NewPacket(data.GetBytes(), 1, testPort1, testChannel1, testPort2, testChannel2, 100, 0)
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
i := i
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
tc.malleate()
|
|
|
|
err := suite.chainA.App.TransferKeeper.OnRecvPacket(suite.chainA.GetContext(), packet, data)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg)
|
|
} else {
|
|
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestOnAcknowledgementPacket tests that successful acknowledgement is a no-op
|
|
// and failure acknowledment leads to refund
|
|
func (suite *KeeperTestSuite) TestOnAcknowledgementPacket() {
|
|
data := types.NewFungibleTokenPacketData(prefixCoins2, testAddr1.String(), testAddr2.String())
|
|
|
|
successAck := types.FungibleTokenPacketAcknowledgement{
|
|
Success: true,
|
|
}
|
|
failedAck := types.FungibleTokenPacketAcknowledgement{
|
|
Success: false,
|
|
Error: "failed packet transfer",
|
|
}
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
ack types.FungibleTokenPacketAcknowledgement
|
|
malleate func()
|
|
source bool
|
|
success bool // success of ack
|
|
}{
|
|
{"success ack causes no-op", successAck,
|
|
func() {}, true, true},
|
|
{"successful refund from source chain", failedAck,
|
|
func() {
|
|
escrow := types.GetEscrowAddress(testPort1, testChannel1)
|
|
_, err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(100))))
|
|
suite.Require().NoError(err)
|
|
}, true, false},
|
|
{"successful refund from external chain", failedAck,
|
|
func() {
|
|
data.Amount = prefixCoins
|
|
}, false, false},
|
|
}
|
|
|
|
packet := channeltypes.NewPacket(data.GetBytes(), 1, testPort1, testChannel1, testPort2, testChannel2, 100, 0)
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
i := i
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
|
|
var denom string
|
|
if tc.source {
|
|
prefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel())
|
|
denom = prefixCoins2[0].Denom[len(prefix):]
|
|
} else {
|
|
denom = data.Amount[0].Denom
|
|
}
|
|
|
|
preCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), testAddr1, denom)
|
|
|
|
err := suite.chainA.App.TransferKeeper.OnAcknowledgementPacket(suite.chainA.GetContext(), packet, data, tc.ack)
|
|
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg)
|
|
|
|
postCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), testAddr1, denom)
|
|
deltaAmount := postCoin.Amount.Sub(preCoin.Amount)
|
|
|
|
if tc.success {
|
|
suite.Require().Equal(sdk.ZeroInt(), deltaAmount, "successful ack changed balance")
|
|
} else {
|
|
suite.Require().Equal(prefixCoins2[0].Amount, deltaAmount, "failed ack did not trigger refund")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestOnTimeoutPacket test private refundPacket function since it is a simple wrapper over it
|
|
func (suite *KeeperTestSuite) TestOnTimeoutPacket() {
|
|
data := types.NewFungibleTokenPacketData(prefixCoins2, testAddr1.String(), testAddr2.String())
|
|
testCoins2 := sdk.NewCoins(sdk.NewCoin("bank/firstchannel/atom", sdk.NewInt(100)))
|
|
|
|
testCases := []struct {
|
|
msg string
|
|
malleate func()
|
|
source bool
|
|
expPass bool
|
|
}{
|
|
{"successful timeout from source chain",
|
|
func() {
|
|
escrow := types.GetEscrowAddress(testPort1, testChannel1)
|
|
_, err := suite.chainA.App.BankKeeper.AddCoins(suite.chainA.GetContext(), escrow, sdk.NewCoins(sdk.NewCoin("atom", sdk.NewInt(100))))
|
|
suite.Require().NoError(err)
|
|
}, true, true},
|
|
{"successful timeout from external chain",
|
|
func() {
|
|
data.Amount = testCoins2
|
|
}, false, true},
|
|
{"no source prefix on coin denom",
|
|
func() {
|
|
data.Amount = prefixCoins2
|
|
}, false, false},
|
|
{"unescrow failed",
|
|
func() {
|
|
}, true, false},
|
|
{"mint failed",
|
|
func() {
|
|
data.Amount[0].Denom = prefixCoins2[0].Denom
|
|
data.Amount[0].Amount = sdk.ZeroInt()
|
|
}, true, false},
|
|
}
|
|
|
|
packet := channeltypes.NewPacket(data.GetBytes(), 1, testPort1, testChannel1, testPort2, testChannel2, 100, 0)
|
|
|
|
for i, tc := range testCases {
|
|
tc := tc
|
|
i := i
|
|
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
|
|
suite.SetupTest() // reset
|
|
|
|
tc.malleate()
|
|
|
|
var denom string
|
|
if tc.source {
|
|
prefix := types.GetDenomPrefix(packet.GetDestPort(), packet.GetDestChannel())
|
|
denom = prefixCoins2[0].Denom[len(prefix):]
|
|
} else {
|
|
denom = data.Amount[0].Denom
|
|
}
|
|
|
|
preCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), testAddr1, denom)
|
|
|
|
err := suite.chainA.App.TransferKeeper.OnTimeoutPacket(suite.chainA.GetContext(), packet, data)
|
|
|
|
postCoin := suite.chainA.App.BankKeeper.GetBalance(suite.chainA.GetContext(), testAddr1, denom)
|
|
deltaAmount := postCoin.Amount.Sub(preCoin.Amount)
|
|
|
|
if tc.expPass {
|
|
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.msg)
|
|
suite.Require().Equal(prefixCoins2[0].Amount.Int64(), deltaAmount.Int64(), "successful timeout did not trigger refund")
|
|
} else {
|
|
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.msg)
|
|
}
|
|
})
|
|
}
|
|
}
|