package tendermint import ( "time" errorsmod "cosmossdk.io/errors" cmtproto "git.cw.tr/mukan-network/mukan-consensus/proto/tendermint/types" cmttypes "git.cw.tr/mukan-network/mukan-consensus/types" clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types" host "git.cw.tr/mukan-network/mukan-ibc/modules/core/24-host" "git.cw.tr/mukan-network/mukan-ibc/modules/core/exported" ) var _ exported.ClientMessage = (*Misbehaviour)(nil) // FrozenHeight is same for all misbehaviour var FrozenHeight = clienttypes.NewHeight(0, 1) // NewMisbehaviour creates a new Misbehaviour instance. func NewMisbehaviour(clientID string, header1, header2 *Header) *Misbehaviour { return &Misbehaviour{ ClientId: clientID, Header1: header1, Header2: header2, } } // ClientType is Tendermint light client func (Misbehaviour) ClientType() string { return exported.Tendermint } // GetTime returns the timestamp at which misbehaviour occurred. It uses the // maximum value from both headers to prevent producing an invalid header outside // of the misbehaviour age range. func (misbehaviour Misbehaviour) GetTime() time.Time { t1, t2 := misbehaviour.Header1.GetTime(), misbehaviour.Header2.GetTime() if t1.After(t2) { return t1 } return t2 } // ValidateBasic implements Misbehaviour interface func (misbehaviour Misbehaviour) ValidateBasic() error { if misbehaviour.Header1 == nil { return errorsmod.Wrap(ErrInvalidHeader, "misbehaviour Header1 cannot be nil") } if misbehaviour.Header2 == nil { return errorsmod.Wrap(ErrInvalidHeader, "misbehaviour Header2 cannot be nil") } if misbehaviour.Header1.TrustedHeight.RevisionHeight == 0 { return errorsmod.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header1 cannot have zero revision height") } if misbehaviour.Header2.TrustedHeight.RevisionHeight == 0 { return errorsmod.Wrapf(ErrInvalidHeaderHeight, "misbehaviour Header2 cannot have zero revision height") } if misbehaviour.Header1.TrustedValidators == nil { return errorsmod.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header1 cannot be empty") } if misbehaviour.Header2.TrustedValidators == nil { return errorsmod.Wrap(ErrInvalidValidatorSet, "trusted validator set in Header2 cannot be empty") } if misbehaviour.Header1.Header.ChainID != misbehaviour.Header2.Header.ChainID { return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "headers must have identical chainIDs") } if err := host.ClientIdentifierValidator(misbehaviour.ClientId); err != nil { return errorsmod.Wrap(err, "misbehaviour client ID is invalid") } // ValidateBasic on both validators if err := misbehaviour.Header1.ValidateBasic(); err != nil { return errorsmod.Wrap( clienttypes.ErrInvalidMisbehaviour, errorsmod.Wrap(err, "header 1 failed validation").Error(), ) } if err := misbehaviour.Header2.ValidateBasic(); err != nil { return errorsmod.Wrap( clienttypes.ErrInvalidMisbehaviour, errorsmod.Wrap(err, "header 2 failed validation").Error(), ) } // Ensure that Height1 is greater than or equal to Height2 if misbehaviour.Header1.GetHeight().LT(misbehaviour.Header2.GetHeight()) { return errorsmod.Wrapf(clienttypes.ErrInvalidMisbehaviour, "Header1 height is less than Header2 height (%s < %s)", misbehaviour.Header1.GetHeight(), misbehaviour.Header2.GetHeight()) } blockID1, err := cmttypes.BlockIDFromProto(&misbehaviour.Header1.Commit.BlockID) if err != nil { return errorsmod.Wrap(err, "invalid block ID from header 1 in misbehaviour") } blockID2, err := cmttypes.BlockIDFromProto(&misbehaviour.Header2.Commit.BlockID) if err != nil { return errorsmod.Wrap(err, "invalid block ID from header 2 in misbehaviour") } if err := validCommit(misbehaviour.Header1.Header.ChainID, *blockID1, misbehaviour.Header1.Commit, misbehaviour.Header1.ValidatorSet); err != nil { return err } return validCommit(misbehaviour.Header2.Header.ChainID, *blockID2, misbehaviour.Header2.Commit, misbehaviour.Header2.ValidatorSet) } // validCommit checks if the given commit is a valid commit from the passed-in validatorset func validCommit(chainID string, blockID cmttypes.BlockID, commit *cmtproto.Commit, valSet *cmtproto.ValidatorSet) (err error) { tmCommit, err := cmttypes.CommitFromProto(commit) if err != nil { return errorsmod.Wrap(err, "commit is not tendermint commit type") } tmValset, err := cmttypes.ValidatorSetFromProto(valSet) if err != nil { return errorsmod.Wrap(err, "validator set is not tendermint validator set type") } if err := tmValset.VerifyCommitLight(chainID, blockID, tmCommit.Height, tmCommit); err != nil { return errorsmod.Wrap(clienttypes.ErrInvalidMisbehaviour, "validator set did not commit to header") } return nil }