ConsensusState is retrieved by light clients in verifyXYZ as necessary (#7005)

* move con state retrieval to verify funcs

* refactor tm client state tests

* fix build

* Apply suggestions from code review

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: Aditya <adityasripal@gmail.com>

* apply @fedekunze review comments

* apply @AdityaSripal review comment

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
Co-authored-by: Aditya <adityasripal@gmail.com>
This commit is contained in:
colin axnér 2020-08-12 11:20:29 +02:00 committed by GitHub
parent 308c879b11
commit d752a7b21f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 578 additions and 497 deletions

View File

@ -46,7 +46,6 @@ type ClientState interface {
proof []byte, proof []byte,
connectionID string, connectionID string,
connectionEnd connectionexported.ConnectionI, connectionEnd connectionexported.ConnectionI,
consensusState ConsensusState,
) error ) error
VerifyChannelState( VerifyChannelState(
store sdk.KVStore, store sdk.KVStore,
@ -57,7 +56,6 @@ type ClientState interface {
portID, portID,
channelID string, channelID string,
channel channelexported.ChannelI, channel channelexported.ChannelI,
consensusState ConsensusState,
) error ) error
VerifyPacketCommitment( VerifyPacketCommitment(
store sdk.KVStore, store sdk.KVStore,
@ -69,7 +67,6 @@ type ClientState interface {
channelID string, channelID string,
sequence uint64, sequence uint64,
commitmentBytes []byte, commitmentBytes []byte,
consensusState ConsensusState,
) error ) error
VerifyPacketAcknowledgement( VerifyPacketAcknowledgement(
store sdk.KVStore, store sdk.KVStore,
@ -81,7 +78,6 @@ type ClientState interface {
channelID string, channelID string,
sequence uint64, sequence uint64,
acknowledgement []byte, acknowledgement []byte,
consensusState ConsensusState,
) error ) error
VerifyPacketAcknowledgementAbsence( VerifyPacketAcknowledgementAbsence(
store sdk.KVStore, store sdk.KVStore,
@ -92,7 +88,6 @@ type ClientState interface {
portID, portID,
channelID string, channelID string,
sequence uint64, sequence uint64,
consensusState ConsensusState,
) error ) error
VerifyNextSequenceRecv( VerifyNextSequenceRecv(
store sdk.KVStore, store sdk.KVStore,
@ -103,7 +98,6 @@ type ClientState interface {
portID, portID,
channelID string, channelID string,
nextSequenceRecv uint64, nextSequenceRecv uint64,
consensusState ConsensusState,
) error ) error
} }

View File

@ -34,7 +34,7 @@ func (k Keeper) VerifyClientConsensusState(
k.clientKeeper.ClientStore(ctx, clientID), k.cdc, targetConsState.GetRoot(), height, k.clientKeeper.ClientStore(ctx, clientID), k.cdc, targetConsState.GetRoot(), height,
connection.GetCounterparty().GetClientID(), consensusHeight, connection.GetCounterparty().GetPrefix(), proof, consensusState, connection.GetCounterparty().GetClientID(), consensusHeight, connection.GetCounterparty().GetPrefix(), proof, consensusState,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed consensus state verification") return sdkerrors.Wrapf(err, "failed consensus state verification for client (%s)", connection.GetClientID())
} }
return nil return nil
@ -55,22 +55,11 @@ func (k Keeper) VerifyConnectionState(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
} }
// TODO: move to specific clients; blocked by #5502
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)
if !found {
return sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}
if err := clientState.VerifyConnectionState( if err := clientState.VerifyConnectionState(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd, consensusState, connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed connection state verification") return sdkerrors.Wrapf(err, "failed connection state verification for client (%s)", connection.GetClientID())
} }
return nil return nil
@ -92,23 +81,12 @@ func (k Keeper) VerifyChannelState(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
} }
// TODO: move to specific clients; blocked by #5502
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)
if !found {
return sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}
if err := clientState.VerifyChannelState( if err := clientState.VerifyChannelState(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, connection.GetCounterparty().GetPrefix(), proof,
portID, channelID, channel, consensusState, portID, channelID, channel,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed channel state verification") return sdkerrors.Wrapf(err, "failed channel state verification for client (%s)", connection.GetClientID())
} }
return nil return nil
@ -131,23 +109,12 @@ func (k Keeper) VerifyPacketCommitment(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
} }
// TODO: move to specific clients; blocked by #5502
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)
if !found {
return sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}
if err := clientState.VerifyPacketCommitment( if err := clientState.VerifyPacketCommitment(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, portID, channelID, connection.GetCounterparty().GetPrefix(), proof, portID, channelID,
sequence, commitmentBytes, consensusState, sequence, commitmentBytes,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed packet commitment verification") return sdkerrors.Wrapf(err, "failed packet commitment verification for client (%s)", connection.GetClientID())
} }
return nil return nil
@ -170,23 +137,12 @@ func (k Keeper) VerifyPacketAcknowledgement(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
} }
// TODO: move to specific clients; blocked by #5502
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)
if !found {
return sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}
if err := clientState.VerifyPacketAcknowledgement( if err := clientState.VerifyPacketAcknowledgement(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, portID, channelID, connection.GetCounterparty().GetPrefix(), proof, portID, channelID,
sequence, acknowledgement, consensusState, sequence, acknowledgement,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed packet acknowledgement verification") return sdkerrors.Wrapf(err, "failed packet acknowledgement verification for client (%s)", connection.GetClientID())
} }
return nil return nil
@ -209,23 +165,12 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
} }
// TODO: move to specific clients; blocked by #5502
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)
if !found {
return sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}
if err := clientState.VerifyPacketAcknowledgementAbsence( if err := clientState.VerifyPacketAcknowledgementAbsence(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, portID, channelID, connection.GetCounterparty().GetPrefix(), proof, portID, channelID,
sequence, consensusState, sequence,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed packet acknowledgement absence verification") return sdkerrors.Wrapf(err, "failed packet acknowledgement absence verification for client (%s)", connection.GetClientID())
} }
return nil return nil
@ -247,23 +192,12 @@ func (k Keeper) VerifyNextSequenceRecv(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID()) return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
} }
// TODO: move to specific clients; blocked by #5502
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
)
if !found {
return sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
)
}
if err := clientState.VerifyNextSequenceRecv( if err := clientState.VerifyNextSequenceRecv(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, portID, channelID, connection.GetCounterparty().GetPrefix(), proof, portID, channelID,
nextSequenceRecv, consensusState, nextSequenceRecv,
); err != nil { ); err != nil {
return sdkerrors.Wrap(err, "failed next sequence receive verification") return sdkerrors.Wrapf(err, "failed next sequence receive verification for client (%s)", connection.GetClientID())
} }
return nil return nil

