-
Notifications
You must be signed in to change notification settings - Fork 122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add handler for consumer double voting #1232
Merged
sainoe
merged 30 commits into
feat/ics-misbehaviour-handling
from
sainoe/ics-equivocation
Aug 28, 2023
Merged
Changes from all commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
6cd3d5e
create new endpoint for consumer double voting
sainoe 890e8b2
add first draft handling logic
sainoe 5446088
first iteration of double voting
sainoe 0d564d1
draft first mem test
sainoe 2d16ede
error handling
sainoe e723911
refactor
sainoe 8551d9e
add unit test of double voting verification
sainoe 2ce34de
remove evidence age checks
sainoe 13c82cc
document
sainoe 5a6ac16
doc
sainoe 1a2ac78
protogen
sainoe 4922a9a
Merge branch 'feat/ics-misbehaviour-handling' into sainoe/ics-equivoc…
sainoe 7273ed4
reformat double voting handling
sainoe 34b7dda
logger nit
sainoe 1e301d5
nits
sainoe 0b2665e
check evidence age duration
sainoe 6e83b8f
move verify double voting evidence to ut
sainoe 5023b69
fix nit
sainoe 8075132
nits
sainoe e8097bd
fix e2e tests
sainoe 7052ec9
improve double vote testing coverage
sainoe f58ccad
remove TODO
sainoe cce27b0
lint
sainoe c38398f
add UT for JailAndTombstoneValidator
sainoe fc8f97e
nits
sainoe f0b44c2
nits
sainoe 18ae136
remove tombstoning and evidence age check
sainoe 98dca7e
lint
sainoe 0ed960d
typo
sainoe 0a38420
improve godoc
sainoe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,12 +8,15 @@ import "gogoproto/gogo.proto"; | |
import "cosmos_proto/cosmos.proto"; | ||
import "google/protobuf/any.proto"; | ||
import "ibc/lightclients/tendermint/v1/tendermint.proto"; | ||
import "tendermint/types/evidence.proto"; | ||
|
||
|
||
// Msg defines the Msg service. | ||
service Msg { | ||
rpc AssignConsumerKey(MsgAssignConsumerKey) returns (MsgAssignConsumerKeyResponse); | ||
rpc RegisterConsumerRewardDenom(MsgRegisterConsumerRewardDenom) returns (MsgRegisterConsumerRewardDenomResponse); | ||
rpc SubmitConsumerMisbehaviour(MsgSubmitConsumerMisbehaviour) returns (MsgSubmitConsumerMisbehaviourResponse); | ||
rpc SubmitConsumerDoubleVoting(MsgSubmitConsumerDoubleVoting) returns (MsgSubmitConsumerDoubleVotingResponse); | ||
} | ||
|
||
message MsgAssignConsumerKey { | ||
|
@@ -59,3 +62,19 @@ message MsgSubmitConsumerMisbehaviour { | |
} | ||
|
||
message MsgSubmitConsumerMisbehaviourResponse {} | ||
|
||
|
||
// MsgSubmitConsumerDoubleVoting defines a message that reports an equivocation | ||
// observed on a consumer chain | ||
message MsgSubmitConsumerDoubleVoting { | ||
option (gogoproto.equal) = false; | ||
option (gogoproto.goproto_getters) = false; | ||
string submitter = 1; | ||
// The equivocation of the consumer chain wrapping | ||
// an evidence of a validator that signed two conflicting votes | ||
tendermint.types.DuplicateVoteEvidence duplicate_vote_evidence = 2; | ||
// The light client header of the infraction block | ||
ibc.lightclients.tendermint.v1.Header infraction_block_header = 3; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only used for the consumer chain ID atm |
||
} | ||
|
||
message MsgSubmitConsumerDoubleVotingResponse {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package integration | ||
|
||
import ( | ||
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" | ||
tmtypes "github.com/tendermint/tendermint/types" | ||
) | ||
|
||
// TestHandleConsumerDoubleVoting verifies that handling a double voting evidence | ||
// of a consumer chain results in the expected jailing of the malicious validator | ||
func (s *CCVTestSuite) TestHandleConsumerDoubleVoting() { | ||
s.SetupCCVChannel(s.path) | ||
// required to have the consumer client revision height greater than 0 | ||
s.SendEmptyVSCPacket() | ||
|
||
// create signing info for all validators | ||
for _, v := range s.providerChain.Vals.Validators { | ||
s.setDefaultValSigningInfo(*v) | ||
} | ||
|
||
valSet, err := tmtypes.ValidatorSetFromProto(s.consumerChain.LastHeader.ValidatorSet) | ||
s.Require().NoError(err) | ||
|
||
val := valSet.Validators[0] | ||
signer := s.consumerChain.Signers[val.Address.String()] | ||
|
||
blockID1 := testutil.MakeBlockID([]byte("blockhash"), 1000, []byte("partshash")) | ||
blockID2 := testutil.MakeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) | ||
|
||
// Note that votes are signed along with the chain ID | ||
// see VoteSignBytes in https://github.com/cometbft/cometbft/blob/main/types/vote.go#L139 | ||
vote1 := testutil.MakeAndSignVote( | ||
blockID1, | ||
s.consumerCtx().BlockHeight(), | ||
s.consumerCtx().BlockTime(), | ||
valSet, | ||
signer, | ||
s.consumerChain.ChainID, | ||
) | ||
|
||
badVote := testutil.MakeAndSignVote( | ||
blockID2, | ||
s.consumerCtx().BlockHeight(), | ||
s.consumerCtx().BlockTime(), | ||
valSet, | ||
signer, | ||
s.consumerChain.ChainID, | ||
) | ||
|
||
testCases := []struct { | ||
name string | ||
ev *tmtypes.DuplicateVoteEvidence | ||
chainID string | ||
expPass bool | ||
}{ | ||
{ | ||
"invalid consumer chain id - shouldn't pass", | ||
&tmtypes.DuplicateVoteEvidence{ | ||
VoteA: vote1, | ||
VoteB: badVote, | ||
ValidatorPower: val.VotingPower, | ||
TotalVotingPower: val.VotingPower, | ||
Timestamp: s.consumerCtx().BlockTime(), | ||
}, | ||
"chainID", | ||
false, | ||
}, | ||
{ | ||
// create an invalid evidence containing two identical votes | ||
"invalid double voting evidence - shouldn't pass", | ||
&tmtypes.DuplicateVoteEvidence{ | ||
VoteA: vote1, | ||
VoteB: vote1, | ||
ValidatorPower: val.VotingPower, | ||
TotalVotingPower: val.VotingPower, | ||
Timestamp: s.consumerCtx().BlockTime(), | ||
}, | ||
s.consumerChain.ChainID, | ||
false, | ||
}, | ||
{ | ||
// In order to create an evidence for a consumer chain, | ||
// we create two votes that only differ by their Block IDs and | ||
// signed them using the same validator private key and chain ID | ||
// of the consumer chain | ||
"valid double voting evidence - should pass", | ||
&tmtypes.DuplicateVoteEvidence{ | ||
VoteA: vote1, | ||
VoteB: badVote, | ||
ValidatorPower: val.VotingPower, | ||
TotalVotingPower: val.VotingPower, | ||
Timestamp: s.consumerCtx().BlockTime(), | ||
}, | ||
s.consumerChain.ChainID, | ||
true, | ||
}, | ||
} | ||
|
||
consuAddr := types.NewConsumerConsAddress(sdk.ConsAddress(val.Address.Bytes())) | ||
provAddr := s.providerApp.GetProviderKeeper().GetProviderAddrFromConsumerAddr(s.providerCtx(), s.consumerChain.ChainID, consuAddr) | ||
|
||
for _, tc := range testCases { | ||
s.Run(tc.name, func() { | ||
err = s.providerApp.GetProviderKeeper().HandleConsumerDoubleVoting( | ||
s.providerCtx(), | ||
tc.ev, | ||
tc.chainID, | ||
) | ||
if tc.expPass { | ||
s.Require().NoError(err) | ||
|
||
// verifies that the jailing has occurred | ||
s.Require().True(s.providerApp.GetTestStakingKeeper().IsValidatorJailed(s.providerCtx(), provAddr.ToSdkConsAddr())) | ||
} else { | ||
s.Require().Error(err) | ||
|
||
// verifies that no jailing and has occurred | ||
s.Require().False(s.providerApp.GetTestStakingKeeper().IsValidatorJailed(s.providerCtx(), provAddr.ToSdkConsAddr())) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package crypto | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/tendermint/tendermint/crypto/tmhash" | ||
tmtypes "github.com/tendermint/tendermint/types" | ||
) | ||
|
||
// utility function duplicated from CometBFT | ||
// see https://github.com/cometbft/cometbft/blob/main/evidence/verify_test.go#L554 | ||
func MakeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) tmtypes.BlockID { | ||
var ( | ||
h = make([]byte, tmhash.Size) | ||
psH = make([]byte, tmhash.Size) | ||
) | ||
copy(h, hash) | ||
copy(psH, partSetHash) | ||
return tmtypes.BlockID{ | ||
Hash: h, | ||
PartSetHeader: tmtypes.PartSetHeader{ | ||
Total: partSetSize, | ||
Hash: psH, | ||
}, | ||
} | ||
} | ||
|
||
func MakeAndSignVote( | ||
blockID tmtypes.BlockID, | ||
blockHeight int64, | ||
blockTime time.Time, | ||
valSet *tmtypes.ValidatorSet, | ||
signer tmtypes.PrivValidator, | ||
chainID string, | ||
) *tmtypes.Vote { | ||
vote, err := tmtypes.MakeVote( | ||
blockHeight, | ||
blockID, | ||
valSet, | ||
signer, | ||
chainID, | ||
blockTime, | ||
) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
v := vote.ToProto() | ||
err = signer.SignVote(chainID, v) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
vote.Signature = v.Signature | ||
return vote | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
syntax = "proto3"; | ||
package tendermint.types; | ||
|
||
option go_package = "github.com/tendermint/tendermint/proto/tendermint/types"; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "google/protobuf/timestamp.proto"; | ||
import "tendermint/types/types.proto"; | ||
import "tendermint/types/validator.proto"; | ||
|
||
message Evidence { | ||
oneof sum { | ||
DuplicateVoteEvidence duplicate_vote_evidence = 1; | ||
LightClientAttackEvidence light_client_attack_evidence = 2; | ||
} | ||
} | ||
|
||
// DuplicateVoteEvidence contains evidence of a validator signed two conflicting votes. | ||
message DuplicateVoteEvidence { | ||
tendermint.types.Vote vote_a = 1; | ||
tendermint.types.Vote vote_b = 2; | ||
int64 total_voting_power = 3; | ||
int64 validator_power = 4; | ||
google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; | ||
} | ||
|
||
// LightClientAttackEvidence contains evidence of a set of validators attempting to mislead a light client. | ||
message LightClientAttackEvidence { | ||
tendermint.types.LightBlock conflicting_block = 1; | ||
int64 common_height = 2; | ||
repeated tendermint.types.Validator byzantine_validators = 3; | ||
int64 total_voting_power = 4; | ||
google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; | ||
} | ||
|
||
message EvidenceList { | ||
repeated Evidence evidence = 1 [(gogoproto.nullable) = false]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definition stays unchanged bc the Hermes relayer is supposedly already using it.