diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index 405433f188e..30cb2eb6774 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -46,7 +46,6 @@ type ClientState interface { proof []byte, connectionID string, connectionEnd connectionexported.ConnectionI, - consensusState ConsensusState, ) error VerifyChannelState( store sdk.KVStore, @@ -57,7 +56,6 @@ type ClientState interface { portID, channelID string, channel channelexported.ChannelI, - consensusState ConsensusState, ) error VerifyPacketCommitment( store sdk.KVStore, @@ -69,7 +67,6 @@ type ClientState interface { channelID string, sequence uint64, commitmentBytes []byte, - consensusState ConsensusState, ) error VerifyPacketAcknowledgement( store sdk.KVStore, @@ -81,7 +78,6 @@ type ClientState interface { channelID string, sequence uint64, acknowledgement []byte, - consensusState ConsensusState, ) error VerifyPacketAcknowledgementAbsence( store sdk.KVStore, @@ -92,7 +88,6 @@ type ClientState interface { portID, channelID string, sequence uint64, - consensusState ConsensusState, ) error VerifyNextSequenceRecv( store sdk.KVStore, @@ -103,7 +98,6 @@ type ClientState interface { portID, channelID string, nextSequenceRecv uint64, - consensusState ConsensusState, ) error } diff --git a/x/ibc/03-connection/keeper/verify.go b/x/ibc/03-connection/keeper/verify.go index 29e9ed6d1c8..916e5391817 100644 --- a/x/ibc/03-connection/keeper/verify.go +++ b/x/ibc/03-connection/keeper/verify.go @@ -34,7 +34,7 @@ func (k Keeper) VerifyClientConsensusState( k.clientKeeper.ClientStore(ctx, clientID), k.cdc, targetConsState.GetRoot(), height, connection.GetCounterparty().GetClientID(), consensusHeight, connection.GetCounterparty().GetPrefix(), proof, consensusState, ); 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 @@ -55,22 +55,11 @@ func (k Keeper) VerifyConnectionState( 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( k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, - connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd, consensusState, + connection.GetCounterparty().GetPrefix(), proof, connectionID, connectionEnd, ); 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 @@ -92,23 +81,12 @@ func (k Keeper) VerifyChannelState( 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( k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, connection.GetCounterparty().GetPrefix(), proof, - portID, channelID, channel, consensusState, + portID, channelID, channel, ); 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 @@ -131,23 +109,12 @@ func (k Keeper) VerifyPacketCommitment( 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( k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, commitmentBytes, consensusState, + sequence, commitmentBytes, ); 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 @@ -170,23 +137,12 @@ func (k Keeper) VerifyPacketAcknowledgement( 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( k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, acknowledgement, consensusState, + sequence, acknowledgement, ); 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 @@ -209,23 +165,12 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence( 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( k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - sequence, consensusState, + sequence, ); 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 @@ -247,23 +192,12 @@ func (k Keeper) VerifyNextSequenceRecv( 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( k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height, connection.GetCounterparty().GetPrefix(), proof, portID, channelID, - nextSequenceRecv, consensusState, + nextSequenceRecv, ); 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 diff --git a/x/ibc/07-tendermint/types/client_state.go b/x/ibc/07-tendermint/types/client_state.go index 330ba3313ac..e7c6ab1d3b2 100644 --- a/x/ibc/07-tendermint/types/client_state.go +++ b/x/ibc/07-tendermint/types/client_state.go @@ -123,7 +123,7 @@ func (cs ClientState) GetProofSpecs() []*ics23.ProofSpec { // VerifyClientConsensusState verifies a proof of the consensus state of the // Tendermint client stored on the target machine. func (cs ClientState) VerifyClientConsensusState( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, provingRoot commitmentexported.Root, height uint64, @@ -133,7 +133,7 @@ func (cs ClientState) VerifyClientConsensusState( proof []byte, consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, _, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -144,6 +144,15 @@ func (cs ClientState) VerifyClientConsensusState( 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) if err != nil { return err @@ -159,16 +168,15 @@ func (cs ClientState) VerifyClientConsensusState( // VerifyConnectionState verifies a proof of the connection state of the // specified connection end stored on the target machine. func (cs ClientState) VerifyConnectionState( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64, prefix commitmentexported.Prefix, proof []byte, connectionID string, connectionEnd connectionexported.ConnectionI, - consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -198,7 +206,7 @@ func (cs ClientState) VerifyConnectionState( // VerifyChannelState verifies a proof of the channel state of the specified // channel end, under the specified port, stored on the target machine. func (cs ClientState) VerifyChannelState( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64, prefix commitmentexported.Prefix, @@ -206,9 +214,8 @@ func (cs ClientState) VerifyChannelState( portID, channelID string, channel channelexported.ChannelI, - consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -238,7 +245,7 @@ func (cs ClientState) VerifyChannelState( // VerifyPacketCommitment verifies a proof of an outgoing packet commitment at // the specified port, specified channel, and specified sequence. func (cs ClientState) VerifyPacketCommitment( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64, prefix commitmentexported.Prefix, @@ -247,9 +254,8 @@ func (cs ClientState) VerifyPacketCommitment( channelID string, sequence uint64, commitmentBytes []byte, - consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -269,7 +275,7 @@ func (cs ClientState) VerifyPacketCommitment( // VerifyPacketAcknowledgement verifies a proof of an incoming packet // acknowledgement at the specified port, specified channel, and specified sequence. func (cs ClientState) VerifyPacketAcknowledgement( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64, prefix commitmentexported.Prefix, @@ -278,9 +284,8 @@ func (cs ClientState) VerifyPacketAcknowledgement( channelID string, sequence uint64, acknowledgement []byte, - consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -301,7 +306,7 @@ func (cs ClientState) VerifyPacketAcknowledgement( // incoming packet acknowledgement at the specified port, specified channel, and // specified sequence. func (cs ClientState) VerifyPacketAcknowledgementAbsence( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64, prefix commitmentexported.Prefix, @@ -309,9 +314,8 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( portID, channelID string, sequence uint64, - consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -331,7 +335,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( // VerifyNextSequenceRecv verifies a proof of the next sequence number to be // received of the specified channel at the specified port. func (cs ClientState) VerifyNextSequenceRecv( - _ sdk.KVStore, + store sdk.KVStore, cdc codec.BinaryMarshaler, height uint64, prefix commitmentexported.Prefix, @@ -339,9 +343,8 @@ func (cs ClientState) VerifyNextSequenceRecv( portID, channelID string, nextSequenceRecv uint64, - consensusState clientexported.ConsensusState, ) error { - merkleProof, err := sanitizeVerificationArgs(cdc, cs, height, prefix, proof, consensusState) + merkleProof, consensusState, err := produceVerificationArgs(store, cdc, cs, height, prefix, proof) if err != nil { return err } @@ -360,53 +363,49 @@ func (cs ClientState) VerifyNextSequenceRecv( 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 -// merkle proof and an error if one occurred. -func sanitizeVerificationArgs( +// merkle proof, the consensus state and an error if one occurred. +func produceVerificationArgs( + store sdk.KVStore, cdc codec.BinaryMarshaler, cs ClientState, height uint64, prefix commitmentexported.Prefix, proof []byte, - consensusState clientexported.ConsensusState, -) (merkleProof commitmenttypes.MerkleProof, err error) { +) (merkleProof commitmenttypes.MerkleProof, consensusState *ConsensusState, err error) { if cs.GetLatestHeight() < height { - return commitmenttypes.MerkleProof{}, sdkerrors.Wrapf( + return commitmenttypes.MerkleProof{}, nil, sdkerrors.Wrapf( sdkerrors.ErrInvalidHeight, "client state height < proof height (%d < %d)", cs.GetLatestHeight(), height, ) } if cs.IsFrozen() && cs.FrozenHeight <= height { - return commitmenttypes.MerkleProof{}, clienttypes.ErrClientFrozen + return commitmenttypes.MerkleProof{}, nil, clienttypes.ErrClientFrozen } 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) 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 { - 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 { - 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 { - return commitmenttypes.MerkleProof{}, sdkerrors.Wrap(clienttypes.ErrInvalidConsensus, "consensus state cannot be empty") - } - - _, ok = consensusState.(*ConsensusState) - if !ok { - return commitmenttypes.MerkleProof{}, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "invalid consensus type %T, expected %T", consensusState, &ConsensusState{}) + consensusState, err = GetConsensusState(store, cdc, height) + if err != nil { + return commitmenttypes.MerkleProof{}, nil, err } - return merkleProof, nil + return merkleProof, consensusState, nil } diff --git a/x/ibc/07-tendermint/types/client_state_test.go b/x/ibc/07-tendermint/types/client_state_test.go index 77d6ae5b14b..79a12623e6e 100644 --- a/x/ibc/07-tendermint/types/client_state_test.go +++ b/x/ibc/07-tendermint/types/client_state_test.go @@ -3,11 +3,13 @@ package types_test import ( 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" "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" + host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) const ( @@ -18,6 +20,10 @@ const ( testSequence = 1 ) +var ( + invalidProof = []byte("invalid proof") +) + func (suite *TendermintTestSuite) TestValidate() { testCases := []struct { 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() { - counterparty := connectiontypes.NewCounterparty("clientB", testConnectionID, commitmenttypes.NewMerklePrefix([]byte("ibc"))) - conn := connectiontypes.NewConnectionEnd(connectiontypes.OPEN, "clientA", counterparty, []string{"1.0.0"}) + var ( + clientState *types.ClientState + proof []byte + proofHeight uint64 + prefix commitmenttypes.MerklePrefix + ) testCases := []struct { - name string - clientState *ibctmtypes.ClientState - connection connectiontypes.ConnectionEnd - consensusState ibctmtypes.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool + name string + malleate func() + 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", - 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, + "successful verification", func() {}, true, }, { - name: "latest client height < height", - 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: false, + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, }, { - name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - connection: conn, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "latest client height < height", func() { + proofHeight = clientState.LatestHeight + 1 + }, false, }, { - name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - connection: conn, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + "client is frozen", func() { + clientState.FrozenHeight = 1 + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyConnectionState( - nil, suite.cdc, height, tc.prefix, tc.proof, testConnectionID, tc.connection, tc.consensusState, - ) + suite.Run(tc.name, func() { + suite.SetupTest() // reset - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + // setup testing conditions + clientA, _, _, connB, _, _ := suite.coordinator.Setup(suite.chainA, suite.chainB) + connection := suite.chainB.GetConnection(connB) + + 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() { - counterparty := channeltypes.NewCounterparty(testPortID, testChannelID) - ch := channeltypes.NewChannel(channeltypes.OPEN, channeltypes.ORDERED, counterparty, []string{testConnectionID}, "1.0.0") + var ( + clientState *types.ClientState + proof []byte + proofHeight uint64 + prefix commitmenttypes.MerklePrefix + ) testCases := []struct { - name string - clientState *ibctmtypes.ClientState - channel channeltypes.Channel - consensusState ibctmtypes.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool + name string + malleate func() + 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", - 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, + "successful verification", func() {}, true, }, { - name: "latest client height < height", - 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.NewMerklePrefix([]byte("ibc")), - expPass: false, + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, }, { - name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - channel: ch, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "latest client height < height", func() { + proofHeight = clientState.LatestHeight + 1 + }, false, }, { - name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - channel: ch, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + "client is frozen", func() { + clientState.FrozenHeight = 1 + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyChannelState( - nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, &tc.channel, tc.consensusState, - ) + suite.Run(tc.name, func() { + suite.SetupTest() // reset - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + // setup testing conditions + clientA, _, _, _, _, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + channel := suite.chainB.GetChannel(channelB) + + 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() { + var ( + clientState *types.ClientState + proof []byte + proofHeight uint64 + prefix commitmenttypes.MerklePrefix + ) + testCases := []struct { - name string - clientState *ibctmtypes.ClientState - commitment []byte - consensusState ibctmtypes.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool + name string + malleate func() + 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", - 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, + "successful verification", func() {}, true, }, { - name: "latest client height < height", - 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.NewMerklePrefix([]byte("ibc")), - expPass: false, + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, }, { - name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - commitment: []byte{}, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "latest client height < height", func() { + proofHeight = clientState.LatestHeight + 1 + }, false, }, { - name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - commitment: []byte{}, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + "client is frozen", func() { + clientState.FrozenHeight = 1 + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyPacketCommitment( - nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.commitment, tc.consensusState, - ) + suite.Run(tc.name, func() { + suite.SetupTest() // reset - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + // setup testing conditions + clientA, _, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelB.PortID, channelB.ID, channelA.PortID, channelA.ID, 100, 0) + 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() { + var ( + clientState *types.ClientState + proof []byte + proofHeight uint64 + prefix commitmenttypes.MerklePrefix + ) + testCases := []struct { - name string - clientState *ibctmtypes.ClientState - ack []byte - consensusState ibctmtypes.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool + name string + malleate func() + 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", - 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, + "successful verification", func() {}, true, }, { - name: "latest client height < height", - 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.NewMerklePrefix([]byte("ibc")), - expPass: false, + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, }, { - name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - ack: []byte{}, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "latest client height < height", func() { + proofHeight = clientState.LatestHeight + 1 + }, false, }, { - name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - ack: []byte{}, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + "client is frozen", func() { + clientState.FrozenHeight = 1 + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyPacketAcknowledgement( - nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.ack, tc.consensusState, - ) + suite.Run(tc.name, func() { + suite.SetupTest() // reset - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + // setup testing conditions + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100, 0) + + // 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() { + var ( + clientState *types.ClientState + proof []byte + proofHeight uint64 + prefix commitmenttypes.MerklePrefix + ) + testCases := []struct { - name string - clientState *ibctmtypes.ClientState - consensusState ibctmtypes.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool + name string + malleate func() + 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", - 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, + "successful verification", func() {}, true, }, { - name: "latest client height < height", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, }, { - name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "latest client height < height", func() { + proofHeight = clientState.LatestHeight + 1 + }, false, }, { - name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + "client is frozen", func() { + clientState.FrozenHeight = 1 + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyPacketAcknowledgementAbsence( - nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.consensusState, - ) + suite.Run(tc.name, func() { + suite.SetupTest() // reset - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + // setup testing conditions + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100, 0) + + // 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() { + var ( + clientState *types.ClientState + proof []byte + proofHeight uint64 + prefix commitmenttypes.MerklePrefix + ) + testCases := []struct { - name string - clientState *ibctmtypes.ClientState - consensusState ibctmtypes.ConsensusState - prefix commitmenttypes.MerklePrefix - proof []byte - expPass bool + name string + malleate func() + 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", - 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, + "successful verification", func() {}, true, }, { - name: "latest client height < height", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "ApplyPrefix failed", func() { + prefix = commitmenttypes.MerklePrefix{} + }, false, }, { - name: "client is frozen", - clientState: &ibctmtypes.ClientState{LatestHeight: height, FrozenHeight: height - 1}, - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - expPass: false, + "latest client height < height", func() { + proofHeight = clientState.LatestHeight + 1 + }, false, }, { - name: "proof verification failed", - clientState: ibctmtypes.NewClientState(chainID, types.DefaultTrustLevel, trustingPeriod, ubdPeriod, maxClockDrift, height, commitmenttypes.GetSDKSpecs()), - consensusState: ibctmtypes.ConsensusState{ - Root: commitmenttypes.NewMerkleRoot(suite.header.AppHash), - NextValidatorsHash: suite.valsHash, - }, - prefix: commitmenttypes.NewMerklePrefix([]byte("ibc")), - proof: []byte{}, - expPass: false, + "client is frozen", func() { + clientState.FrozenHeight = 1 + }, false, + }, + { + "proof verification failed", func() { + proof = invalidProof + }, false, }, } - for i, tc := range testCases { + for _, tc := range testCases { tc := tc - err := tc.clientState.VerifyNextSequenceRecv( - nil, suite.cdc, height, tc.prefix, tc.proof, testPortID, testChannelID, testSequence, tc.consensusState, - ) + suite.Run(tc.name, func() { + suite.SetupTest() // reset - if tc.expPass { - suite.Require().NoError(err, "valid test case %d failed: %s", i, tc.name) - } else { - suite.Require().Error(err, "invalid test case %d passed: %s", i, tc.name) - } + // setup testing conditions + clientA, clientB, _, _, channelA, channelB := suite.coordinator.Setup(suite.chainA, suite.chainB) + packet := channeltypes.NewPacket(ibctesting.TestHash, 1, channelA.PortID, channelA.ID, channelB.PortID, channelB.ID, 100, 0) + + // 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) + } + }) } } diff --git a/x/ibc/07-tendermint/types/store.go b/x/ibc/07-tendermint/types/store.go new file mode 100644 index 00000000000..d0760778360 --- /dev/null +++ b/x/ibc/07-tendermint/types/store.go @@ -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 +} diff --git a/x/ibc/07-tendermint/types/store_test.go b/x/ibc/07-tendermint/types/store_test.go new file mode 100644 index 00000000000..7eb0e95a8a0 --- /dev/null +++ b/x/ibc/07-tendermint/types/store_test.go @@ -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) + } + }) + } +} diff --git a/x/ibc/07-tendermint/types/tendermint_test.go b/x/ibc/07-tendermint/types/tendermint_test.go index 372d30aeb86..572ebdb2a80 100644 --- a/x/ibc/07-tendermint/types/tendermint_test.go +++ b/x/ibc/07-tendermint/types/tendermint_test.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types" + ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing" ) const ( @@ -28,6 +29,13 @@ const ( type TendermintTestSuite struct { 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 aminoCdc *codec.LegacyAmino cdc codec.Marshaler @@ -39,6 +47,11 @@ type TendermintTestSuite struct { } 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 app := simapp.Setup(checkTx) diff --git a/x/ibc/09-localhost/types/client_state.go b/x/ibc/09-localhost/types/client_state.go index a8de30cee0d..3421d4cb3f1 100644 --- a/x/ibc/09-localhost/types/client_state.go +++ b/x/ibc/09-localhost/types/client_state.go @@ -91,7 +91,6 @@ func (cs ClientState) VerifyConnectionState( _ []byte, connectionID string, connectionEnd connectionexported.ConnectionI, - _ clientexported.ConsensusState, ) error { path, err := commitmenttypes.ApplyPrefix(prefix, host.ConnectionPath(connectionID)) if err != nil { @@ -130,7 +129,6 @@ func (cs ClientState) VerifyChannelState( portID, channelID string, channel channelexported.ChannelI, - _ clientexported.ConsensusState, ) error { path, err := commitmenttypes.ApplyPrefix(prefix, host.ChannelPath(portID, channelID)) if err != nil { @@ -170,7 +168,6 @@ func (cs ClientState) VerifyPacketCommitment( channelID string, sequence uint64, commitmentBytes []byte, - _ clientexported.ConsensusState, ) error { path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketCommitmentPath(portID, channelID, sequence)) if err != nil { @@ -204,7 +201,6 @@ func (cs ClientState) VerifyPacketAcknowledgement( channelID string, sequence uint64, acknowledgement []byte, - _ clientexported.ConsensusState, ) error { path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { @@ -238,7 +234,6 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence( portID, channelID string, sequence uint64, - _ clientexported.ConsensusState, ) error { path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence)) if err != nil { @@ -264,7 +259,6 @@ func (cs ClientState) VerifyNextSequenceRecv( portID, channelID string, nextSequenceRecv uint64, - _ clientexported.ConsensusState, ) error { path, err := commitmenttypes.ApplyPrefix(prefix, host.NextSequenceRecvPath(portID, channelID)) if err != nil { diff --git a/x/ibc/09-localhost/types/client_state_test.go b/x/ibc/09-localhost/types/client_state_test.go index 7666ae8af4b..62cd5dfe444 100644 --- a/x/ibc/09-localhost/types/client_state_test.go +++ b/x/ibc/09-localhost/types/client_state_test.go @@ -88,7 +88,7 @@ func (suite *LocalhostTestSuite) TestVerifyConnectionState() { tc := tc 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 { @@ -139,7 +139,7 @@ func (suite *LocalhostTestSuite) TestVerifyChannelState() { tc := tc 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 { @@ -194,7 +194,7 @@ func (suite *LocalhostTestSuite) TestVerifyPacketCommitment() { tc := tc 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 { @@ -249,7 +249,7 @@ func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() { tc := tc 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 { @@ -280,7 +280,7 @@ func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgementAbsence() { tc := tc 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 { @@ -330,7 +330,7 @@ func (suite *LocalhostTestSuite) TestVerifyNextSeqRecv() { tc := tc 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 { diff --git a/x/ibc/testing/chain.go b/x/ibc/testing/chain.go index 655d66f3208..3d308f5c510 100644 --- a/x/ibc/testing/chain.go +++ b/x/ibc/testing/chain.go @@ -15,6 +15,7 @@ import ( "github.com/tendermint/tendermint/version" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -70,6 +71,7 @@ type TestChain struct { CurrentHeader abci.Header // header for current block height QueryServer types.QueryServer TxConfig client.TxConfig + Codec codec.BinaryMarshaler Vals *tmtypes.ValidatorSet Signers []tmtypes.PrivValidator @@ -127,6 +129,7 @@ func NewTestChain(t *testing.T, chainID string) *TestChain { CurrentHeader: header, QueryServer: app.IBCKeeper, TxConfig: txConfig, + Codec: app.AppCodec(), Vals: valSet, Signers: signers, senderPrivKey: senderPrivKey,