diff --git a/x/ibc/02-client/alias.go b/x/ibc/02-client/alias.go index aedc47bfdc76..00519d9da512 100644 --- a/x/ibc/02-client/alias.go +++ b/x/ibc/02-client/alias.go @@ -9,20 +9,21 @@ package client import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) const ( - DefaultCodespace = types.DefaultCodespace - CodeClientExists = types.CodeClientExists - CodeClientNotFound = types.CodeClientNotFound - CodeClientFrozen = types.CodeClientFrozen - CodeConsensusStateNotFound = types.CodeConsensusStateNotFound - CodeInvalidConsensusState = types.CodeInvalidConsensusState - CodeClientTypeNotFound = types.CodeClientTypeNotFound - CodeInvalidClientType = types.CodeInvalidClientType - CodeRootNotFound = types.CodeRootNotFound - CodeInvalidHeader = types.CodeInvalidHeader - CodeInvalidEvidence = types.CodeInvalidEvidence + DefaultCodespace = errors.DefaultCodespace + CodeClientExists = errors.CodeClientExists + CodeClientNotFound = errors.CodeClientNotFound + CodeClientFrozen = errors.CodeClientFrozen + CodeConsensusStateNotFound = errors.CodeConsensusStateNotFound + CodeInvalidConsensusState = errors.CodeInvalidConsensusState + CodeClientTypeNotFound = errors.CodeClientTypeNotFound + CodeInvalidClientType = errors.CodeInvalidClientType + CodeRootNotFound = errors.CodeRootNotFound + CodeInvalidHeader = errors.CodeInvalidHeader + CodeInvalidEvidence = errors.CodeInvalidEvidence AttributeKeyClientID = types.AttributeKeyClientID SubModuleName = types.SubModuleName StoreKey = types.StoreKey @@ -40,16 +41,16 @@ var ( QuerierConsensusState = keeper.QuerierConsensusState QuerierVerifiedRoot = keeper.QuerierVerifiedRoot RegisterCodec = types.RegisterCodec - ErrClientExists = types.ErrClientExists - ErrClientNotFound = types.ErrClientNotFound - ErrClientFrozen = types.ErrClientFrozen - ErrConsensusStateNotFound = types.ErrConsensusStateNotFound - ErrInvalidConsensus = types.ErrInvalidConsensus - ErrClientTypeNotFound = types.ErrClientTypeNotFound - ErrInvalidClientType = types.ErrInvalidClientType - ErrRootNotFound = types.ErrRootNotFound - ErrInvalidHeader = types.ErrInvalidHeader - ErrInvalidEvidence = types.ErrInvalidEvidence + ErrClientExists = errors.ErrClientExists + ErrClientNotFound = errors.ErrClientNotFound + ErrClientFrozen = errors.ErrClientFrozen + ErrConsensusStateNotFound = errors.ErrConsensusStateNotFound + ErrInvalidConsensus = errors.ErrInvalidConsensus + ErrClientTypeNotFound = errors.ErrClientTypeNotFound + ErrInvalidClientType = errors.ErrInvalidClientType + ErrRootNotFound = errors.ErrRootNotFound + ErrInvalidHeader = errors.ErrInvalidHeader + ErrInvalidEvidence = errors.ErrInvalidEvidence ClientStatePath = types.ClientStatePath ClientTypePath = types.ClientTypePath ConsensusStatePath = types.ConsensusStatePath diff --git a/x/ibc/02-client/exported/exported.go b/x/ibc/02-client/exported/exported.go index ff5f586a75cb..e7d1ac5c5411 100644 --- a/x/ibc/02-client/exported/exported.go +++ b/x/ibc/02-client/exported/exported.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" + cmn "github.com/tendermint/tendermint/libs/common" ) // Blockchain is consensus algorithm which generates valid Headers. It generates @@ -37,6 +38,7 @@ type Evidence interface { Route() string Type() string String() string + Hash() cmn.HexBytes ValidateBasic() sdk.Error // The consensus address of the malicious validator at time of infraction diff --git a/x/ibc/02-client/keeper/client.go b/x/ibc/02-client/keeper/client.go index e180919dec7c..aa1faf956625 100644 --- a/x/ibc/02-client/keeper/client.go +++ b/x/ibc/02-client/keeper/client.go @@ -7,6 +7,7 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) // CreateClient creates a new client state and populates it with a given consensus @@ -17,7 +18,7 @@ func (k Keeper) CreateClient( ) (types.State, error) { _, found := k.GetClientState(ctx, clientID) if found { - return types.State{}, types.ErrClientExists(k.codespace, clientID) + return types.State{}, errors.ErrClientExists(k.codespace, clientID) } _, found = k.GetClientType(ctx, clientID) @@ -36,26 +37,26 @@ func (k Keeper) CreateClient( func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.Header) error { clientType, found := k.GetClientType(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrClientTypeNotFound(k.codespace), "cannot update client") + return sdkerrors.Wrap(errors.ErrClientTypeNotFound(k.codespace), "cannot update client") } // check that the header consensus matches the client one if header.ClientType() != clientType { - return sdkerrors.Wrap(types.ErrInvalidConsensus(k.codespace), "cannot update client") + return sdkerrors.Wrap(errors.ErrInvalidConsensus(k.codespace), "cannot update client") } clientState, found := k.GetClientState(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrClientNotFound(k.codespace, clientID), "cannot update client") + return sdkerrors.Wrap(errors.ErrClientNotFound(k.codespace, clientID), "cannot update client") } if clientState.Frozen { - return sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientID), "cannot update client") + return sdkerrors.Wrap(errors.ErrClientFrozen(k.codespace, clientID), "cannot update client") } consensusState, found := k.GetConsensusState(ctx, clientID) if !found { - return sdkerrors.Wrap(types.ErrConsensusStateNotFound(k.codespace), "cannot update client") + return sdkerrors.Wrap(errors.ErrConsensusStateNotFound(k.codespace), "cannot update client") } if header.GetHeight() < consensusState.GetHeight() { @@ -83,7 +84,7 @@ func (k Keeper) UpdateClient(ctx sdk.Context, clientID string, header exported.H func (k Keeper) CheckMisbehaviourAndUpdateState(ctx sdk.Context, clientID string, evidence exported.Evidence) error { clientState, found := k.GetClientState(ctx, clientID) if !found { - sdk.ResultFromError(types.ErrClientNotFound(k.codespace, clientID)) + sdk.ResultFromError(errors.ErrClientNotFound(k.codespace, clientID)) } err := k.checkMisbehaviour(ctx, evidence) diff --git a/x/ibc/02-client/keeper/keeper.go b/x/ibc/02-client/keeper/keeper.go index c9f7fbe1c9f7..f753f88656d5 100644 --- a/x/ibc/02-client/keeper/keeper.go +++ b/x/ibc/02-client/keeper/keeper.go @@ -11,6 +11,8 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" commitment "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -29,8 +31,8 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, codespace sdk.CodespaceType) return Keeper{ storeKey: key, cdc: cdc, - codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, types.DefaultCodespace)), // "ibc/client", - prefix: []byte(types.SubModuleName + "/"), // "client/" + codespace: sdk.CodespaceType(fmt.Sprintf("%s/%s", codespace, errors.DefaultCodespace)), // "ibc/client", + prefix: []byte(types.SubModuleName + "/"), // "client/" } } @@ -127,25 +129,27 @@ func (k Keeper) initialize(ctx sdk.Context, clientID string, consensusState expo } func (k Keeper) checkMisbehaviour(ctx sdk.Context, evidence exported.Evidence) error { - // switch evidence.H1().ClientType() { - // case exported.Tendermint: - // var tmEvidence tendermint.Evidence - // _, ok := evidence.(tendermint.Evidence) - // if !ok { - // return sdkerrors.Wrap(types.ErrInvalidClientType(k.codespace), "consensus type is not Tendermint") - // } - // // TODO: pass past consensus states - // return tendermint.CheckMisbehaviour(tmEvidence) - // default: - // panic("unregistered consensus type") - // } + switch evidence.Type() { + case exported.ClientTypeTendermint: + var tmEvidence tendermint.Evidence + _, ok := evidence.(tendermint.Evidence) + if !ok { + return errors.ErrInvalidClientType(k.codespace, "consensus type is not Tendermint") + } + err := tendermint.CheckMisbehaviour(tmEvidence) + if err != nil { + return errors.ErrInvalidEvidence(k.codespace, err.Error()) + } + default: + panic(fmt.Sprintf("unregistered evidence type: %s", evidence.Type())) + } return nil } // freeze updates the state of the client in the event of a misbehaviour func (k Keeper) freeze(ctx sdk.Context, clientState types.State) (types.State, error) { if clientState.Frozen { - return types.State{}, sdkerrors.Wrap(types.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") + return types.State{}, sdkerrors.Wrap(errors.ErrClientFrozen(k.codespace, clientState.ID()), "already frozen") } clientState.Frozen = true diff --git a/x/ibc/02-client/keeper/querier.go b/x/ibc/02-client/keeper/querier.go index 8c9ce5a263ef..5f56b8d6b01a 100644 --- a/x/ibc/02-client/keeper/querier.go +++ b/x/ibc/02-client/keeper/querier.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" ) // QuerierClientState defines the sdk.Querier to query the IBC client state @@ -20,7 +21,7 @@ func QuerierClientState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byt clientState, found := k.GetClientState(ctx, params.ClientID) if !found { - return nil, types.ErrClientTypeNotFound(k.codespace) + return nil, errors.ErrClientTypeNotFound(k.codespace) } bz, err := types.SubModuleCdc.MarshalJSON(clientState) @@ -42,7 +43,7 @@ func QuerierConsensusState(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([] consensusState, found := k.GetConsensusState(ctx, params.ClientID) if !found { - return nil, types.ErrConsensusStateNotFound(k.codespace) + return nil, errors.ErrConsensusStateNotFound(k.codespace) } bz, err := types.SubModuleCdc.MarshalJSON(consensusState) @@ -64,7 +65,7 @@ func QuerierVerifiedRoot(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]by root, found := k.GetVerifiedRoot(ctx, params.ClientID, params.Height) if !found { - return nil, types.ErrRootNotFound(k.codespace) + return nil, errors.ErrRootNotFound(k.codespace) } bz, err := types.SubModuleCdc.MarshalJSON(root) diff --git a/x/ibc/02-client/types/codec.go b/x/ibc/02-client/types/codec.go index eae86ec2b53d..69985c6044b4 100644 --- a/x/ibc/02-client/types/codec.go +++ b/x/ibc/02-client/types/codec.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/tendermint" ) -var SubModuleCdc *codec.Codec +var SubModuleCdc = codec.New() // RegisterCodec registers the IBC client interfaces and types func RegisterCodec(cdc *codec.Codec) { @@ -21,4 +21,9 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(tendermint.ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) cdc.RegisterConcrete(tendermint.Header{}, "ibc/client/tendermint/Header", nil) + cdc.RegisterConcrete(tendermint.Evidence{}, "ibc/client/tendermint/Evidence", nil) +} + +func init() { + RegisterCodec(SubModuleCdc) } diff --git a/x/ibc/02-client/types/errors.go b/x/ibc/02-client/types/errors/errors.go similarity index 94% rename from x/ibc/02-client/types/errors.go rename to x/ibc/02-client/types/errors/errors.go index d4e6e9bb5558..c133bcfef808 100644 --- a/x/ibc/02-client/types/errors.go +++ b/x/ibc/02-client/types/errors/errors.go @@ -1,4 +1,4 @@ -package types +package errors import ( "fmt" @@ -8,7 +8,7 @@ import ( // client error codes const ( - DefaultCodespace sdk.CodespaceType = SubModuleName + DefaultCodespace sdk.CodespaceType = "client" CodeClientExists sdk.CodeType = 101 CodeClientNotFound sdk.CodeType = 102 @@ -68,6 +68,6 @@ func ErrInvalidHeader(codespace sdk.CodespaceType) sdk.Error { } // ErrInvalidEvidence implements sdk.Error -func ErrInvalidEvidence(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidEvidence, "invalid evidence") +func ErrInvalidEvidence(codespace sdk.CodespaceType, msg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidEvidence, "invalid evidence: %s", msg) } diff --git a/x/ibc/02-client/types/msgs.go b/x/ibc/02-client/types/msgs.go index c9d46cafa4db..17bd0786a91f 100644 --- a/x/ibc/02-client/types/msgs.go +++ b/x/ibc/02-client/types/msgs.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" host "github.com/cosmos/cosmos-sdk/x/ibc/24-host" ibctypes "github.com/cosmos/cosmos-sdk/x/ibc/types" ) @@ -45,10 +46,10 @@ func (msg MsgCreateClient) ValidateBasic() sdk.Error { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if _, err := exported.ClientTypeFromString(msg.ClientType); err != nil { - return ErrInvalidClientType(DefaultCodespace, err.Error()) + return errors.ErrInvalidClientType(errors.DefaultCodespace, err.Error()) } if msg.ConsensusState == nil { - return ErrInvalidConsensus(DefaultCodespace) + return errors.ErrInvalidConsensus(errors.DefaultCodespace) } if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") @@ -100,7 +101,7 @@ func (msg MsgUpdateClient) ValidateBasic() sdk.Error { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if msg.Header == nil { - return ErrInvalidHeader(DefaultCodespace) + return errors.ErrInvalidHeader(errors.DefaultCodespace) } if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") @@ -150,7 +151,10 @@ func (msg MsgSubmitMisbehaviour) ValidateBasic() sdk.Error { return sdk.NewError(host.IBCCodeSpace, 1, fmt.Sprintf("invalid client ID: %s", err.Error())) } if msg.Evidence == nil { - return ErrInvalidEvidence(DefaultCodespace) + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "evidence is nil") + } + if err := msg.Evidence.ValidateBasic(); err != nil { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, err.Error()) } if msg.Signer.Empty() { return sdk.ErrInvalidAddress("empty address") diff --git a/x/ibc/02-client/types/tendermint/codec.go b/x/ibc/02-client/types/tendermint/codec.go new file mode 100644 index 000000000000..46814fec2479 --- /dev/null +++ b/x/ibc/02-client/types/tendermint/codec.go @@ -0,0 +1,18 @@ +package tendermint + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var SubModuleCdc = codec.New() + +// RegisterCodec registers the Tendermint types +func RegisterCodec(cdc *codec.Codec) { + cdc.RegisterConcrete(ConsensusState{}, "ibc/client/tendermint/ConsensusState", nil) + cdc.RegisterConcrete(Header{}, "ibc/client/tendermint/Header", nil) + cdc.RegisterConcrete(Evidence{}, "ibc/client/tendermint/Evidence", nil) +} + +func init() { + RegisterCodec(SubModuleCdc) +} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour.go b/x/ibc/02-client/types/tendermint/misbehaviour.go index 229a0ab36967..512bb0f72161 100644 --- a/x/ibc/02-client/types/tendermint/misbehaviour.go +++ b/x/ibc/02-client/types/tendermint/misbehaviour.go @@ -1,12 +1,96 @@ package tendermint import ( + "fmt" + + yaml "gopkg.in/yaml.v2" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported" + "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types/errors" + + "github.com/tendermint/tendermint/crypto/tmhash" + cmn "github.com/tendermint/tendermint/libs/common" + tmtypes "github.com/tendermint/tendermint/types" ) -// CheckMisbehaviour checks if the evidence provided is a misbehaviour -func CheckMisbehaviour(evidence exported.Evidence) sdk.Error { - // TODO: check evidence +var _ exported.Evidence = Evidence{} + +// Evidence is a wrapper over tendermint's DuplicateVoteEvidence +// that implements Evidence interface expected by ICS-02 +type Evidence struct { + *tmtypes.DuplicateVoteEvidence + ChainID string `json:"chain_id" yaml:"chain_id"` + ValidatorPower int64 `json:"val_power" yaml:"val_power"` + TotalPower int64 `json:"total_power" yaml:"total_power"` +} + +// Type implements exported.Evidence interface +func (ev Evidence) Route() string { + return exported.ClientTypeTendermint +} + +// Type implements exported.Evidence interface +func (ev Evidence) Type() string { + return exported.ClientTypeTendermint +} + +// String implements exported.Evidence interface +func (ev Evidence) String() string { + bz, err := yaml.Marshal(ev) + if err != nil { + panic(err) + } + return string(bz) +} + +// Hash implements exported.Evidence interface +func (ev Evidence) Hash() cmn.HexBytes { + return tmhash.Sum(SubModuleCdc.MustMarshalBinaryBare(ev)) +} + +// ValidateBasic implements exported.Evidence interface +func (ev Evidence) ValidateBasic() sdk.Error { + if ev.DuplicateVoteEvidence == nil { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "duplicate evidence is nil") + } + err := ev.DuplicateVoteEvidence.ValidateBasic() + if err != nil { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, err.Error()) + } + if ev.ChainID == "" { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, "chainID is empty") + } + if ev.ValidatorPower <= 0 { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, fmt.Sprintf("Invalid Validator Power: %d", ev.ValidatorPower)) + } + if ev.TotalPower < ev.ValidatorPower { + return errors.ErrInvalidEvidence(errors.DefaultCodespace, fmt.Sprintf("Invalid Total Power: %d", ev.TotalPower)) + } return nil } + +// GetConsensusAddress implements exported.Evidence interface +func (ev Evidence) GetConsensusAddress() sdk.ConsAddress { + return sdk.ConsAddress(ev.DuplicateVoteEvidence.Address()) +} + +// GetHeight implements exported.Evidence interface +func (ev Evidence) GetHeight() int64 { + return ev.DuplicateVoteEvidence.Height() +} + +// GetValidatorPower implements exported.Evidence interface +func (ev Evidence) GetValidatorPower() int64 { + return ev.ValidatorPower +} + +// GetTotalPower implements exported.Evidence interface +func (ev Evidence) GetTotalPower() int64 { + return ev.TotalPower +} + +// CheckMisbehaviour checks if the evidence provided is a misbehaviour +func CheckMisbehaviour(evidence Evidence) error { + return evidence.DuplicateVoteEvidence.Verify(evidence.ChainID, evidence.DuplicateVoteEvidence.PubKey) +} diff --git a/x/ibc/02-client/types/tendermint/misbehaviour_test.go b/x/ibc/02-client/types/tendermint/misbehaviour_test.go new file mode 100644 index 000000000000..fb432e32bcea --- /dev/null +++ b/x/ibc/02-client/types/tendermint/misbehaviour_test.go @@ -0,0 +1,107 @@ +package tendermint + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/tmhash" + tmtypes "github.com/tendermint/tendermint/types" + yaml "gopkg.in/yaml.v2" +) + +// Copied unimported test functions from tmtypes to use them here +func makeBlockID(hash []byte, partSetSize int, partSetHash []byte) tmtypes.BlockID { + return tmtypes.BlockID{ + Hash: hash, + PartsHeader: tmtypes.PartSetHeader{ + Total: partSetSize, + Hash: partSetHash, + }, + } + +} + +func makeVote(val tmtypes.PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID tmtypes.BlockID) *tmtypes.Vote { + addr := val.GetPubKey().Address() + v := &tmtypes.Vote{ + ValidatorAddress: addr, + ValidatorIndex: valIndex, + Height: height, + Round: round, + Type: tmtypes.SignedMsgType(step), + BlockID: blockID, + } + err := val.SignVote(chainID, v) + if err != nil { + panic(err) + } + return v +} + +func randomDuplicatedVoteEvidence() *tmtypes.DuplicateVoteEvidence { + val := tmtypes.NewMockPV() + blockID := makeBlockID(tmhash.Sum([]byte("blockhash")), 1000, tmhash.Sum([]byte("partshash"))) + blockID2 := makeBlockID(tmhash.Sum([]byte("blockhash2")), 1000, tmhash.Sum([]byte("partshash"))) + const chainID = "mychain" + return &tmtypes.DuplicateVoteEvidence{ + PubKey: val.GetPubKey(), + VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), + VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), + } +} + +func TestString(t *testing.T) { + dupEv := randomDuplicatedVoteEvidence() + ev := Evidence{ + DuplicateVoteEvidence: dupEv, + ChainID: "mychain", + ValidatorPower: 10, + TotalPower: 50, + } + + byteStr, err := yaml.Marshal(ev) + require.Nil(t, err) + require.Equal(t, string(byteStr), ev.String(), "Evidence String method does not work as expected") + +} + +func TestValidateBasic(t *testing.T) { + dupEv := randomDuplicatedVoteEvidence() + + // good evidence + ev := Evidence{ + DuplicateVoteEvidence: dupEv, + ChainID: "mychain", + ValidatorPower: 10, + TotalPower: 50, + } + + err := ev.ValidateBasic() + require.Nil(t, err, "good evidence failed on ValidateBasic: %v", err) + + // invalid duplicate evidence + ev.DuplicateVoteEvidence.VoteA = nil + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid duplicate evidence passed on ValidateBasic") + + // reset duplicate evidence to be valid, and set empty chainID + dupEv = randomDuplicatedVoteEvidence() + ev.DuplicateVoteEvidence = dupEv + ev.ChainID = "" + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid chain-id passed on ValidateBasic") + + // reset chainID and set 0 validator power + ev.ChainID = "mychain" + ev.ValidatorPower = 0 + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid validator power passed on ValidateBasic") + + // reset validator power and set invalid total power + ev.ValidatorPower = 10 + ev.TotalPower = 9 + err = ev.ValidateBasic() + require.NotNil(t, err, "invalid total power passed on ValidateBasic") + +} diff --git a/x/ibc/24-host/errors.go b/x/ibc/24-host/errors.go index e1fa5e0e7a08..1468e29d5725 100644 --- a/x/ibc/24-host/errors.go +++ b/x/ibc/24-host/errors.go @@ -13,4 +13,7 @@ var ( // ErrInvalidPath is returned if path string is invalid ErrInvalidPath = sdkerrors.Register(IBCCodeSpace, 2, "invalid path") + + // ErrInvalidEvidence is returned if evidence is invalid + ErrInvalidEvidence = sdkerrors.Register(IBCCodeSpace, 3, "invalid evidence") )