View File

@ -123,7 +123,7 @@ func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec {
// VerifyClientConsensusState verifies a proof of the consensus state of the // VerifyClientConsensusState verifies a proof of the consensus state of the
// Tendermint client stored on the target machine. // Tendermint client stored on the target machine.
func (cs ClientState) VerifyClientConsensusState( func (cs ClientState) VerifyClientConsensusState(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
provingRoot commitmentexported.Root, provingRoot commitmentexported.Root,
height uint64, height uint64,
@ -133,7 +133,7 @@ func (cs ClientState) VerifyClientConsensusState(
proof []byte, proof []byte,
consensusState clientexported.ConsensusState, consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, _, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -144,6 +144,15 @@ func (cs ClientState) VerifyClientConsensusState(
return err return err
} }
if consensusState == nil {
return sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty")
}
_, ok := consensusState.(*ConsensusState)
if !ok {
return sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{})
}
bz, err := codec.MarshalAny(cdc, consensusState) bz, err := codec.MarshalAny(cdc, consensusState)
if err != nil { if err != nil {
return err return err
@ -159,16 +168,15 @@ func (cs ClientState) VerifyClientConsensusState(
// VerifyConnectionState verifies a proof of the connection state of the // VerifyConnectionState verifies a proof of the connection state of the
// specified connection end stored on the target machine. // specified connection end stored on the target machine.
func (cs ClientState) VerifyConnectionState( func (cs ClientState) VerifyConnectionState(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
proof []byte, proof []byte,
connectionID string, connectionID string,
connectionEnd connectionexported.ConnectionI, connectionEnd connectionexported.ConnectionI,
consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -198,7 +206,7 @@ func (cs ClientState) VerifyConnectionState(
// VerifyChannelState verifies a proof of the channel state of the specified // VerifyChannelState verifies a proof of the channel state of the specified
// channel end, under the specified port, stored on the target machine. // channel end, under the specified port, stored on the target machine.
func (cs ClientState) VerifyChannelState( func (cs ClientState) VerifyChannelState(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
@ -206,9 +214,8 @@ func (cs ClientState) VerifyChannelState(
portID, portID,
channelID string, channelID string,
channel channelexported.ChannelI, channel channelexported.ChannelI,
consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -238,7 +245,7 @@ func (cs ClientState) VerifyChannelState(
// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at // VerifyPacketCommitment verifies a proof of an outgoing packet commitment at
// the specified port, specified channel, and specified sequence. // the specified port, specified channel, and specified sequence.
func (cs ClientState) VerifyPacketCommitment( func (cs ClientState) VerifyPacketCommitment(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
@ -247,9 +254,8 @@ func (cs ClientState) VerifyPacketCommitment(
channelID string, channelID string,
sequence uint64, sequence uint64,
commitmentBytes []byte, commitmentBytes []byte,
consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -269,7 +275,7 @@ func (cs ClientState) VerifyPacketCommitment(
// VerifyPacketAcknowledgement verifies a proof of an incoming packet // VerifyPacketAcknowledgement verifies a proof of an incoming packet
// acknowledgement at the specified port, specified channel, and specified sequence. // acknowledgement at the specified port, specified channel, and specified sequence.
func (cs ClientState) VerifyPacketAcknowledgement( func (cs ClientState) VerifyPacketAcknowledgement(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
@ -278,9 +284,8 @@ func (cs ClientState) VerifyPacketAcknowledgement(
channelID string, channelID string,
sequence uint64, sequence uint64,
acknowledgement []byte, acknowledgement []byte,
consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -301,7 +306,7 @@ func (cs ClientState) VerifyPacketAcknowledgement(
// incoming packet acknowledgement at the specified port, specified channel, and // incoming packet acknowledgement at the specified port, specified channel, and
// specified sequence. // specified sequence.
func (cs ClientState) VerifyPacketAcknowledgementAbsence( func (cs ClientState) VerifyPacketAcknowledgementAbsence(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
@ -309,9 +314,8 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
portID, portID,
channelID string, channelID string,
sequence uint64, sequence uint64,
consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -331,7 +335,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
// VerifyNextSequenceRecv verifies a proof of the next sequence number to be // VerifyNextSequenceRecv verifies a proof of the next sequence number to be
// received of the specified channel at the specified port. // received of the specified channel at the specified port.
func (cs ClientState) VerifyNextSequenceRecv( func (cs ClientState) VerifyNextSequenceRecv(
_ sdk.KVStore, store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
@ -339,9 +343,8 @@ func (cs ClientState) VerifyNextSequenceRecv(
portID, portID,
channelID string, channelID string,
nextSequenceRecv uint64, nextSequenceRecv uint64,
consensusState clientexported.ConsensusState,
) error { ) error {
merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof)
if err != nil { if err != nil {
return err return err
} }
@ -360,53 +363,49 @@ func (cs ClientState) VerifyNextSequenceRecv(
return nil return nil
} }
// sanitizeVerificationArgs perfoms the basic checks on the arguments that are // produceVerificationArgs perfoms the basic checks on the arguments that are
// shared between the verification functions and returns the unmarshalled // shared between the verification functions and returns the unmarshalled
// merkle proof and an error if one occurred. // merkle proof, the consensus state and an error if one occurred.
func sanitizeVerificationArgs( func produceVerificationArgs(
store sdk.KVStore,
cdc codec.BinaryMarshaler, cdc codec.BinaryMarshaler,
cs ClientState, cs ClientState,
height uint64, height uint64,
prefix commitmentexported.Prefix, prefix commitmentexported.Prefix,
proof []byte, proof []byte,
consensusState clientexported.ConsensusState, ) (merkleProof commitmenttypes.MerkleProof, consensusState *ConsensusState, err error) {
) (merkleProof commitmenttypes.MerkleProof, err error) {
if cs.GetLatestHeight() < height { if cs.GetLatestHeight() < height {
return commitmenttypes.MerkleProof{}, sdkerrors.Wrapf( return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidHeight, sdkerrors.ErrInvalidHeight,
"client state height < proof height (%d < %d)", cs.GetLatestHeight(), height, "client state height < proof height (%d < %d)", cs.GetLatestHeight(), height,
) )
} }
if cs.IsFrozen() && cs.FrozenHeight <= height { if cs.IsFrozen() && cs.FrozenHeight <= height {
return commitmenttypes.MerkleProof{}, clienttypes.ErrClientFrozen return commitmenttypes.MerkleProof{}, nil, clienttypes.ErrClientFrozen
} }
if prefix == nil { if prefix == nil {
return commitmenttypes.MerkleProof{}, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty") return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidPrefix, "prefix cannot be empty")
} }
_, ok := prefix.(*commitmenttypes.MerklePrefix) _, ok := prefix.(*commitmenttypes.MerklePrefix)
if !ok { if !ok {
return commitmenttypes.MerkleProof{}, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected *MerklePrefix", prefix) return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf(commitmenttypes.ErrInvalidPrefix, "invalid prefix type %T, expected *MerklePrefix", prefix)
} }
if proof == nil { if proof == nil {
return commitmenttypes.MerkleProof{}, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty") return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "proof cannot be empty")
} }
if err = cdc.UnmarshalBinaryBare(proof, &merkleProof); err != nil { if err = cdc.UnmarshalBinaryBare(proof, &merkleProof); err != nil {
return commitmenttypes.MerkleProof{}, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof") return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into commitment merkle proof")
} }
if consensusState == nil { consensusState, err = GetConsensusState(store, cdc, height)
return commitmenttypes.MerkleProof{}, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") if err != nil {
return commitmenttypes.MerkleProof{}, nil, err
} }
_, ok = consensusState.(*ConsensusState) return merkleProof, consensusState, nil
if !ok {
return commitmenttypes.MerkleProof{}, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{})
}
return merkleProof, nil
} }

View File

@ -3,11 +3,13 @@ package types_test
import ( import (
ics23 "github.com/confio/ics23/go" ics23 "github.com/confio/ics23/go"
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types" clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types" channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types" commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
) )
const ( const (
@ -18,6 +20,10 @@ const (
testSequence = 1 testSequence = 1
) )
var (
invalidProof = []byte("invalid proof")
)
func (suite *TendermintTestSuite) TestValidate() { func (suite *TendermintTestSuite) TestValidate() {
testCases := []struct { testCases := []struct {
name string name string
@ -160,478 +166,506 @@ func (suite *TendermintTestSuite) TestVerifyClientConsensusState() {
} }
} }
// test verification of the connection on chainB being represented in the
// light client on chainA
func (suite *TendermintTestSuite) TestVerifyConnectionState() { func (suite *TendermintTestSuite) TestVerifyConnectionState() {
counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc"))) var (
conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []string{"1.0.0"}) clientState *types.ClientState
proof []byte
proofHeight uint64
prefix commitmenttypes.MerklePrefix
)
testCases := []struct { testCases := []struct {
name string name string
clientState *ibctmtypes.ClientState malleate func()
connection connectiontypes.ConnectionEnd expPass bool
consensusState ibctmtypes.ConsensusState
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
}{ }{
// FIXME: uncomment
// {
// name: "successful verification",
// clientState: ibctmtypes.NewClientState(chainID, chainID, chainID, height, commitmenttypes.GetSDKSpecs()),
// connection: conn,
// consensusState: ibctmtypes.ConsensusState{
// Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
// },
// prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
// expPass: true,
// },
{ {
name: "ApplyPrefix failed", "successful verification", func() {}, true,
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
connection: conn,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.MerklePrefix{},
expPass: false,
}, },
{ {
name: "latest client height < height", "ApplyPrefix failed", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), prefix = commitmenttypes.MerklePrefix{}
connection: conn, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "client is frozen", "latest client height < height", func() {
clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, proofHeight = clientState.LatestHeight + 1
connection: conn, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "proof verification failed", "client is frozen", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState.FrozenHeight = 1
connection: conn, }, false,
consensusState: ibctmtypes.ConsensusState{ },
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), {
NextValidatorsHash: suite.valsHash, "proof verification failed", func() {
}, proof = invalidProof
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), }, false,
proof: []byte{},
expPass: false,
}, },
} }
for i, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
err := tc.clientState.VerifyConnectionState( suite.Run(tc.name, func() {
nil, suite.cdc, height, tc.prefix, tc.proof, testConnectionID, tc.connection, tc.consensusState, suite.SetupTest() // reset
)
if tc.expPass { // setup testing conditions
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) clientA, _, _, connB, _, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
} else { connection := suite.chainB.GetConnection(connB)
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
} var ok bool
clientStateI := suite.chainA.GetClientState(clientA)
clientState, ok = clientStateI.(*types.ClientState)
suite.Require().True(ok)
prefix = suite.chainB.GetPrefix()
// make connection proof
connectionKey := host.KeyConnection(connB.ID)
proof, proofHeight = suite.chainB.QueryProof(connectionKey)
tc.malleate() // make changes as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
err := clientState.VerifyConnectionState(
store, suite.chainA.Codec, proofHeight, &prefix, proof, connB.ID, connection,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
} }
} }
// test verification of the channel on chainB being represented in the light
// client on chainA
func (suite *TendermintTestSuite) TestVerifyChannelState() { func (suite *TendermintTestSuite) TestVerifyChannelState() {
counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) var (
ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") clientState *types.ClientState
proof []byte
proofHeight uint64
prefix commitmenttypes.MerklePrefix
)
testCases := []struct { testCases := []struct {
name string name string
clientState *ibctmtypes.ClientState malleate func()
channel channeltypes.Channel expPass bool
consensusState ibctmtypes.ConsensusState
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
}{ }{
// FIXME: uncomment
// {
// name: "successful verification",
// clientState: ibctmtypes.NewClientState(chainID, height, commitmenttypes.GetSDKSpecs()),
// connection: conn,
// consensusState: ibctmtypes.ConsensusState{
// Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
// },
// prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
// expPass: true,
// },
{ {
name: "ApplyPrefix failed", "successful verification", func() {}, true,
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
channel: ch,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.MerklePrefix{},
expPass: false,
}, },
{ {
name: "latest client height < height", "ApplyPrefix failed", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), prefix = commitmenttypes.MerklePrefix{}
channel: ch, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "client is frozen", "latest client height < height", func() {
clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, proofHeight = clientState.LatestHeight + 1
channel: ch, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "proof verification failed", "client is frozen", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState.FrozenHeight = 1
channel: ch, }, false,
consensusState: ibctmtypes.ConsensusState{ },
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), {
NextValidatorsHash: suite.valsHash, "proof verification failed", func() {
}, proof = invalidProof
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), }, false,
proof: []byte{},
expPass: false,
}, },
} }
for i, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
err := tc.clientState.VerifyChannelState( suite.Run(tc.name, func() {
nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, &tc.channel, tc.consensusState, suite.SetupTest() // reset
)
if tc.expPass { // setup testing conditions
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) clientA, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
} else { channel := suite.chainB.GetChannel(channelB)
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
} var ok bool
clientStateI := suite.chainA.GetClientState(clientA)
clientState, ok = clientStateI.(*types.ClientState)
suite.Require().True(ok)
prefix = suite.chainB.GetPrefix()
// make channel proof
channelKey := host.KeyChannel(channelB.PortID, channelB.ID)
proof, proofHeight = suite.chainB.QueryProof(channelKey)
tc.malleate() // make changes as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
err := clientState.VerifyChannelState(
store, suite.chainA.Codec, proofHeight, &prefix, proof,
channelB.PortID, channelB.ID, channel,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
} }
} }
// test verification of the packet commitment on chainB being represented
// in the light client on chainA. A send from chainB to chainA is simulated.
func (suite *TendermintTestSuite) TestVerifyPacketCommitment() { func (suite *TendermintTestSuite) TestVerifyPacketCommitment() {
var (
clientState *types.ClientState
proof []byte
proofHeight uint64
prefix commitmenttypes.MerklePrefix
)
testCases := []struct { testCases := []struct {
name string name string
clientState *ibctmtypes.ClientState malleate func()
commitment []byte expPass bool
consensusState ibctmtypes.ConsensusState
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
}{ }{
// FIXME: uncomment
// {
// name: "successful verification",
// clientState: ibctmtypes.NewClientState(chainID, height, commitmenttypes.GetSDKSpecs()),
// connection: conn,
// consensusState: ibctmtypes.ConsensusState{
// Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
// },
// prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
// expPass: true,
// },
{ {
name: "ApplyPrefix failed", "successful verification", func() {}, true,
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
commitment: []byte{},
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.MerklePrefix{},
expPass: false,
}, },
{ {
name: "latest client height < height", "ApplyPrefix failed", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), prefix = commitmenttypes.MerklePrefix{}
commitment: []byte{}, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "client is frozen", "latest client height < height", func() {
clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, proofHeight = clientState.LatestHeight + 1
commitment: []byte{}, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "proof verification failed", "client is frozen", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState.FrozenHeight = 1
commitment: []byte{}, }, false,
consensusState: ibctmtypes.ConsensusState{ },
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), {
NextValidatorsHash: suite.valsHash, "proof verification failed", func() {
}, proof = invalidProof
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), }, false,
proof: []byte{},
expPass: false,
}, },
} }
for i, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
err := tc.clientState.VerifyPacketCommitment( suite.Run(tc.name, func() {
nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.commitment, tc.consensusState, suite.SetupTest() // reset
)
if tc.expPass { // setup testing conditions
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
} else { packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, 100, 0)
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) err := suite.coordinator.SendPacket(suite.chainB, suite.chainA, packet, clientA)
} suite.Require().NoError(err)
var ok bool
clientStateI := suite.chainA.GetClientState(clientA)
clientState, ok = clientStateI.(*types.ClientState)
suite.Require().True(ok)
prefix = suite.chainB.GetPrefix()
// make packet commitment proof
packetKey := host.KeyPacketCommitment(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
proof, proofHeight = suite.chainB.QueryProof(packetKey)
tc.malleate() // make changes as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
err = clientState.VerifyPacketCommitment(
store, suite.chainA.Codec, proofHeight, &prefix, proof,
packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence(), channeltypes.CommitPacket(packet),
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
} }
} }
// test verification of the acknowledgement on chainB being represented
// in the light client on chainA. A send and ack from chainA to chainB
// is simulated.
func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() { func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() {
var (
clientState *types.ClientState
proof []byte
proofHeight uint64
prefix commitmenttypes.MerklePrefix
)
testCases := []struct { testCases := []struct {
name string name string
clientState *ibctmtypes.ClientState malleate func()
ack []byte expPass bool
consensusState ibctmtypes.ConsensusState
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
}{ }{
// FIXME: uncomment
// {
// name: "successful verification",
// clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
// connection: conn,
// consensusState: ibctmtypes.ConsensusState{
// Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
// },
// prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
// expPass: true,
// },
{ {
name: "ApplyPrefix failed", "successful verification", func() {}, true,
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
ack: []byte{},
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.MerklePrefix{},
expPass: false,
}, },
{ {
name: "latest client height < height", "ApplyPrefix failed", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), prefix = commitmenttypes.MerklePrefix{}
ack: []byte{}, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "client is frozen", "latest client height < height", func() {
clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, proofHeight = clientState.LatestHeight + 1
ack: []byte{}, }, false,
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "proof verification failed", "client is frozen", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState.FrozenHeight = 1
ack: []byte{}, }, false,
consensusState: ibctmtypes.ConsensusState{ },
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), {
NextValidatorsHash: suite.valsHash, "proof verification failed", func() {
}, proof = invalidProof
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), }, false,
proof: []byte{},
expPass: false,
}, },
} }
for i, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
err := tc.clientState.VerifyPacketAcknowledgement( suite.Run(tc.name, func() {
nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.ack, tc.consensusState, suite.SetupTest() // reset
)
if tc.expPass { // setup testing conditions
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
} else { packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100, 0)
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
} // send packet
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
// write ack
err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA)
suite.Require().NoError(err)
var ok bool
clientStateI := suite.chainA.GetClientState(clientA)
clientState, ok = clientStateI.(*types.ClientState)
suite.Require().True(ok)
prefix = suite.chainB.GetPrefix()
// make packet acknowledgement proof
acknowledgementKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proof, proofHeight = suite.chainB.QueryProof(acknowledgementKey)
tc.malleate() // make changes as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
err = clientState.VerifyPacketAcknowledgement(
store, suite.chainA.Codec, proofHeight, &prefix, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(), ibctesting.TestHash,
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
} }
} }
// test verification of the absent acknowledgement on chainB being represented
// in the light client on chainA. A send from chainB to chainA is simulated, but
// no receive.
func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() { func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() {
var (
clientState *types.ClientState
proof []byte
proofHeight uint64
prefix commitmenttypes.MerklePrefix
)
testCases := []struct { testCases := []struct {
name string name string
clientState *ibctmtypes.ClientState malleate func()
consensusState ibctmtypes.ConsensusState expPass bool
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
}{ }{
// FIXME: uncomment
// {
// name: "successful verification",
// clientState: ibctmtypes.NewClientState(chainID, chainID, height, commitmenttypes.GetSDKSpecs()),
// connection: conn,
// consensusState: ibctmtypes.ConsensusState{
// Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
// },
// prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
// expPass: true,
// },
{ {
name: "ApplyPrefix failed", "successful verification", func() {}, true,
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.MerklePrefix{},
expPass: false,
}, },
{ {
name: "latest client height < height", "ApplyPrefix failed", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), prefix = commitmenttypes.MerklePrefix{}
consensusState: ibctmtypes.ConsensusState{ }, false,
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "client is frozen", "latest client height < height", func() {
clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, proofHeight = clientState.LatestHeight + 1
consensusState: ibctmtypes.ConsensusState{ }, false,
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "proof verification failed", "client is frozen", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState.FrozenHeight = 1
consensusState: ibctmtypes.ConsensusState{ }, false,
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), },
NextValidatorsHash: suite.valsHash, {
}, "proof verification failed", func() {
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof = invalidProof
proof: []byte{}, }, false,
expPass: false,
}, },
} }
for i, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
err := tc.clientState.VerifyPacketAcknowledgementAbsence( suite.Run(tc.name, func() {
nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.consensusState, suite.SetupTest() // reset
)
if tc.expPass { // setup testing conditions
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
} else { packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100, 0)
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
} // send packet, but no recv
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
// need to update chainA's client representing chainB to prove missing ack
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
var ok bool
clientStateI := suite.chainA.GetClientState(clientA)
clientState, ok = clientStateI.(*types.ClientState)
suite.Require().True(ok)
prefix = suite.chainB.GetPrefix()
// make packet acknowledgement absence proof
acknowledgementKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proof, proofHeight = suite.chainB.QueryProof(acknowledgementKey)
tc.malleate() // make changes as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
err = clientState.VerifyPacketAcknowledgementAbsence(
store, suite.chainA.Codec, proofHeight, &prefix, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
} }
} }
// test verification of the next receive sequence on chainB being represented
// in the light client on chainA. A send and receive from chainB to chainA is
// simulated.
func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() { func (suite *TendermintTestSuite) TestVerifyNextSeqRecv() {
var (
clientState *types.ClientState
proof []byte
proofHeight uint64
prefix commitmenttypes.MerklePrefix
)
testCases := []struct { testCases := []struct {
name string name string
clientState *ibctmtypes.ClientState malleate func()
consensusState ibctmtypes.ConsensusState expPass bool
prefix commitmenttypes.MerklePrefix
proof []byte
expPass bool
}{ }{
// FIXME: uncomment
// {
// name: "successful verification",
// clientState: ibctmtypes.NewClientState(chainID, height, commitmenttypes.GetSDKSpecs()),
// connection: conn,
// consensusState: ibctmtypes.ConsensusState{
// Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
// },
// prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
// expPass: true,
// },
{ {
name: "ApplyPrefix failed", "successful verification", func() {}, true,
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()),
consensusState: ibctmtypes.ConsensusState{
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.MerklePrefix{},
expPass: false,
}, },
{ {
name: "latest client height < height", "ApplyPrefix failed", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), prefix = commitmenttypes.MerklePrefix{}
consensusState: ibctmtypes.ConsensusState{ }, false,
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "client is frozen", "latest client height < height", func() {
clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, proofHeight = clientState.LatestHeight + 1
consensusState: ibctmtypes.ConsensusState{ }, false,
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash),
},
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")),
expPass: false,
}, },
{ {
name: "proof verification failed", "client is frozen", func() {
clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), clientState.FrozenHeight = 1
consensusState: ibctmtypes.ConsensusState{ }, false,
Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), },
NextValidatorsHash: suite.valsHash, {
}, "proof verification failed", func() {
prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), proof = invalidProof
proof: []byte{}, }, false,
expPass: false,
}, },
} }
for i, tc := range testCases { for _, tc := range testCases {
tc := tc tc := tc
err := tc.clientState.VerifyNextSequenceRecv( suite.Run(tc.name, func() {
nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.consensusState, suite.SetupTest() // reset
)
if tc.expPass { // setup testing conditions
suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB)
} else { packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100, 0)
suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name)
} // send packet
err := suite.coordinator.SendPacket(suite.chainA, suite.chainB, packet, clientB)
suite.Require().NoError(err)
// write ack, next seq recv incremented
err = suite.coordinator.PacketExecuted(suite.chainB, suite.chainA, packet, clientA)
suite.Require().NoError(err)
// need to update chainA's client representing chainB
suite.coordinator.UpdateClient(suite.chainA, suite.chainB, clientA, clientexported.Tendermint)
var ok bool
clientStateI := suite.chainA.GetClientState(clientA)
clientState, ok = clientStateI.(*types.ClientState)
suite.Require().True(ok)
prefix = suite.chainB.GetPrefix()
// make next seq recv proof
nextSeqRecvKey := host.KeyNextSequenceRecv(packet.GetDestPort(), packet.GetDestChannel())
proof, proofHeight = suite.chainB.QueryProof(nextSeqRecvKey)
tc.malleate() // make changes as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
err = clientState.VerifyNextSequenceRecv(
store, suite.chainA.Codec, proofHeight, &prefix, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
if tc.expPass {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
}
})
} }
} }

