2014-09-04 03:32:38 -07:00
|
|
|
package consensus
|
2014-08-10 16:35:08 -07:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
. "github.com/tendermint/tendermint/binary"
|
|
|
|
. "github.com/tendermint/tendermint/blocks"
|
2014-09-04 03:32:38 -07:00
|
|
|
. "github.com/tendermint/tendermint/state"
|
2014-08-10 16:35:08 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
VoteTypeBare = byte(0x00)
|
|
|
|
VoteTypePrecommit = byte(0x01)
|
|
|
|
VoteTypeCommit = byte(0x02)
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
ErrVoteUnexpectedPhase = errors.New("Unexpected phase")
|
|
|
|
ErrVoteInvalidAccount = errors.New("Invalid round vote account")
|
|
|
|
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
|
|
|
|
ErrVoteInvalidHash = errors.New("Invalid hash")
|
|
|
|
ErrVoteConflictingSignature = errors.New("Conflicting round vote signature")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Represents a bare, precommit, or commit vote for proposals.
|
|
|
|
type Vote struct {
|
|
|
|
Height uint32
|
2014-09-07 02:21:25 -07:00
|
|
|
Round uint16 // zero if commit vote.
|
2014-08-10 16:35:08 -07:00
|
|
|
Type byte
|
|
|
|
Hash []byte // empty if vote is nil.
|
|
|
|
Signature
|
|
|
|
}
|
|
|
|
|
2014-09-03 20:41:57 -07:00
|
|
|
func ReadVote(r io.Reader, n *int64, err *error) *Vote {
|
2014-08-10 16:35:08 -07:00
|
|
|
return &Vote{
|
2014-09-03 20:41:57 -07:00
|
|
|
Height: ReadUInt32(r, n, err),
|
|
|
|
Round: ReadUInt16(r, n, err),
|
|
|
|
Type: ReadByte(r, n, err),
|
|
|
|
Hash: ReadByteSlice(r, n, err),
|
|
|
|
Signature: ReadSignature(r, n, err),
|
2014-08-10 16:35:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Vote) WriteTo(w io.Writer) (n int64, err error) {
|
2014-09-03 20:41:57 -07:00
|
|
|
WriteUInt32(w, v.Height, &n, &err)
|
|
|
|
WriteUInt16(w, v.Round, &n, &err)
|
|
|
|
WriteByte(w, v.Type, &n, &err)
|
|
|
|
WriteByteSlice(w, v.Hash, &n, &err)
|
|
|
|
WriteBinary(w, v.Signature, &n, &err)
|
2014-08-10 16:35:08 -07:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-09-07 02:21:25 -07:00
|
|
|
func (v *Vote) GetDocument() string {
|
|
|
|
return GenVoteDocument(v.Type, v.Height, v.Round, v.Hash)
|
2014-08-10 16:35:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// VoteSet helps collect signatures from validators at each height+round
|
|
|
|
// for a predefined vote type.
|
|
|
|
type VoteSet struct {
|
|
|
|
mtx sync.Mutex
|
|
|
|
height uint32
|
|
|
|
round uint16
|
|
|
|
type_ byte
|
2014-09-04 03:32:38 -07:00
|
|
|
validators *ValidatorSet
|
2014-08-10 16:35:08 -07:00
|
|
|
votes map[uint64]*Vote
|
|
|
|
votesByHash map[string]uint64
|
|
|
|
totalVotes uint64
|
|
|
|
totalVotingPower uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructs a new VoteSet struct used to accumulate votes for each round.
|
2014-09-04 03:32:38 -07:00
|
|
|
func NewVoteSet(height uint32, round uint16, type_ byte, validators *ValidatorSet) *VoteSet {
|
2014-09-07 02:21:25 -07:00
|
|
|
if type_ == VoteTypeCommit && round != 0 {
|
|
|
|
panic("Expected round 0 for commit vote set")
|
|
|
|
}
|
2014-08-10 16:35:08 -07:00
|
|
|
totalVotingPower := uint64(0)
|
2014-09-04 03:32:38 -07:00
|
|
|
for _, val := range validators.Map() {
|
2014-08-30 16:28:51 -07:00
|
|
|
totalVotingPower += val.VotingPower
|
2014-08-10 16:35:08 -07:00
|
|
|
}
|
|
|
|
return &VoteSet{
|
|
|
|
height: height,
|
|
|
|
round: round,
|
|
|
|
type_: type_,
|
|
|
|
validators: validators,
|
2014-09-04 03:32:38 -07:00
|
|
|
votes: make(map[uint64]*Vote, validators.Size()),
|
2014-08-10 16:35:08 -07:00
|
|
|
votesByHash: make(map[string]uint64),
|
|
|
|
totalVotes: 0,
|
|
|
|
totalVotingPower: totalVotingPower,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// True if added, false if not.
|
|
|
|
// Returns ErrVote[UnexpectedPhase|InvalidAccount|InvalidSignature|InvalidHash|ConflictingSignature]
|
|
|
|
func (vs *VoteSet) AddVote(vote *Vote) (bool, error) {
|
|
|
|
vs.mtx.Lock()
|
|
|
|
defer vs.mtx.Unlock()
|
|
|
|
|
|
|
|
// Make sure the phase matches.
|
2014-09-07 02:21:25 -07:00
|
|
|
if vote.Height != vs.height ||
|
|
|
|
(vote.Type != VoteTypeCommit && vote.Round != vs.round) ||
|
|
|
|
vote.Type != vs.type_ {
|
2014-08-10 16:35:08 -07:00
|
|
|
return false, ErrVoteUnexpectedPhase
|
|
|
|
}
|
|
|
|
|
2014-09-04 03:32:38 -07:00
|
|
|
val := vs.validators.Get(vote.SignerId)
|
2014-08-10 16:35:08 -07:00
|
|
|
// Ensure that signer is a validator.
|
|
|
|
if val == nil {
|
|
|
|
return false, ErrVoteInvalidAccount
|
|
|
|
}
|
|
|
|
// Check signature.
|
2014-09-07 02:21:25 -07:00
|
|
|
if !val.Verify([]byte(vote.GetDocument()), vote.Signature) {
|
2014-08-10 16:35:08 -07:00
|
|
|
// Bad signature.
|
|
|
|
return false, ErrVoteInvalidSignature
|
|
|
|
}
|
|
|
|
// If vote already exists, return false.
|
2014-08-30 16:28:51 -07:00
|
|
|
if existingVote, ok := vs.votes[vote.SignerId]; ok {
|
2014-08-10 16:35:08 -07:00
|
|
|
if bytes.Equal(existingVote.Hash, vote.Hash) {
|
|
|
|
return false, nil
|
|
|
|
} else {
|
|
|
|
return false, ErrVoteConflictingSignature
|
|
|
|
}
|
|
|
|
}
|
2014-08-30 16:28:51 -07:00
|
|
|
vs.votes[vote.SignerId] = vote
|
|
|
|
vs.votesByHash[string(vote.Hash)] += val.VotingPower
|
|
|
|
vs.totalVotes += val.VotingPower
|
2014-08-10 16:35:08 -07:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns either a blockhash (or nil) that received +2/3 majority.
|
|
|
|
// If there exists no such majority, returns (nil, false).
|
|
|
|
func (vs *VoteSet) TwoThirdsMajority() (hash []byte, ok bool) {
|
|
|
|
vs.mtx.Lock()
|
|
|
|
defer vs.mtx.Unlock()
|
2014-08-30 16:28:51 -07:00
|
|
|
twoThirdsMajority := (vs.totalVotingPower*2 + 2) / 3
|
2014-08-10 16:35:08 -07:00
|
|
|
if vs.totalVotes < twoThirdsMajority {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
for hash, votes := range vs.votesByHash {
|
|
|
|
if votes >= twoThirdsMajority {
|
|
|
|
if hash == "" {
|
|
|
|
return nil, true
|
|
|
|
} else {
|
|
|
|
return []byte(hash), true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns blockhashes (or nil) that received a +1/3 majority.
|
|
|
|
// If there exists no such majority, returns nil.
|
|
|
|
func (vs *VoteSet) OneThirdMajority() (hashes []interface{}) {
|
|
|
|
vs.mtx.Lock()
|
|
|
|
defer vs.mtx.Unlock()
|
2014-08-30 16:28:51 -07:00
|
|
|
oneThirdMajority := (vs.totalVotingPower + 2) / 3
|
2014-08-10 16:35:08 -07:00
|
|
|
if vs.totalVotes < oneThirdMajority {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for hash, votes := range vs.votesByHash {
|
|
|
|
if votes >= oneThirdMajority {
|
|
|
|
if hash == "" {
|
|
|
|
hashes = append(hashes, nil)
|
|
|
|
} else {
|
|
|
|
hashes = append(hashes, []byte(hash))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hashes
|
|
|
|
}
|