cosmos-sdk/x/ibc/04-channel/keeper/timeout_test.go

364 lines
20 KiB
Go

package keeper_test
import (
"fmt"
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
)
// TestTimeoutPacket test the TimeoutPacket call on chainA by ensuring the timeout has passed
// on chainB, but that no ack has been written yet. Test cases expected to reach proof
// verification must specify which proof to use using the ordered bool.
func (suite *KeeperTestSuite) TestTimeoutPacket() {
var (
packet types.Packet
nextSeqRecv uint64
ordered bool
)
testCases := []testCase{
{"success: ORDERED", func() {
ordered = true
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, true},
{"success: UNORDERED", func() {
ordered = false
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp)
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, true},
{"channel not found", func() {
// use wrong channel naming
_, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
}, false},
{"channel not open", func() {
_, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
err := suite.coordinator.SetChannelClosed(suite.chainA, suite.chainB, channelA)
suite.Require().NoError(err)
}, false},
{"packet destination port ≠ channel counterparty port", func() {
_, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
// use wrong port for dest
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
}, false},
{"packet destination channel ID ≠ channel counterparty channel ID", func() {
_, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
// use wrong channel for dest
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp)
}, false},
{"connection not found", func() {
channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA}
channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB}
// pass channel check
suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(
suite.chainA.GetContext(),
channelA.PortID, channelA.ID,
types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion),
)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
}, false},
{"timeout", func() {
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, false},
{"packet already received ", func() {
ordered = true
nextSeqRecv = 2
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, false},
{"packet hasn't been sent", func() {
clientA, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, false},
{"next seq receive verification failed", func() {
// set ordered to false resulting in wrong proof provided
ordered = false
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp)
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, false},
{"packet ack verification failed", func() {
// set ordered to true resulting in wrong proof provided
ordered = true
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp)
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}, false},
}
for i, tc := range testCases {
tc := tc
suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() {
var (
proof []byte
proofHeight uint64
)
suite.SetupTest() // reset
nextSeqRecv = 1 // must be explicitly changed
tc.malleate()
orderedPacketKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
unorderedPacketKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
if ordered {
proof, proofHeight = suite.chainB.QueryProof(orderedPacketKey)
} else {
proof, proofHeight = suite.chainB.QueryProof(unorderedPacketKey)
}
err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutPacket(suite.chainA.GetContext(), packet, proof, proofHeight, nextSeqRecv)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestTimeoutExectued verifies that packet commitments are deleted on chainA after the
// channel capabilities are verified.
func (suite *KeeperTestSuite) TestTimeoutExecuted() {
var (
packet types.Packet
chanCap *capabilitytypes.Capability
)
testCases := []testCase{
{"success ORDERED", func() {
_, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, true},
{"channel not found", func() {
// use wrong channel naming
_, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
}, false},
{"incorrect capability", func() {
_, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
chanCap = capabilitytypes.NewCapability(100)
}, false},
}
for i, tc := range testCases {
tc := tc
suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() {
suite.SetupTest() // reset
tc.malleate()
err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutExecuted(suite.chainA.GetContext(), chanCap, packet)
pc := suite.chainA.App.IBCKeeper.ChannelKeeper.GetPacketCommitment(suite.chainA.GetContext(), packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
if tc.expPass {
suite.NoError(err)
suite.Nil(pc)
} else {
suite.Error(err)
}
})
}
}
// TestTimeoutOnClose tests the call TimeoutOnClose on chainA by closing the corresponding
// channel on chainB after the packet commitment has been created.
func (suite *KeeperTestSuite) TestTimeoutOnClose() {
var (
packet types.Packet
chanCap *capabilitytypes.Capability
nextSeqRecv uint64
ordered bool
)
testCases := []testCase{
{"success: ORDERED", func() {
ordered = true
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, true},
{"success: UNORDERED", func() {
ordered = false
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp)
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, true},
{"channel not found", func() {
// use wrong channel naming
_, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, ibctesting.InvalidID, ibctesting.InvalidID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
}, false},
{"packet dest port ≠ channel counterparty port", func() {
_, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
// use wrong port for dest
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, ibctesting.InvalidID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"packet dest channel ID ≠ channel counterparty channel ID", func() {
_, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
// use wrong channel for dest
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, ibctesting.InvalidID, timeoutHeight, disabledTimeoutTimestamp)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"connection not found", func() {
channelA := ibctesting.TestChannel{PortID: portID, ID: channelIDA}
channelB := ibctesting.TestChannel{PortID: portID, ID: channelIDB}
// pass channel check
suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(
suite.chainA.GetContext(),
channelA.PortID, channelA.ID,
types.NewChannel(types.OPEN, types.ORDERED, types.NewCounterparty(channelB.PortID, channelB.ID), []string{connIDA}, ibctesting.ChannelVersion),
)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, timeoutHeight, disabledTimeoutTimestamp)
// create chancap
suite.chainA.CreateChannelCapability(channelA.PortID, channelA.ID)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"packet hasn't been sent", func() {
_, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"packet already received", func() {
nextSeqRecv = 2
ordered = true
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"channel verification failed", func() {
ordered = true
_, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"next seq receive verification failed", func() {
// set ordered to false providing the wrong proof for ORDERED case
ordered = false
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"packet ack verification failed", func() {
// set ordered to true providing the wrong proof for UNORDERED case
ordered = true
clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), disabledTimeoutTimestamp)
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
chanCap = suite.chainA.GetChannelCapability(channelA.PortID, channelA.ID)
}, false},
{"channel capability not found", func() {
ordered = true
clientA, clientB, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
channelA, channelB := suite.coordinator.CreateChannel(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
packet = types.NewPacket(validPacketData, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, uint64(suite.chainB.GetContext().BlockHeight()), uint64(suite.chainB.GetContext().BlockTime().UnixNano()))
suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.coordinator.SetChannelClosed(suite.chainB, suite.chainA, channelB)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
chanCap = capabilitytypes.NewCapability(100)
}, false},
}
for i, tc := range testCases {
tc := tc
suite.Run(fmt.Sprintf("Case %s, %d/%d tests", tc.msg, i, len(testCases)), func() {
var proof []byte
suite.SetupTest() // reset
nextSeqRecv = 1 // must be explicitly changed
tc.malleate()
channelKey := host.KeyChannel(packet.GetDestPort(), packet.GetDestChannel())
unorderedPacketKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
orderedPacketKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
proofClosed, proofHeight := suite.chainB.QueryProof(channelKey)
if ordered {
proof, _ = suite.chainB.QueryProof(orderedPacketKey)
} else {
proof, _ = suite.chainB.QueryProof(unorderedPacketKey)
}
err := suite.chainA.App.IBCKeeper.ChannelKeeper.TimeoutOnClose(suite.chainA.GetContext(), chanCap, packet, proof, proofClosed, proofHeight, nextSeqRecv)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}