diff --git a/consensus/pol.go b/consensus/pol.go index 9725a7bb..eca67b64 100644 --- a/consensus/pol.go +++ b/consensus/pol.go @@ -79,6 +79,9 @@ func (pol *POL) Verify(vset *state.ValidatorSet) error { if val == nil { return Errorf("Invalid validator for commit %v for POL %v", sig, pol) } + if round >= pol.Round { + return Errorf("Invalid commit round %v for POL %v", round, pol) + } commitDoc := BinaryBytes(&Vote{Height: pol.Height, Round: round, Type: VoteTypeCommit, BlockHash: pol.BlockHash}) // TODO cache diff --git a/consensus/pol_test.go b/consensus/pol_test.go new file mode 100644 index 00000000..db622c1d --- /dev/null +++ b/consensus/pol_test.go @@ -0,0 +1,196 @@ +package consensus + +import ( + . "github.com/tendermint/tendermint/blocks" + . "github.com/tendermint/tendermint/common" + + "bytes" + "testing" +) + +// NOTE: see consensus/test.go for common test methods. + +func TestVerifyVotes(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 0, 10, 1) + + // Make a POL with -2/3 votes. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 0, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 0, Type: VoteTypeBare, BlockHash: blockHash, + } + for i := 0; i < 6; i++ { + privAccounts[i].Sign(vote) + pol.Votes = append(pol.Votes, vote.Signature) + } + + // Check that validation fails. + if err := pol.Verify(valSet); err == nil { + t.Errorf("Expected POL.Verify() to fail, not enough votes.") + } + + // Make a POL with +2/3 votes. + privAccounts[7].Sign(vote) + pol.Votes = append(pol.Votes, vote.Signature) + + // Check that validation succeeds. + if err := pol.Verify(valSet); err != nil { + t.Errorf("Expected POL.Verify() to succeed") + } +} + +func TestVerifyInvalidVote(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 0, 10, 1) + + // Make a POL with +2/3 votes with the wrong signature. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 0, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 0, Type: VoteTypeBare, BlockHash: blockHash, + } + for i := 0; i < 7; i++ { + privAccounts[i].Sign(vote) + // Mutate the signature. + vote.Signature.Bytes[0] += byte(0x01) + pol.Votes = append(pol.Votes, vote.Signature) + } + + // Check that validation fails. + if err := pol.Verify(valSet); err == nil { + t.Errorf("Expected POL.Verify() to fail, wrong signatures.") + } +} + +func TestVerifyCommits(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 2, 10, 1) + + // Make a POL with +2/3 votes. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 2, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 1, Type: VoteTypeCommit, BlockHash: blockHash, + } + for i := 0; i < 7; i++ { + privAccounts[i].Sign(vote) + pol.Commits = append(pol.Commits, vote.Signature) + pol.CommitRounds = append(pol.CommitRounds, 1) + } + + // Check that validation succeeds. + if err := pol.Verify(valSet); err != nil { + t.Errorf("Expected POL.Verify() to succeed") + } +} + +func TestVerifyInvalidCommits(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 2, 10, 1) + + // Make a POL with +2/3 votes with the wrong signature. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 2, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 1, Type: VoteTypeCommit, BlockHash: blockHash, + } + for i := 0; i < 7; i++ { + privAccounts[i].Sign(vote) + // Mutate the signature. + vote.Signature.Bytes[0] += byte(0x01) + pol.Commits = append(pol.Commits, vote.Signature) + pol.CommitRounds = append(pol.CommitRounds, 1) + } + + // Check that validation fails. + if err := pol.Verify(valSet); err == nil { + t.Errorf("Expected POL.Verify() to fail, wrong signatures.") + } +} + +func TestVerifyInvalidCommitRounds(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 2, 10, 1) + + // Make a POL with +2/3 commits for the current round. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 2, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 2, Type: VoteTypeCommit, BlockHash: blockHash, + } + for i := 0; i < 7; i++ { + privAccounts[i].Sign(vote) + pol.Commits = append(pol.Commits, vote.Signature) + pol.CommitRounds = append(pol.CommitRounds, 2) + } + + // Check that validation fails. + if err := pol.Verify(valSet); err == nil { + t.Errorf("Expected POL.Verify() to fail, same round.") + } +} + +func TestVerifyInvalidCommitRounds2(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 2, 10, 1) + + // Make a POL with +2/3 commits for future round. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 2, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 3, Type: VoteTypeCommit, BlockHash: blockHash, + } + for i := 0; i < 7; i++ { + privAccounts[i].Sign(vote) + pol.Commits = append(pol.Commits, vote.Signature) + pol.CommitRounds = append(pol.CommitRounds, 3) + } + + // Check that validation fails. + if err := pol.Verify(valSet); err == nil { + t.Errorf("Expected POL.Verify() to fail, future round.") + } +} + +func TestReadWrite(t *testing.T) { + _, valSet, privAccounts := makeVoteSet(0, 0, 10, 1) + + // Make a POL with +2/3 votes. + blockHash := RandBytes(32) + pol := &POL{ + Height: 0, Round: 0, BlockHash: blockHash, + } + vote := &Vote{ + Height: 0, Round: 0, Type: VoteTypeBare, BlockHash: blockHash, + } + for i := 0; i < 7; i++ { + privAccounts[i].Sign(vote) + pol.Votes = append(pol.Votes, vote.Signature) + } + + // Write it to a buffer. + buf := new(bytes.Buffer) + _, err := pol.WriteTo(buf) + if err != nil { + t.Fatalf("Failed to write POL") + } + + // Read from buffer. + var n int64 + pol2 := ReadPOL(buf, &n, &err) + if err != nil { + t.Fatalf("Failed to read POL") + } + + // Check that validation succeeds. + if err := pol2.Verify(valSet); err != nil { + t.Errorf("Expected POL.Verify() to succeed") + } +} diff --git a/consensus/test.go b/consensus/test.go new file mode 100644 index 00000000..147282d7 --- /dev/null +++ b/consensus/test.go @@ -0,0 +1,27 @@ +package consensus + +import ( + . "github.com/tendermint/tendermint/blocks" + "github.com/tendermint/tendermint/state" +) + +func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) { + privAccount := state.GenPrivAccount() + privAccount.Id = id + return &state.Validator{ + Account: privAccount.Account, + VotingPower: votingPower, + }, privAccount +} + +func makeVoteSet(height uint32, round uint16, numValidators int, votingPower uint64) (*VoteSet, *state.ValidatorSet, []*state.PrivAccount) { + vals := make([]*state.Validator, numValidators) + privAccounts := make([]*state.PrivAccount, numValidators) + for i := 0; i < numValidators; i++ { + val, privAccount := makeValidator(uint64(i), votingPower) + vals[i] = val + privAccounts[i] = privAccount + } + valSet := state.NewValidatorSet(vals) + return NewVoteSet(height, round, VoteTypeBare, valSet), valSet, privAccounts +} diff --git a/consensus/vote_set_test.go b/consensus/vote_set_test.go index 4a760b99..d235ea4f 100644 --- a/consensus/vote_set_test.go +++ b/consensus/vote_set_test.go @@ -3,31 +3,11 @@ package consensus import ( . "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/common" - "github.com/tendermint/tendermint/state" "testing" ) -func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) { - privAccount := state.GenPrivAccount() - privAccount.Id = id - return &state.Validator{ - Account: privAccount.Account, - VotingPower: votingPower, - }, privAccount -} - -func makeVoteSet(height uint32, round uint16, numValidators int, votingPower uint64) (*VoteSet, *state.ValidatorSet, []*state.PrivAccount) { - vals := make([]*state.Validator, numValidators) - privAccounts := make([]*state.PrivAccount, numValidators) - for i := 0; i < numValidators; i++ { - val, privAccount := makeValidator(uint64(i), votingPower) - vals[i] = val - privAccounts[i] = privAccount - } - valSet := state.NewValidatorSet(vals) - return NewVoteSet(height, round, VoteTypeBare, valSet), valSet, privAccounts -} +// NOTE: see consensus/test.go for common test methods. func TestAddVote(t *testing.T) { voteSet, _, privAccounts := makeVoteSet(0, 0, 10, 1)