View File

@ -0,0 +1,37 @@
package types
import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// GetConsensusState retrieves the consensus state from the client prefixed
// store. An error is returned if the consensus state does not exist.
func GetConsensusState(store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64) (*ConsensusState, error) {
bz := store.Get(host.KeyConsensusState(height))
if bz == nil {
return nil, sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"consensus state does not exist for height %d", height,
)
}
var consensusStateI clientexported.ConsensusState
if err := codec.UnmarshalAny(cdc, &consensusStateI, bz); err != nil {
return nil, err
}
consensusState, ok := consensusStateI.(*ConsensusState)
if !ok {
return nil, sdkerrors.Wrapf(
clienttypes.ErrInvalidConsensus,
"invalid consensus type %T, expected %T", consensusState, &ConsensusState{},
)
}
return consensusState, nil
}

View File

@ -0,0 +1,73 @@
package types_test
import (
"github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
func (suite *TendermintTestSuite) TestGetConsensusState() {
var (
height uint64
clientA string
)
testCases := []struct {
name string
malleate func()
expPass bool
}{
{
"success", func() {}, true,
},
{
"consensus state not found", func() {
// use height with no consensus state set
height = height + 1
}, false,
},
{
"not a consensus state interface", func() {
// marshal an empty client state and set as consensus state
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
clientStateBz := suite.chainA.App.IBCKeeper.ClientKeeper.MustMarshalClientState(&types.ClientState{})
store.Set(host.KeyConsensusState(height), clientStateBz)
}, false,
},
// TODO: uncomment upon merge of solomachine
// {
// "invalid consensus state (solomachine)", func() {
// // marshal and set solomachine consensus state
// store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
// consensusStateBz := suite.chainA.App.IBCKeeper.ClientKeeper.MustMarshalConsensusState(&solomachinetypes.ConsensusState{})
// store.Set(host.KeyConsensusState(height), consensusStateBz)
// }, false,
// },
}
for _, tc := range testCases {
tc := tc
suite.Run(tc.name, func() {
suite.SetupTest()
clientA, _, _, _, _, _ = suite.coordinator.Setup(suite.chainA, suite.chainB)
clientState := suite.chainA.GetClientState(clientA)
height = clientState.GetLatestHeight()
tc.malleate() // change vars as necessary
store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)
consensusState, err := types.GetConsensusState(store, suite.chainA.Codec, height)
if tc.expPass {
suite.Require().NoError(err)
expConsensusState, found := suite.chainA.GetConsensusState(clientA, height)
suite.Require().True(found)
suite.Require().Equal(expConsensusState, consensusState)
} else {
suite.Require().Error(err)
suite.Require().Nil(consensusState)
}
})
}
}

