From 9cdcffbe4be2c629bc56d057c38d18f15dbb51ce Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 28 Aug 2017 19:46:38 -0400 Subject: [PATCH] types: comments; compiles; evidence test --- state/execution.go | 7 ++-- types/evidence.go | 26 +++++++++----- types/evidence_test.go | 82 ++++++++++++++++++++++++++++++++++++++++++ types/vote.go | 3 +- 4 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 types/evidence_test.go diff --git a/state/execution.go b/state/execution.go index d838531f..7e84ce2d 100644 --- a/state/execution.go +++ b/state/execution.go @@ -310,11 +310,8 @@ func (s *State) validateBlock(b *types.Block) error { } for _, ev := range block.Evidence.Evidence { - if err := ev.VoteA.Verify(s.ChainID, ev.PubKey); err != nil { - return types.ErrEvidenceInvalid(ev, err) - } - if err := ev.VoteB.Verify(s.ChainID, ev.PubKey); err != nil { - return types.ErrEvidenceInvalid(ev, err) + if err := ev.Verify(s.ChainID); err != nil { + return types.NewEvidenceInvalidErr(ev, err) } } diff --git a/types/evidence.go b/types/evidence.go index a9fdc42f..b61edcc2 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -7,36 +7,44 @@ import ( "github.com/tendermint/go-crypto" ) +// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid. type ErrEvidenceInvalid struct { - Evidence Evidence - Error error + Evidence Evidence + ErrorValue error } +func NewEvidenceInvalidErr(ev Evidence, err error) *ErrEvidenceInvalid { + return &ErrEvidenceInvalid{ev, err} +} + +// Error returns a string representation of the error. func (err *ErrEvidenceInvalid) Error() string { - return fmt.Sprintf("Invalid evidence: %v. Evidence: %v", err.Error, err.Evidence) + return fmt.Sprintf("Invalid evidence: %v. Evidence: %v", err.ErrorValue, err.Evidence) } // Evidence represents any provable malicious activity by a validator type Evidence interface { - Verify() error + Verify(chainID string) error Address() []byte } //------------------------------------------- +// DuplicateVoteEvidence contains evidence a validator signed two conflicting votes. type DuplicateVoteEvidence struct { PubKey crypto.PubKey VoteA *Vote VoteB *Vote } -// Address returns the address of the validator +// Address returns the address of the validator. func (dve *DuplicateVoteEvidence) Address() []byte { return dve.PubKey.Address() } -// Verify returns an error if the two votes aren't from the same validator, for the same H/R/S, but for different blocks -func (dve *DuplicateVoteEvidence) Verify() error { +// Verify returns an error if the two votes aren't conflicting. +// To be conflicting, they must be from the same validator, for the same H/R/S, but for different blocks. +func (dve *DuplicateVoteEvidence) Verify(chainID string) error { // H/R/S must be the same if dve.VoteA.Height != dve.VoteB.Height || dve.VoteA.Round != dve.VoteB.Round || @@ -59,10 +67,10 @@ func (dve *DuplicateVoteEvidence) Verify() error { } // Signatures must be valid - if !dve.PubKey.Verify(SignBytes(chainID, dve.VoteA), dve.VoteA.Signature) { + if !dve.PubKey.VerifyBytes(SignBytes(chainID, dve.VoteA), dve.VoteA.Signature) { return ErrVoteInvalidSignature } - if !dve.PubKey.Verify(SignBytes(chainID, dve.VoteB), dve.VoteB.Signature) { + if !dve.PubKey.VerifyBytes(SignBytes(chainID, dve.VoteB), dve.VoteB.Signature) { return ErrVoteInvalidSignature } diff --git a/types/evidence_test.go b/types/evidence_test.go new file mode 100644 index 00000000..a3f81154 --- /dev/null +++ b/types/evidence_test.go @@ -0,0 +1,82 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +type voteData struct { + vote1 *Vote + vote2 *Vote + valid bool +} + +func makeVote(val *PrivValidator, chainID string, valIndex, height, round, step int, blockID BlockID) *Vote { + v := &Vote{ + ValidatorAddress: val.PubKey.Address(), + ValidatorIndex: valIndex, + Height: height, + Round: round, + Type: byte(step), + BlockID: blockID, + } + sig := val.PrivKey.Sign(SignBytes(chainID, v)) + v.Signature = sig + return v + +} + +func makeBlockID(hash string, partSetSize int, partSetHash string) BlockID { + return BlockID{ + Hash: []byte(hash), + PartsHeader: PartSetHeader{ + Total: partSetSize, + Hash: []byte(partSetHash), + }, + } + +} + +func TestEvidence(t *testing.T) { + val := GenPrivValidator() + val2 := GenPrivValidator() + blockID := makeBlockID("blockhash", 1000, "partshash") + blockID2 := makeBlockID("blockhash2", 1000, "partshash") + blockID3 := makeBlockID("blockhash", 10000, "partshash") + blockID4 := makeBlockID("blockhash", 10000, "partshash2") + + chainID := "mychain" + + vote1 := makeVote(val, chainID, 0, 10, 2, 1, blockID) + badVote := makeVote(val, chainID, 0, 10, 2, 1, blockID) + badVote.Signature = val2.PrivKey.Sign(SignBytes(chainID, badVote)) + + cases := []voteData{ + {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID2), true}, // different block ids + {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID3), true}, + {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID4), true}, + {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID), false}, // wrong block id + {vote1, makeVote(val, "mychain2", 0, 10, 2, 1, blockID2), false}, // wrong chain id + {vote1, makeVote(val, chainID, 1, 10, 2, 1, blockID2), false}, // wrong val index + {vote1, makeVote(val, chainID, 0, 11, 2, 1, blockID2), false}, // wrong height + {vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round + {vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step + {vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator + {vote1, badVote, false}, // signed by wrong key + } + + for _, c := range cases { + ev := &DuplicateVoteEvidence{ + PubKey: val.PubKey, + VoteA: c.vote1, + VoteB: c.vote2, + } + if c.valid { + assert.Nil(t, ev.Verify(chainID), "evidence should be valid") + } else { + assert.NotNil(t, ev.Verify(chainID), "evidence should be invalid") + } + } + +} diff --git a/types/vote.go b/types/vote.go index 82adf0d1..3136e4bb 100644 --- a/types/vote.go +++ b/types/vote.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "errors" "fmt" "io" @@ -103,7 +104,7 @@ func (vote *Vote) String() string { } func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error { - if !bytes.Equal(pubKey.Address(), v.ValidatorAddress) { + if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) { return ErrVoteInvalidValidatorAddress }