Skip to content

Commit

Permalink
fix: verify equivocation using validator pubkey in `SubmitConsumerDou…
Browse files Browse the repository at this point in the history
…bleVoting` msg (#1264)

* verify dv evidence using malicious validator pubkey in infraction block header

* nits

* nits
  • Loading branch information
sainoe authored Sep 6, 2023
1 parent 2501e83 commit eb6a079
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 46 deletions.
25 changes: 25 additions & 0 deletions tests/integration/double_vote.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand Down Expand Up @@ -78,6 +80,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
name string
ev *tmtypes.DuplicateVoteEvidence
chainID string
pubkey crypto.PubKey
expPass bool
}{
{
Expand All @@ -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,
},
{
Expand All @@ -103,6 +120,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
Timestamp: s.consumerCtx().BlockTime(),
},
s.consumerChain.ChainID,
consuVal.PubKey,
false,
},
{
Expand All @@ -119,6 +137,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
Timestamp: s.consumerCtx().BlockTime(),
},
s.consumerChain.ChainID,
consuVal.PubKey,
true,
},
{
Expand All @@ -132,6 +151,7 @@ func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() {
Timestamp: s.consumerCtx().BlockTime(),
},
s.consumerChain.ChainID,
provVal.PubKey,
true,
},
}
Expand All @@ -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 {
Expand Down
57 changes: 13 additions & 44 deletions x/ccv/provider/keeper/double_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -14,27 +13,26 @@ 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,
chainID,
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)

Expand Down Expand Up @@ -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
}
31 changes: 29 additions & 2 deletions x/ccv/provider/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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{
Expand Down

0 comments on commit eb6a079

Please sign in to comment.