diff --git a/tests/integration/double_vote.go b/tests/integration/double_vote.go index 184f7604bb..c79b92115e 100644 --- a/tests/integration/double_vote.go +++ b/tests/integration/double_vote.go @@ -1,9 +1,11 @@ package integration import ( + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" testutil "github.com/cosmos/interchain-security/v2/testutil/crypto" "github.com/cosmos/interchain-security/v2/x/ccv/provider/types" + "github.com/tendermint/tendermint/crypto" tmtypes "github.com/tendermint/tendermint/types" ) @@ -78,6 +80,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { name string ev *tmtypes.DuplicateVoteEvidence chainID string + pubkey crypto.PubKey expPass bool }{ { @@ -90,6 +93,20 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { Timestamp: s.consumerCtx().BlockTime(), }, "chainID", + consuVal.PubKey, + false, + }, + { + "wrong public key - shouldn't pass", + &tmtypes.DuplicateVoteEvidence{ + VoteA: consuVote, + VoteB: consuVote, + ValidatorPower: consuVal.VotingPower, + TotalVotingPower: consuVal.VotingPower, + Timestamp: s.consumerCtx().BlockTime(), + }, + s.consumerChain.ChainID, + provVal.PubKey, false, }, { @@ -103,6 +120,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { Timestamp: s.consumerCtx().BlockTime(), }, s.consumerChain.ChainID, + consuVal.PubKey, false, }, { @@ -119,6 +137,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { Timestamp: s.consumerCtx().BlockTime(), }, s.consumerChain.ChainID, + consuVal.PubKey, true, }, { @@ -132,6 +151,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { Timestamp: s.consumerCtx().BlockTime(), }, s.consumerChain.ChainID, + provVal.PubKey, true, }, } @@ -151,10 +171,15 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { s.providerApp.GetProviderKeeper().DeleteKeyAssignments(provCtx, s.consumerChain.ChainID) } + // convert validator public key + pk, err := cryptocodec.FromTmPubKeyInterface(tc.pubkey) + s.Require().NoError(err) + err = s.providerApp.GetProviderKeeper().HandleConsumerDoubleVoting( provCtx, tc.ev, tc.chainID, + pk, ) if tc.expPass { diff --git a/x/ccv/provider/keeper/double_vote.go b/x/ccv/provider/keeper/double_vote.go index 171be6b250..ee5f716ef8 100644 --- a/x/ccv/provider/keeper/double_vote.go +++ b/x/ccv/provider/keeper/double_vote.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" - cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,9 +13,19 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) -// HandleConsumerDoubleVoting verifies a double voting evidence for a given a consumer chain and, -// if successful, executes the jailing of the malicious validator. -func (k Keeper) HandleConsumerDoubleVoting(ctx sdk.Context, evidence *tmtypes.DuplicateVoteEvidence, chainID string) error { +// HandleConsumerDoubleVoting verifies a double voting evidence for a given a consumer chain ID +// and a public key and, if successful, executes the jailing of the malicious validator. +func (k Keeper) HandleConsumerDoubleVoting( + ctx sdk.Context, + evidence *tmtypes.DuplicateVoteEvidence, + chainID string, + pubkey cryptotypes.PubKey, +) error { + // verifies the double voting evidence using the consumer chain public key + if err := k.VerifyDoubleVotingEvidence(ctx, *evidence, chainID, pubkey); err != nil { + return err + } + // get the validator's consensus address on the provider providerAddr := k.GetProviderAddrFromConsumerAddr( ctx, @@ -24,17 +33,6 @@ func (k Keeper) HandleConsumerDoubleVoting(ctx sdk.Context, evidence *tmtypes.Du types.NewConsumerConsAddress(sdk.ConsAddress(evidence.VoteA.ValidatorAddress.Bytes())), ) - // get validator pubkey used on the consumer chain - pubkey, err := k.getValidatorPubkeyOnConsumer(ctx, chainID, providerAddr) - if err != nil { - return err - } - - // verifies the double voting evidence using the consumer chain public key - if err := k.VerifyDoubleVotingEvidence(ctx, *evidence, chainID, pubkey); err != nil { - return err - } - // execute the jailing k.JailValidator(ctx, providerAddr) @@ -107,32 +105,3 @@ func (k Keeper) VerifyDoubleVotingEvidence( return nil } - -// getValidatorPubkeyOnConsumer returns the public key a validator used on a given consumer chain. -// Note that it can either be an assigned public key or the same public key the validator uses -// on the provider chain. -func (k Keeper) getValidatorPubkeyOnConsumer( - ctx sdk.Context, - chainID string, - providerAddr types.ProviderConsAddress, -) (pubkey cryptotypes.PubKey, err error) { - tmPK, ok := k.GetValidatorConsumerPubKey(ctx, chainID, providerAddr) - if ok { - pubkey, err = cryptocodec.FromTmProtoPublicKey(tmPK) - if err != nil { - return - } - } else { - val, ok := k.stakingKeeper.GetValidatorByConsAddr(ctx, providerAddr.ToSdkConsAddr()) - if !ok { - err = fmt.Errorf("cannot find validator %s", providerAddr.String()) - return - } - pubkey, err = val.ConsPubKey() - if err != nil { - return - } - } - - return -} diff --git a/x/ccv/provider/keeper/msg_server.go b/x/ccv/provider/keeper/msg_server.go index b6b96bb010..9293859b02 100644 --- a/x/ccv/provider/keeper/msg_server.go +++ b/x/ccv/provider/keeper/msg_server.go @@ -4,6 +4,8 @@ import ( "context" "encoding/base64" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" @@ -156,8 +158,33 @@ func (k msgServer) SubmitConsumerDoubleVoting(goCtx context.Context, msg *types. return nil, err } - if err := k.Keeper.HandleConsumerDoubleVoting(ctx, evidence, msg.InfractionBlockHeader.Header.ChainID); err != nil { - return &types.MsgSubmitConsumerDoubleVotingResponse{}, err + // parse the validator set of the infraction block header in order + // to find the public key of the validator who double voted + + // get validator set + valset, err := tmtypes.ValidatorSetFromProto(msg.InfractionBlockHeader.ValidatorSet) + if err != nil { + return nil, err + } + + // look for the malicious validator in the validator set + _, validator := valset.GetByAddress(evidence.VoteA.ValidatorAddress) + if validator == nil { + return nil, errorsmod.Wrapf( + ccvtypes.ErrInvalidEvidence, + "misbehaving validator %s cannot be found in the infraction block header validator set", + evidence.VoteA.ValidatorAddress) + } + + pubkey, err := cryptocodec.FromTmPubKeyInterface(validator.PubKey) + if err != nil { + return nil, err + } + + // handle the double voting evidence using the chain ID of the infraction block header + // and the malicious validator's public key + if err := k.Keeper.HandleConsumerDoubleVoting(ctx, evidence, msg.InfractionBlockHeader.Header.ChainID, pubkey); err != nil { + return nil, err } ctx.EventManager().EmitEvents(sdk.Events{