cosmos-sdk/x/ibc/03-connection/keeper/verify_test.go

422 lines
14 KiB
Go

package keeper_test
import (
"fmt"
"time"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
)
// TestVerifyClientConsensusState verifies that the consensus state of
// chainA stored on clientB (which is on chainB) matches the consensus
// state for chainA at that height.
func (suite *KeeperTestSuite) TestVerifyClientConsensusState() {
var (
connA *ibctesting.TestConnection
connB *ibctesting.TestConnection
changeClientID bool
heightDiff uint64
)
cases := []struct {
msg string
malleate func()
expPass bool
}{
{"verification success", func() {
_, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
}, true},
{"client state not found", func() {
_, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
changeClientID = true
}, false},
{"consensus state not found", func() {
_, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
heightDiff = 5
}, false},
{"verification failed", func() {
_, _, connA, connB = suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
clientB := connB.ClientID
// give chainB wrong consensus state for chainA
consState, found := suite.chainB.App.IBCKeeper.ClientKeeper.GetLatestClientConsensusState(suite.chainB.GetContext(), clientB)
suite.Require().True(found)
tmConsState, ok := consState.(ibctmtypes.ConsensusState)
suite.Require().True(ok)
tmConsState.Timestamp = time.Now()
suite.chainB.App.IBCKeeper.ClientKeeper.SetClientConsensusState(suite.chainB.GetContext(), clientB, tmConsState.Height, tmConsState)
suite.coordinator.CommitBlock(suite.chainB)
}, false},
}
for _, tc := range cases {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
heightDiff = 0 // must be explicitly changed in malleate
changeClientID = false // must be explicitly changed in malleate
tc.malleate()
connection := suite.chainA.GetConnection(connA)
if changeClientID {
connection.ClientID = ibctesting.InvalidID
}
proof, consensusHeight := suite.chainB.QueryConsensusStateProof(connB.ClientID)
proofHeight := uint64(suite.chainA.GetContext().BlockHeight() - 1)
consensusState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetSelfConsensusState(suite.chainA.GetContext(), consensusHeight)
suite.Require().True(found)
err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyClientConsensusState(
suite.chainA.GetContext(), connection,
proofHeight+heightDiff, consensusHeight, proof, consensusState,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestVerifyConnectionState verifies the connection state of the connection
// on chainB. The connections on chainA and chainB are fully opened.
func (suite *KeeperTestSuite) TestVerifyConnectionState() {
cases := []struct {
msg string
changeClientID bool
changeConnectionState bool
heightDiff uint64
expPass bool
}{
{"verification success", false, false, 0, true},
{"client state not found - changed client ID", true, false, 0, false},
{"consensus state not found - increased proof height", false, false, 5, false},
{"verification failed - connection state is different than proof", false, true, 0, false},
}
for _, tc := range cases {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
_, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
connection := suite.chainA.GetConnection(connA)
if tc.changeClientID {
connection.ClientID = ibctesting.InvalidID
}
expectedConnection := suite.chainB.GetConnection(connB)
connectionKey := host.KeyConnection(connB.ID)
proof, proofHeight := suite.chainB.QueryProof(connectionKey)
if tc.changeConnectionState {
expectedConnection.State = types.TRYOPEN
}
err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyConnectionState(
suite.chainA.GetContext(), connection,
proofHeight+tc.heightDiff, proof, connB.ID, expectedConnection,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestVerifyChannelState verifies the channel state of the channel on
// chainB. The channels on chainA and chainB are fully opened.
func (suite *KeeperTestSuite) TestVerifyChannelState() {
cases := []struct {
msg string
changeClientID bool
changeChannelState bool
heightDiff uint64
expPass bool
}{
{"verification success", false, false, 0, true},
{"client state not found- changed client ID", true, false, 0, false},
{"consensus state not found - increased proof height", false, false, 5, false},
{"verification failed - changed channel state", false, true, 0, false},
}
for _, tc := range cases {
tc := tc
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset
_, _, connA, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
connection := suite.chainA.GetConnection(connA)
if tc.changeClientID {
connection.ClientID = ibctesting.InvalidID
}
channelKey := host.KeyChannel(channelB.PortID, channelB.ID)
proof, proofHeight := suite.chainB.QueryProof(channelKey)
channel := suite.chainB.GetChannel(channelB)
if tc.changeChannelState {
channel.State = channeltypes.TRYOPEN
}
err := suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyChannelState(
suite.chainA.GetContext(), connection, proofHeight+tc.heightDiff, proof,
channelB.PortID, channelB.ID, channel,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestVerifyPacketCommitmentState has chainB verify the packet commitment
// on channelA. The channels on chainA and chainB are fully opened and a
// packet is sent from chainA to chainB, but has not been received.
func (suite *KeeperTestSuite) TestVerifyPacketCommitment() {
cases := []struct {
msg string
changeClientID bool
changePacketCommitmentState bool
heightDiff uint64
expPass bool
}{
{"verification success", false, false, 0, true},
{"client state not found- changed client ID", true, false, 0, false},
{"consensus state not found - increased proof height", false, false, 5, false},
{"verification failed - changed packet commitment state", false, true, 0, false},
}
for _, tc := range cases {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
_, clientB, _, connB, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
connection := suite.chainB.GetConnection(connB)
if tc.changeClientID {
connection.ClientID = ibctesting.InvalidID
}
packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100000, 0)
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
commitmentKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
proof, proofHeight := suite.chainA.QueryProof(commitmentKey)
if tc.changePacketCommitmentState {
packet.Data = []byte(ibctesting.InvalidID)
}
err = suite.chainB.App.IBCKeeper.ConnectionKeeper.VerifyPacketCommitment(
suite.chainB.GetContext(), connection, proofHeight+tc.heightDiff, proof,
packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), channeltypes.CommitPacket(packet),
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestVerifyPacketAcknowledgement has chainA verify the acknowledgement on
// channelB. The channels on chainA and chainB are fully opened and a packet
// is sent from chainA to chainB and received.
func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() {
cases := []struct {
msg string
changeClientID bool
changeAcknowledgement bool
heightDiff uint64
expPass bool
}{
{"verification success", false, false, 0, true},
{"client state not found- changed client ID", true, false, 0, false},
{"consensus state not found - increased proof height", false, false, 5, false},
{"verification failed - changed acknowledgement", false, true, 0, false},
}
for _, tc := range cases {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
clientA, clientB, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
connection := suite.chainA.GetConnection(connA)
if tc.changeClientID {
connection.ClientID = ibctesting.InvalidID
}
// send and receive packet
packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100000, 0)
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA)
suite.Require().NoError(err)
packetAckKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proof, proofHeight := suite.chainB.QueryProof(packetAckKey)
ack := ibctesting.TestHash
if tc.changeAcknowledgement {
ack = []byte(ibctesting.InvalidID)
}
err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgement(
suite.chainA.GetContext(), connection, proofHeight+tc.heightDiff, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ack,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestVerifyPacketAcknowledgementAbsence has chainA verify the acknowledgement
// absence on channelB. The channels on chainA and chainB are fully opened and
// a packet is sent from chainA to chainB and not received.
func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() {
cases := []struct {
msg string
changeClientID bool
recvAck bool
heightDiff uint64
expPass bool
}{
{"verification success", false, false, 0, true},
{"client state not found - changed client ID", true, false, 0, false},
{"consensus state not found - increased proof height", false, false, 5, false},
{"verification failed - acknowledgement was received", false, true, 0, false},
}
for _, tc := range cases {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
clientA, clientB, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
connection := suite.chainA.GetConnection(connA)
if tc.changeClientID {
connection.ClientID = ibctesting.InvalidID
}
// send, only receive if specified
packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100000, 0)
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
if tc.recvAck {
err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA)
suite.Require().NoError(err)
} else {
// need to update height to prove absence
suite.coordinator.CommitBlock(suite.chainA, suite.chainB)
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
}
packetAckKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proof, proofHeight := suite.chainB.QueryProof(packetAckKey)
err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence(
suite.chainA.GetContext(), connection, proofHeight+tc.heightDiff, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}
// TestVerifyNextSequenceRecv has chainA verify the next sequence receive on
// channelB. The channels on chainA and chainB are fully opened and a packet
// is sent from chainA to chainB and received.
func (suite *KeeperTestSuite) TestVerifyNextSequenceRecv() {
cases := []struct {
msg string
changeClientID bool
offsetSeq uint64
heightDiff uint64
expPass bool
}{
{"verification success", false, 0, 0, true},
{"client state not found- changed client ID", true, 0, 0, false},
{"consensus state not found - increased proof height", false, 0, 5, false},
{"verification failed - wrong expected next seq recv", false, 1, 0, false},
}
for _, tc := range cases {
tc := tc
suite.Run(tc.msg, func() {
suite.SetupTest() // reset
clientA, clientB, connA, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
connection := suite.chainA.GetConnection(connA)
if tc.changeClientID {
connection.ClientID = ibctesting.InvalidID
}
// send and receive packet
packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100000, 0)
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA)
suite.Require().NoError(err)
nextSeqRecvKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
proof, proofHeight := suite.chainB.QueryProof(nextSeqRecvKey)
err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyNextSequenceRecv(
suite.chainA.GetContext(), connection, proofHeight+tc.heightDiff, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence()+tc.offsetSeq,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
}
}