Add validator index and address to Vote.

This commit is contained in:
Jae Kwon
2016-11-15 18:33:16 -05:00
committed by Ethan Buchman
parent 0b098a2eee
commit 3c5a2f55c2
11 changed files with 321 additions and 356 deletions
+1 -3
View File
@@ -91,9 +91,7 @@ type EventDataRoundState struct {
}
type EventDataVote struct {
Index int
Address []byte
Vote *Vote
Vote *Vote
}
func (_ EventDataNewBlock) AssertIsTMEventData() {}
+1
View File
@@ -313,6 +313,7 @@ func (ac accumComparable) Less(o interface{}) bool {
//----------------------------------------
// For testing
// NOTE: PrivValidator are in order.
func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []*PrivValidator) {
vals := make([]*Validator, numValidators)
privValidators := make([]*PrivValidator, numValidators)
+11 -5
View File
@@ -11,10 +11,11 @@ import (
)
var (
ErrVoteUnexpectedStep = errors.New("Unexpected step")
ErrVoteInvalidAccount = errors.New("Invalid round vote account")
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
ErrVoteUnexpectedStep = errors.New("Unexpected step")
ErrVoteInvalidValidatorIndex = errors.New("Invalid round vote validator index")
ErrVoteInvalidValidatorAddress = errors.New("Invalid round vote validator address")
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
)
type ErrVoteConflictingSignature struct {
@@ -28,6 +29,8 @@ func (err *ErrVoteConflictingSignature) Error() string {
// Represents a prevote, precommit, or commit vote from validators for consensus.
type Vote struct {
ValidatorAddress []byte `json:"validator_address"`
ValidatorIndex int `json:"validator_index"`
Height int `json:"height"`
Round int `json:"round"`
Type byte `json:"type"`
@@ -67,5 +70,8 @@ func (vote *Vote) String() string {
PanicSanity("Unknown vote type")
}
return fmt.Sprintf("Vote{%v/%02d/%v(%v) %X#%v %v}", vote.Height, vote.Round, vote.Type, typeString, Fingerprint(vote.BlockHash), vote.BlockPartsHeader, vote.Signature)
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v}",
vote.ValidatorIndex, Fingerprint(vote.ValidatorAddress),
vote.Height, vote.Round, vote.Type, typeString,
Fingerprint(vote.BlockHash), vote.Signature)
}
+27 -37
View File
@@ -86,65 +86,55 @@ func (voteSet *VoteSet) Size() int {
}
}
// Returns added=true, index if vote was added
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// Returns added=true
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidIndex|InvalidAddress|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// Duplicate votes return added=false, err=nil.
// NOTE: vote should not be mutated after adding.
func (voteSet *VoteSet) AddByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) {
func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.addByIndex(valIndex, vote)
return voteSet.addVote(vote)
}
// Returns added=true, index if vote was added
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// Duplicate votes return added=false, err=nil.
// NOTE: vote should not be mutated after adding.
func (voteSet *VoteSet) AddByAddress(address []byte, vote *Vote) (added bool, index int, err error) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
valIndex := vote.ValidatorIndex
valAddr := vote.ValidatorAddress
// Ensure that signer is a validator.
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil {
return false, 0, ErrVoteInvalidAccount
// Ensure thta validator index was set
if valIndex < 0 || len(valAddr) == 0 {
panic("Validator index or address was not set in vote.")
}
return voteSet.addVote(val, valIndex, vote)
}
func (voteSet *VoteSet) addByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) {
// Ensure that signer is a validator.
address, val := voteSet.valSet.GetByIndex(valIndex)
if val == nil {
return false, nil, ErrVoteInvalidAccount
}
added, _, err = voteSet.addVote(val, valIndex, vote)
return
}
func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool, int, error) {
// Make sure the step matches. (or that vote is commit && round < voteSet.round)
if (vote.Height != voteSet.height) ||
(vote.Round != voteSet.round) ||
(vote.Type != voteSet.type_) {
return false, 0, ErrVoteUnexpectedStep
return false, ErrVoteUnexpectedStep
}
// Ensure that signer is a validator.
lookupAddr, val := voteSet.valSet.GetByIndex(valIndex)
if val == nil {
return false, ErrVoteInvalidValidatorIndex
}
// Ensure that the signer has the right address
if !bytes.Equal(valAddr, lookupAddr) {
return false, ErrVoteInvalidValidatorAddress
}
// If vote already exists, return false.
if existingVote := voteSet.votes[valIndex]; existingVote != nil {
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
return false, valIndex, nil
return false, nil
} else {
// Check signature.
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
// Bad signature.
return false, 0, ErrVoteInvalidSignature
return false, ErrVoteInvalidSignature
}
return false, valIndex, &ErrVoteConflictingSignature{
return false, &ErrVoteConflictingSignature{
VoteA: existingVote,
VoteB: vote,
}
@@ -154,7 +144,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
// Check signature.
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
// Bad signature.
return false, 0, ErrVoteInvalidSignature
return false, ErrVoteInvalidSignature
}
// Add vote.
@@ -173,7 +163,7 @@ func (voteSet *VoteSet) addVote(val *Validator, valIndex int, vote *Vote) (bool,
voteSet.maj23Exists = true
}
return true, valIndex, nil
return true, nil
}
func (voteSet *VoteSet) BitArray() *BitArray {
+97 -25
View File
@@ -10,12 +10,21 @@ import (
"testing"
)
// Move it out?
// NOTE: privValidators are in order
// TODO: Move it out?
func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []*PrivValidator) {
valSet, privValidators := RandValidatorSet(numValidators, votingPower)
return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators
}
// Convenience: Return new vote with different validator address/index
func withValidator(vote *Vote, addr []byte, idx int) *Vote {
vote = vote.Copy()
vote.ValidatorAddress = addr
vote.ValidatorIndex = idx
return vote
}
// Convenience: Return new vote with different height
func withHeight(vote *Vote, height int) *Vote {
vote = vote.Copy()
@@ -53,7 +62,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
func signAddVote(privVal *PrivValidator, vote *Vote, voteSet *VoteSet) (bool, error) {
vote.Signature = privVal.Sign(SignBytes(voteSet.ChainID(), vote)).(crypto.SignatureEd25519)
added, _, err := voteSet.AddByAddress(privVal.Address, vote)
added, err := voteSet.AddVote(vote)
return added, err
}
@@ -75,7 +84,14 @@ func TestAddVote(t *testing.T) {
t.Errorf("There should be no 2/3 majority")
}
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
vote := &Vote{
ValidatorAddress: val0.Address,
ValidatorIndex: 0, // since privValidators are in order
Height: height,
Round: round,
Type: VoteTypePrevote,
BlockHash: nil,
}
signAddVote(val0, vote, voteSet)
if voteSet.GetByAddress(val0.Address) == nil {
@@ -94,10 +110,17 @@ func Test2_3Majority(t *testing.T) {
height, round := 1, 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
voteProto := &Vote{
ValidatorAddress: nil, // NOTE: must fill in
ValidatorIndex: -1, // NOTE: must fill in
Height: height,
Round: round,
Type: VoteTypePrevote,
BlockHash: nil,
}
// 6 out of 10 voted for nil.
for i := 0; i < 6; i++ {
vote := withValidator(voteProto, privValidators[i].Address, i)
signAddVote(privValidators[i], vote, voteSet)
}
hash, header, ok := voteSet.TwoThirdsMajority()
@@ -107,6 +130,7 @@ func Test2_3Majority(t *testing.T) {
// 7th validator voted for some blockhash
{
vote := withValidator(voteProto, privValidators[6].Address, 6)
signAddVote(privValidators[6], withBlockHash(vote, RandBytes(32)), voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
if hash != nil || !header.IsZero() || ok {
@@ -116,6 +140,7 @@ func Test2_3Majority(t *testing.T) {
// 8th validator voted for nil.
{
vote := withValidator(voteProto, privValidators[7].Address, 7)
signAddVote(privValidators[7], vote, voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
if hash != nil || !header.IsZero() || !ok {
@@ -132,10 +157,19 @@ func Test2_3MajorityRedux(t *testing.T) {
blockPartsTotal := 123
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: blockHash, BlockPartsHeader: blockPartsHeader}
voteProto := &Vote{
ValidatorAddress: nil, // NOTE: must fill in
ValidatorIndex: -1, // NOTE: must fill in
Height: height,
Round: round,
Type: VoteTypePrevote,
BlockHash: blockHash,
BlockPartsHeader: blockPartsHeader,
}
// 66 out of 100 voted for nil.
for i := 0; i < 66; i++ {
vote := withValidator(voteProto, privValidators[i].Address, i)
signAddVote(privValidators[i], vote, voteSet)
}
hash, header, ok := voteSet.TwoThirdsMajority()
@@ -145,6 +179,7 @@ func Test2_3MajorityRedux(t *testing.T) {
// 67th validator voted for nil
{
vote := withValidator(voteProto, privValidators[66].Address, 66)
signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
if hash != nil || !header.IsZero() || ok {
@@ -154,6 +189,7 @@ func Test2_3MajorityRedux(t *testing.T) {
// 68th validator voted for a different BlockParts PartSetHeader
{
vote := withValidator(voteProto, privValidators[67].Address, 67)
blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)}
signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
@@ -164,6 +200,7 @@ func Test2_3MajorityRedux(t *testing.T) {
// 69th validator voted for different BlockParts Total
{
vote := withValidator(voteProto, privValidators[68].Address, 68)
blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash}
signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
@@ -174,6 +211,7 @@ func Test2_3MajorityRedux(t *testing.T) {
// 70th validator voted for different BlockHash
{
vote := withValidator(voteProto, privValidators[69].Address, 69)
signAddVote(privValidators[69], withBlockHash(vote, RandBytes(32)), voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
if hash != nil || !header.IsZero() || ok {
@@ -183,6 +221,7 @@ func Test2_3MajorityRedux(t *testing.T) {
// 71st validator voted for the right BlockHash & BlockPartsHeader
{
vote := withValidator(voteProto, privValidators[70].Address, 70)
signAddVote(privValidators[70], vote, voteSet)
hash, header, ok = voteSet.TwoThirdsMajority()
if !bytes.Equal(hash, blockHash) || !header.Equals(blockPartsHeader) || !ok {
@@ -195,35 +234,58 @@ func TestBadVotes(t *testing.T) {
height, round := 1, 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1)
voteProto := &Vote{
ValidatorAddress: nil,
ValidatorIndex: -1,
Height: height,
Round: round,
Type: VoteTypePrevote,
BlockHash: nil,
}
// val0 votes for nil.
vote := &Vote{Height: height, Round: round, Type: VoteTypePrevote, BlockHash: nil}
added, err := signAddVote(privValidators[0], vote, voteSet)
if !added || err != nil {
t.Errorf("Expected VoteSet.Add to succeed")
{
vote := withValidator(voteProto, privValidators[0].Address, 0)
added, err := signAddVote(privValidators[0], vote, voteSet)
if !added || err != nil {
t.Errorf("Expected VoteSet.Add to succeed")
}
}
// val0 votes again for some block.
added, err = signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet)
if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, dupeout.")
{
vote := withValidator(voteProto, privValidators[0].Address, 0)
added, err := signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet)
if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, dupeout.")
}
}
// val1 votes on another height
added, err = signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
if added {
t.Errorf("Expected VoteSet.Add to fail, wrong height")
{
vote := withValidator(voteProto, privValidators[1].Address, 1)
added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet)
if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, wrong height")
}
}
// val2 votes on another round
added, err = signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
if added {
t.Errorf("Expected VoteSet.Add to fail, wrong round")
{
vote := withValidator(voteProto, privValidators[2].Address, 2)
added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet)
if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, wrong round")
}
}
// val3 votes of another type.
added, err = signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet)
if added {
t.Errorf("Expected VoteSet.Add to fail, wrong type")
{
vote := withValidator(voteProto, privValidators[3].Address, 3)
added, err := signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet)
if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, wrong type")
}
}
}
@@ -232,11 +294,19 @@ func TestMakeCommit(t *testing.T) {
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrecommit, 10, 1)
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
vote := &Vote{Height: height, Round: round, Type: VoteTypePrecommit,
BlockHash: blockHash, BlockPartsHeader: blockPartsHeader}
voteProto := &Vote{
ValidatorAddress: nil,
ValidatorIndex: -1,
Height: height,
Round: round,
Type: VoteTypePrecommit,
BlockHash: blockHash,
BlockPartsHeader: blockPartsHeader,
}
// 6 out of 10 voted for some block.
for i := 0; i < 6; i++ {
vote := withValidator(voteProto, privValidators[i].Address, i)
signAddVote(privValidators[i], vote, voteSet)
}
@@ -245,13 +315,15 @@ func TestMakeCommit(t *testing.T) {
// 7th voted for some other block.
{
vote := withBlockHash(vote, RandBytes(32))
vote := withValidator(voteProto, privValidators[6].Address, 6)
vote = withBlockHash(vote, RandBytes(32))
vote = withBlockPartsHeader(vote, PartSetHeader{123, RandBytes(32)})
signAddVote(privValidators[6], vote, voteSet)
}
// The 8th voted like everyone else.
{
vote := withValidator(voteProto, privValidators[7].Address, 7)
signAddVote(privValidators[7], vote, voteSet)
}