tendermint/types/vote_set.go

326 lines
8.5 KiB
Go
Raw Normal View History

package types
2014-09-14 15:37:32 -07:00
import (
"bytes"
2014-10-15 20:15:38 -07:00
"fmt"
"strings"
2014-09-14 15:37:32 -07:00
"sync"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
2014-09-14 15:37:32 -07:00
)
// VoteSet helps collect signatures from validators at each height+round
// for a predefined vote type.
// Note that there three kinds of votes: prevotes, precommits, and commits.
2014-09-14 15:37:32 -07:00
// A commit of prior rounds can be added added in lieu of votes/precommits.
// NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64.
2014-09-14 15:37:32 -07:00
type VoteSet struct {
2016-05-08 15:00:58 -07:00
chainID string
height int
round int
type_ byte
2014-09-14 15:37:32 -07:00
mtx sync.Mutex
valSet *ValidatorSet
votes []*Vote // validator index -> vote
votesBitArray *BitArray // validator index -> has vote?
votesByBlock map[string]int64 // string(blockHash)+string(blockParts) -> vote sum.
totalVotes int64
maj23Hash []byte
maj23PartsHeader PartSetHeader
maj23Exists bool
2014-09-14 15:37:32 -07:00
}
// Constructs a new VoteSet struct used to accumulate votes for given height/round.
2016-05-08 15:00:58 -07:00
func NewVoteSet(chainID string, height int, round int, type_ byte, valSet *ValidatorSet) *VoteSet {
2014-10-31 18:35:38 -07:00
if height == 0 {
2015-07-19 16:42:52 -07:00
PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.")
2014-10-31 18:35:38 -07:00
}
2014-09-14 15:37:32 -07:00
return &VoteSet{
2016-05-08 15:00:58 -07:00
chainID: chainID,
2014-10-30 03:32:09 -07:00
height: height,
round: round,
type_: type_,
valSet: valSet,
votes: make([]*Vote, valSet.Size()),
votesBitArray: NewBitArray(valSet.Size()),
votesByBlock: make(map[string]int64),
2014-10-30 03:32:09 -07:00
totalVotes: 0,
2014-09-14 15:37:32 -07:00
}
}
2016-05-08 15:00:58 -07:00
func (voteSet *VoteSet) ChainID() string {
return voteSet.chainID
}
func (voteSet *VoteSet) Height() int {
if voteSet == nil {
return 0
} else {
return voteSet.height
}
}
func (voteSet *VoteSet) Round() int {
2015-06-19 15:30:10 -07:00
if voteSet == nil {
2015-09-10 01:29:49 -07:00
return -1
2015-06-19 15:30:10 -07:00
} else {
return voteSet.round
}
}
func (voteSet *VoteSet) Type() byte {
if voteSet == nil {
return 0x00
} else {
return voteSet.type_
}
}
func (voteSet *VoteSet) Size() int {
if voteSet == nil {
2014-11-03 15:50:23 -08:00
return 0
} else {
return voteSet.valSet.Size()
2014-11-03 15:50:23 -08:00
}
}
// Returns added=true, index if vote was added
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
2015-06-22 19:04:31 -07:00
// Duplicate votes return added=false, err=nil.
// NOTE: vote should not be mutated after adding.
2015-08-26 15:56:34 -07:00
func (voteSet *VoteSet) AddByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
2014-09-14 15:37:32 -07:00
return voteSet.addByIndex(valIndex, vote)
}
// Returns added=true, index if vote was added
// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
2015-06-22 19:04:31 -07:00
// 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()
// Ensure that signer is a validator.
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil {
return false, 0, ErrVoteInvalidAccount
}
return voteSet.addVote(val, valIndex, vote)
}
2015-08-26 15:56:34 -07:00
func (voteSet *VoteSet) addByIndex(valIndex int, vote *Vote) (added bool, address []byte, err error) {
// Ensure that signer is a validator.
2015-08-26 15:56:34 -07:00
address, val := voteSet.valSet.GetByIndex(valIndex)
if val == nil {
2015-08-26 15:56:34 -07:00
return false, nil, ErrVoteInvalidAccount
}
2015-08-26 15:56:34 -07:00
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)
2015-06-05 14:15:40 -07:00
if (vote.Height != voteSet.height) ||
(vote.Round != voteSet.round) ||
(vote.Type != voteSet.type_) {
return false, 0, ErrVoteUnexpectedStep
2014-09-14 15:37:32 -07:00
}
// If vote already exists, return false.
if existingVote := voteSet.votes[valIndex]; existingVote != nil {
2014-09-14 15:37:32 -07:00
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
return false, valIndex, nil
2014-09-14 15:37:32 -07:00
} else {
2016-03-17 01:27:58 -07:00
// Check signature.
2016-05-08 15:00:58 -07:00
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
2016-03-17 01:27:58 -07:00
// Bad signature.
return false, 0, ErrVoteInvalidSignature
}
return false, valIndex, &ErrVoteConflictingSignature{
VoteA: existingVote,
VoteB: vote,
}
2014-09-14 15:37:32 -07:00
}
}
2016-03-17 01:27:58 -07:00
// Check signature.
2016-05-08 15:00:58 -07:00
if !val.PubKey.VerifyBytes(SignBytes(voteSet.chainID, vote), vote.Signature) {
2016-03-17 01:27:58 -07:00
// Bad signature.
return false, 0, ErrVoteInvalidSignature
}
2014-09-14 15:37:32 -07:00
// Add vote.
voteSet.votes[valIndex] = vote
voteSet.votesBitArray.SetIndex(valIndex, true)
blockKey := string(vote.BlockHash) + string(wire.BinaryBytes(vote.BlockPartsHeader))
totalBlockHashVotes := voteSet.votesByBlock[blockKey] + val.VotingPower
voteSet.votesByBlock[blockKey] = totalBlockHashVotes
voteSet.totalVotes += val.VotingPower
2014-09-14 15:37:32 -07:00
// If we just nudged it up to two thirds majority, add it.
if totalBlockHashVotes > voteSet.valSet.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= voteSet.valSet.TotalVotingPower()*2/3 {
voteSet.maj23Hash = vote.BlockHash
voteSet.maj23PartsHeader = vote.BlockPartsHeader
voteSet.maj23Exists = true
2014-09-14 15:37:32 -07:00
}
return true, valIndex, nil
2014-09-14 15:37:32 -07:00
}
func (voteSet *VoteSet) BitArray() *BitArray {
if voteSet == nil {
return nil
2014-11-03 15:50:23 -08:00
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.votesBitArray.Copy()
2014-10-30 03:32:09 -07:00
}
func (voteSet *VoteSet) GetByIndex(valIndex int) *Vote {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.votes[valIndex]
2014-09-14 15:37:32 -07:00
}
func (voteSet *VoteSet) GetByAddress(address []byte) *Vote {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil {
2015-07-19 16:42:52 -07:00
PanicSanity("GetByAddress(address) returned nil")
2014-09-14 15:37:32 -07:00
}
return voteSet.votes[valIndex]
2014-09-14 15:37:32 -07:00
}
func (voteSet *VoteSet) HasTwoThirdsMajority() bool {
if voteSet == nil {
return false
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.maj23Exists
}
func (voteSet *VoteSet) IsCommit() bool {
if voteSet == nil {
return false
}
2015-10-12 15:19:55 -07:00
if voteSet.type_ != VoteTypePrecommit {
return false
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return len(voteSet.maj23Hash) > 0
}
2015-06-05 14:15:40 -07:00
func (voteSet *VoteSet) HasTwoThirdsAny() bool {
if voteSet == nil {
return false
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
2015-06-24 14:04:40 -07:00
return voteSet.totalVotes > voteSet.valSet.TotalVotingPower()*2/3
2015-06-05 14:15:40 -07:00
}
2014-09-14 15:37:32 -07:00
// Returns either a blockhash (or nil) that received +2/3 majority.
// If there exists no such majority, returns (nil, false).
func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if voteSet.maj23Exists {
return voteSet.maj23Hash, voteSet.maj23PartsHeader, true
2014-10-21 23:30:18 -07:00
} else {
return nil, PartSetHeader{}, false
2014-09-14 15:37:32 -07:00
}
}
func (voteSet *VoteSet) String() string {
2015-06-24 14:04:40 -07:00
if voteSet == nil {
return "nil-VoteSet"
}
2014-12-23 01:35:54 -08:00
return voteSet.StringIndented("")
2014-10-15 20:15:38 -07:00
}
2014-12-23 01:35:54 -08:00
func (voteSet *VoteSet) StringIndented(indent string) string {
voteStrings := make([]string, len(voteSet.votes))
2014-12-17 01:37:13 -08:00
for i, vote := range voteSet.votes {
if vote == nil {
2014-12-23 01:35:54 -08:00
voteStrings[i] = "nil-Vote"
2014-12-17 01:37:13 -08:00
} else {
voteStrings[i] = vote.String()
}
2014-10-15 20:15:38 -07:00
}
return fmt.Sprintf(`VoteSet{
%s H:%v R:%v T:%v
%s %v
%s %v
%s}`,
indent, voteSet.height, voteSet.round, voteSet.type_,
2014-10-15 20:15:38 -07:00
indent, strings.Join(voteStrings, "\n"+indent+" "),
indent, voteSet.votesBitArray,
2014-10-15 20:15:38 -07:00
indent)
}
2014-12-23 01:35:54 -08:00
func (voteSet *VoteSet) StringShort() string {
if voteSet == nil {
return "nil-VoteSet"
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
2015-05-04 11:18:21 -07:00
return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v +2/3:%v %v}`,
voteSet.height, voteSet.round, voteSet.type_, voteSet.maj23Exists, voteSet.votesBitArray)
}
2015-06-19 15:30:10 -07:00
//--------------------------------------------------------------------------------
2016-04-02 09:10:16 -07:00
// Commit
2015-06-19 15:30:10 -07:00
2016-04-02 09:10:16 -07:00
func (voteSet *VoteSet) MakeCommit() *Commit {
if voteSet.type_ != VoteTypePrecommit {
2016-04-02 09:10:16 -07:00
PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit")
2015-06-19 15:30:10 -07:00
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if len(voteSet.maj23Hash) == 0 {
2016-04-02 09:10:16 -07:00
PanicSanity("Cannot MakeCommit() unless a blockhash has +2/3")
2015-06-19 15:30:10 -07:00
}
precommits := make([]*Vote, voteSet.valSet.Size())
voteSet.valSet.Iterate(func(valIndex int, val *Validator) bool {
2015-06-19 15:30:10 -07:00
vote := voteSet.votes[valIndex]
if vote == nil {
return false
}
if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
return false
}
if !vote.BlockPartsHeader.Equals(voteSet.maj23PartsHeader) {
2015-06-19 15:30:10 -07:00
return false
}
2015-06-24 14:04:40 -07:00
precommits[valIndex] = vote
2015-06-19 15:30:10 -07:00
return false
})
2016-04-02 09:10:16 -07:00
return &Commit{
2015-06-19 15:30:10 -07:00
Precommits: precommits,
}
}
2016-05-08 15:00:58 -07:00
//----------------------------------------
// Common interface between *consensus.VoteSet and types.Commit
type VoteSetReader interface {
Height() int
Round() int
Type() byte
Size() int
BitArray() *BitArray
GetByIndex(int) *Vote
IsCommit() bool
}