View File

@ -14,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
) )
const ( const (
@ -28,6 +29,13 @@ const (
type TendermintTestSuite struct { type TendermintTestSuite struct {
suite.Suite suite.Suite
coordinator *ibctesting.Coordinator
// testing chains used for convenience and readability
chainA *ibctesting.TestChain
chainB *ibctesting.TestChain
// TODO: deprecate usage in favor of testing package
ctx sdk.Context ctx sdk.Context
aminoCdc *codec.LegacyAmino aminoCdc *codec.LegacyAmino
cdc codec.Marshaler cdc codec.Marshaler
@ -39,6 +47,11 @@ type TendermintTestSuite struct {
} }
func (suite *TendermintTestSuite) SetupTest() { func (suite *TendermintTestSuite) SetupTest() {
suite.coordinator = ibctesting.NewCoordinator(suite.T(), 2)
suite.chainA = suite.coordinator.GetChain(ibctesting.GetChainID(0))
suite.chainB = suite.coordinator.GetChain(ibctesting.GetChainID(1))
// TODO: deprecate usage in favor of testing package
checkTx := false checkTx := false
app := simapp.Setup(checkTx) app := simapp.Setup(checkTx)

View File

@ -91,7 +91,6 @@ func (cs ClientState) VerifyConnectionState(
_ []byte, _ []byte,
connectionID string, connectionID string,
connectionEnd connectionexported.ConnectionI, connectionEnd connectionexported.ConnectionI,
_ clientexported.ConsensusState,
) error { ) error {
path, err := commitmenttypes.ApplyPrefix(prefix, host.ConnectionPath(connectionID)) path, err := commitmenttypes.ApplyPrefix(prefix, host.ConnectionPath(connectionID))
if err != nil { if err != nil {
@ -130,7 +129,6 @@ func (cs ClientState) VerifyChannelState(
portID, portID,
channelID string, channelID string,
channel channelexported.ChannelI, channel channelexported.ChannelI,
_ clientexported.ConsensusState,
) error { ) error {
path, err := commitmenttypes.ApplyPrefix(prefix, host.ChannelPath(portID, channelID)) path, err := commitmenttypes.ApplyPrefix(prefix, host.ChannelPath(portID, channelID))
if err != nil { if err != nil {
@ -170,7 +168,6 @@ func (cs ClientState) VerifyPacketCommitment(
channelID string, channelID string,
sequence uint64, sequence uint64,
commitmentBytes []byte, commitmentBytes []byte,
_ clientexported.ConsensusState,
) error { ) error {
path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketCommitmentPath(portID, channelID, sequence)) path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketCommitmentPath(portID, channelID, sequence))
if err != nil { if err != nil {
@ -204,7 +201,6 @@ func (cs ClientState) VerifyPacketAcknowledgement(
channelID string, channelID string,
sequence uint64, sequence uint64,
acknowledgement []byte, acknowledgement []byte,
_ clientexported.ConsensusState,
) error { ) error {
path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence)) path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence))
if err != nil { if err != nil {
@ -238,7 +234,6 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
portID, portID,
channelID string, channelID string,
sequence uint64, sequence uint64,
_ clientexported.ConsensusState,
) error { ) error {
path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence)) path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence))
if err != nil { if err != nil {
@ -264,7 +259,6 @@ func (cs ClientState) VerifyNextSequenceRecv(
portID, portID,
channelID string, channelID string,
nextSequenceRecv uint64, nextSequenceRecv uint64,
_ clientexported.ConsensusState,
) error { ) error {
path, err := commitmenttypes.ApplyPrefix(prefix, host.NextSequenceRecvPath(portID, channelID)) path, err := commitmenttypes.ApplyPrefix(prefix, host.NextSequenceRecvPath(portID, channelID))
if err != nil { if err != nil {

View File

@ -88,7 +88,7 @@ func (suite *LocalhostTestSuite) TestVerifyConnectionState() {
tc := tc tc := tc
err := tc.clientState.VerifyConnectionState( err := tc.clientState.VerifyConnectionState(
suite.store, suite.cdc, height, tc.prefix, tc.proof, testConnectionID, &tc.connection, nil, suite.store, suite.cdc, height, tc.prefix, tc.proof, testConnectionID, &tc.connection,
) )
if tc.expPass { if tc.expPass {
@ -139,7 +139,7 @@ func (suite *LocalhostTestSuite) TestVerifyChannelState() {
tc := tc tc := tc
err := tc.clientState.VerifyChannelState( err := tc.clientState.VerifyChannelState(
suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, &tc.channel, nil, suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, &tc.channel,
) )
if tc.expPass { if tc.expPass {
@ -194,7 +194,7 @@ func (suite *LocalhostTestSuite) TestVerifyPacketCommitment() {
tc := tc tc := tc
err := tc.clientState.VerifyPacketCommitment( err := tc.clientState.VerifyPacketCommitment(
suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.commitment, nil, suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.commitment,
) )
if tc.expPass { if tc.expPass {
@ -249,7 +249,7 @@ func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() {
tc := tc tc := tc
err := tc.clientState.VerifyPacketAcknowledgement( err := tc.clientState.VerifyPacketAcknowledgement(
suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.ack, nil, suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.ack,
) )
if tc.expPass { if tc.expPass {
@ -280,7 +280,7 @@ func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgementAbsence() {
tc := tc tc := tc
err := tc.clientState.VerifyPacketAcknowledgementAbsence( err := tc.clientState.VerifyPacketAcknowledgementAbsence(
suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, nil, suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence,
) )
if tc.expPass { if tc.expPass {
@ -330,7 +330,7 @@ func (suite *LocalhostTestSuite) TestVerifyNextSeqRecv() {
tc := tc tc := tc
err := tc.clientState.VerifyNextSequenceRecv( err := tc.clientState.VerifyNextSequenceRecv(
suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, nil, suite.store, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence,
) )
if tc.expPass { if tc.expPass {

View File

@ -15,6 +15,7 @@ import (
"github.com/tendermint/tendermint/version" "github.com/tendermint/tendermint/version"
"github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/simapp"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@ -70,6 +71,7 @@ type TestChain struct {
CurrentHeader abci.Header // header for current block height CurrentHeader abci.Header // header for current block height
QueryServer types.QueryServer QueryServer types.QueryServer
TxConfig client.TxConfig TxConfig client.TxConfig
Codec codec.BinaryMarshaler
Vals *tmtypes.ValidatorSet Vals *tmtypes.ValidatorSet
Signers []tmtypes.PrivValidator Signers []tmtypes.PrivValidator
@ -127,6 +129,7 @@ func NewTestChain(t *testing.T, chainID string) *TestChain {
CurrentHeader: header, CurrentHeader: header,
QueryServer: app.IBCKeeper, QueryServer: app.IBCKeeper,
TxConfig: txConfig, TxConfig: txConfig,
Codec: app.AppCodec(),
Vals: valSet, Vals: valSet,
Signers: signers, Signers: signers,
senderPrivKey: senderPrivKey, senderPrivKey: senderPrivKey,