diff --git a/modules/light-clients/06-solomachine/misbehaviour_handle.go b/modules/light-clients/06-solomachine/misbehaviour_handle.go index 26d8d0f5442..e13e23ae46e 100644 --- a/modules/light-clients/06-solomachine/misbehaviour_handle.go +++ b/modules/light-clients/06-solomachine/misbehaviour_handle.go @@ -7,8 +7,18 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" commitmenttypes "github.com/cosmos/ibc-go/v6/modules/core/23-commitment/types" + "github.com/cosmos/ibc-go/v6/modules/core/exported" ) +// CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false +func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, clientMsg exported.ClientMessage) bool { + if _, ok := clientMsg.(*Misbehaviour); ok { + return true + } + + return false +} + func (cs ClientState) verifyMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, misbehaviour *Misbehaviour) error { // NOTE: a check that the misbehaviour message data are not equal is done by // misbehaviour.ValidateBasic which is called by the 02-client keeper. diff --git a/modules/light-clients/06-solomachine/update.go b/modules/light-clients/06-solomachine/update.go index ae32cc95cae..2ccd4d74add 100644 --- a/modules/light-clients/06-solomachine/update.go +++ b/modules/light-clients/06-solomachine/update.go @@ -106,15 +106,6 @@ func (cs ClientState) UpdateState(ctx sdk.Context, cdc codec.BinaryCodec, client return []exported.Height{clienttypes.NewHeight(0, cs.Sequence)} } -// CheckForMisbehaviour returns true for type Misbehaviour (passed VerifyClientMessage check), otherwise returns false -func (cs ClientState) CheckForMisbehaviour(_ sdk.Context, _ codec.BinaryCodec, _ sdk.KVStore, clientMsg exported.ClientMessage) bool { - if _, ok := clientMsg.(*Misbehaviour); ok { - return true - } - - return false -} - // UpdateStateOnMisbehaviour updates state upon misbehaviour. This method should only be called on misbehaviour // as it does not perform any misbehaviour checks. func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) { diff --git a/modules/light-clients/07-tendermint/misbehaviour_handle.go b/modules/light-clients/07-tendermint/misbehaviour_handle.go index b5d6059a946..5b970de4584 100644 --- a/modules/light-clients/07-tendermint/misbehaviour_handle.go +++ b/modules/light-clients/07-tendermint/misbehaviour_handle.go @@ -2,6 +2,7 @@ package tendermint import ( "bytes" + "reflect" "time" "github.com/cosmos/cosmos-sdk/codec" @@ -10,8 +11,54 @@ import ( tmtypes "github.com/tendermint/tendermint/types" clienttypes "github.com/cosmos/ibc-go/v6/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v6/modules/core/exported" ) +// CheckForMisbehaviour detects duplicate height misbehaviour and BFT time violation misbehaviour +func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { + switch msg := msg.(type) { + case *Header: + tmHeader := msg + consState := tmHeader.ConsensusState() + + // Check if the Client store already has a consensus state for the header's height + // If the consensus state exists, and it matches the header then we return early + // since header has already been submitted in a previous UpdateClient. + existingConsState, _ := GetConsensusState(clientStore, cdc, tmHeader.GetHeight()) + if existingConsState != nil { + // This header has already been submitted and the necessary state is already stored + // in client store, thus we can return early without further validation. + if reflect.DeepEqual(existingConsState, tmHeader.ConsensusState()) { //nolint:gosimple + return false + } + + // A consensus state already exists for this height, but it does not match the provided header. + // The assumption is that Header has already been validated. Thus we can return true as misbehaviour is present + return true + } + + // Check that consensus state timestamps are monotonic + prevCons, prevOk := GetPreviousConsensusState(clientStore, cdc, tmHeader.GetHeight()) + nextCons, nextOk := GetNextConsensusState(clientStore, cdc, tmHeader.GetHeight()) + // if previous consensus state exists, check consensus state time is greater than previous consensus state time + // if previous consensus state is not before current consensus state return true + if prevOk && !prevCons.Timestamp.Before(consState.Timestamp) { + return true + } + // if next consensus state exists, check consensus state time is less than next consensus state time + // if next consensus state is not after current consensus state return true + if nextOk && !nextCons.Timestamp.After(consState.Timestamp) { + return true + } + case *Misbehaviour: + // The correctness of Misbehaviour ClientMessage types is ensured by calling VerifyClientMessage prior to this function + // Thus, here we can return true, as ClientMessage is of type Misbehaviour + return true + } + + return false +} + // verifyMisbehaviour determines whether or not two conflicting // headers at the same height would have convinced the light client. // diff --git a/modules/light-clients/07-tendermint/update.go b/modules/light-clients/07-tendermint/update.go index 209b5b53a62..ecd1f58e21e 100644 --- a/modules/light-clients/07-tendermint/update.go +++ b/modules/light-clients/07-tendermint/update.go @@ -3,7 +3,6 @@ package tendermint import ( "bytes" "fmt" - "reflect" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -195,51 +194,6 @@ func (cs ClientState) pruneOldestConsensusState(ctx sdk.Context, cdc codec.Binar } } -// CheckForMisbehaviour detects duplicate height misbehaviour and BFT time violation misbehaviour -func (cs ClientState) CheckForMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, msg exported.ClientMessage) bool { - switch msg := msg.(type) { - case *Header: - tmHeader := msg - consState := tmHeader.ConsensusState() - - // Check if the Client store already has a consensus state for the header's height - // If the consensus state exists, and it matches the header then we return early - // since header has already been submitted in a previous UpdateClient. - existingConsState, _ := GetConsensusState(clientStore, cdc, tmHeader.GetHeight()) - if existingConsState != nil { - // This header has already been submitted and the necessary state is already stored - // in client store, thus we can return early without further validation. - if reflect.DeepEqual(existingConsState, tmHeader.ConsensusState()) { //nolint:gosimple - return false - } - - // A consensus state already exists for this height, but it does not match the provided header. - // The assumption is that Header has already been validated. Thus we can return true as misbehaviour is present - return true - } - - // Check that consensus state timestamps are monotonic - prevCons, prevOk := GetPreviousConsensusState(clientStore, cdc, tmHeader.GetHeight()) - nextCons, nextOk := GetNextConsensusState(clientStore, cdc, tmHeader.GetHeight()) - // if previous consensus state exists, check consensus state time is greater than previous consensus state time - // if previous consensus state is not before current consensus state return true - if prevOk && !prevCons.Timestamp.Before(consState.Timestamp) { - return true - } - // if next consensus state exists, check consensus state time is less than next consensus state time - // if next consensus state is not after current consensus state return true - if nextOk && !nextCons.Timestamp.After(consState.Timestamp) { - return true - } - case *Misbehaviour: - // The correctness of Misbehaviour ClientMessage types is ensured by calling VerifyClientMessage prior to this function - // Thus, here we can return true, as ClientMessage is of type Misbehaviour - return true - } - - return false -} - // UpdateStateOnMisbehaviour updates state upon misbehaviour, freezing the ClientState. This method should only be called when misbehaviour is detected // as it does not perform any misbehaviour checks. func (cs ClientState) UpdateStateOnMisbehaviour(ctx sdk.Context, cdc codec.BinaryCodec, clientStore sdk.KVStore, _ exported.ClientMessage) {