tendermint/consensus/height_vote_set.go

221 lines
6.0 KiB
Go
Raw Normal View History

2015-06-04 13:36:57 -07:00
package consensus
import (
"strings"
"sync"
. "github.com/tendermint/go-common"
2015-06-04 13:36:57 -07:00
"github.com/tendermint/tendermint/types"
)
type RoundVoteSet struct {
Prevotes *types.VoteSet
Precommits *types.VoteSet
2015-06-04 13:36:57 -07:00
}
/*
Keeps track of all VoteSets from round 0 to round 'round'.
Also keeps track of up to one RoundVoteSet greater than
'round' from each peer, to facilitate catchup syncing of commits.
A commit is +2/3 precommits for a block at a round,
but which round is not known in advance, so when a peer
provides a precommit for a round greater than mtx.round,
we create a new entry in roundVoteSets but also remember the
peer to prevent abuse.
We let each peer provide us with up to 2 unexpected "catchup" rounds.
One for their LastCommit round, and another for the official commit round.
*/
2015-06-04 13:36:57 -07:00
type HeightVoteSet struct {
2016-05-08 15:00:58 -07:00
chainID string
height int
valSet *types.ValidatorSet
2015-06-04 13:36:57 -07:00
mtx sync.Mutex
round int // max tracked round
roundVoteSets map[int]RoundVoteSet // keys: [0...round]
peerCatchupRounds map[string][]int // keys: peer.Key; values: at most 2 rounds
2015-06-04 13:36:57 -07:00
}
2016-05-08 15:00:58 -07:00
func NewHeightVoteSet(chainID string, height int, valSet *types.ValidatorSet) *HeightVoteSet {
2015-06-04 13:36:57 -07:00
hvs := &HeightVoteSet{
2016-07-11 18:45:08 -07:00
chainID: chainID,
2015-06-04 13:36:57 -07:00
}
2016-07-11 18:45:08 -07:00
hvs.Reset(height, valSet)
return hvs
}
func (hvs *HeightVoteSet) Reset(height int, valSet *types.ValidatorSet) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
hvs.height = height
hvs.valSet = valSet
hvs.roundVoteSets = make(map[int]RoundVoteSet)
hvs.peerCatchupRounds = make(map[string][]int)
2016-07-11 18:45:08 -07:00
2015-06-24 14:04:40 -07:00
hvs.addRound(0)
hvs.round = 0
2015-06-04 13:36:57 -07:00
}
func (hvs *HeightVoteSet) Height() int {
2016-07-11 18:45:08 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
2015-06-04 13:36:57 -07:00
return hvs.height
}
func (hvs *HeightVoteSet) Round() int {
2015-06-04 13:36:57 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
return hvs.round
}
2015-06-05 14:15:40 -07:00
// Create more RoundVoteSets up to round.
func (hvs *HeightVoteSet) SetRound(round int) {
2015-06-04 13:36:57 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if hvs.round != 0 && (round < hvs.round+1) {
2015-07-19 16:42:52 -07:00
PanicSanity("SetRound() must increment hvs.round")
2015-06-04 13:36:57 -07:00
}
for r := hvs.round + 1; r <= round; r++ {
2015-06-22 19:04:31 -07:00
if _, ok := hvs.roundVoteSets[r]; ok {
continue // Already exists because peerCatchupRounds.
2015-06-04 13:36:57 -07:00
}
hvs.addRound(r)
2015-06-04 13:36:57 -07:00
}
hvs.round = round
}
func (hvs *HeightVoteSet) addRound(round int) {
2015-06-22 19:04:31 -07:00
if _, ok := hvs.roundVoteSets[round]; ok {
2015-07-19 16:42:52 -07:00
PanicSanity("addRound() for an existing round")
}
2015-08-12 11:00:23 -07:00
log.Debug("addRound(round)", "round", round)
2016-05-08 15:00:58 -07:00
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet)
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrecommit, hvs.valSet)
2015-06-22 19:04:31 -07:00
hvs.roundVoteSets[round] = RoundVoteSet{
Prevotes: prevotes,
Precommits: precommits,
}
}
2015-06-22 19:04:31 -07:00
// Duplicate votes return added=false, err=nil.
2015-06-24 14:04:40 -07:00
// By convention, peerKey is "" if origin is self.
func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerKey string) (added bool, err error) {
2015-06-04 13:36:57 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
2016-09-05 17:33:02 -07:00
if !types.IsVoteTypeValid(vote.Type) {
return
}
2015-06-04 13:36:57 -07:00
voteSet := hvs.getVoteSet(vote.Round, vote.Type)
if voteSet == nil {
if rndz := hvs.peerCatchupRounds[peerKey]; len(rndz) < 2 {
hvs.addRound(vote.Round)
voteSet = hvs.getVoteSet(vote.Round, vote.Type)
hvs.peerCatchupRounds[peerKey] = append(rndz, vote.Round)
} else {
// Peer has sent a vote that does not match our round,
// for more than one round. Bad peer!
// TODO punish peer.
log.Warn("Deal with peer giving votes from unwanted rounds")
return
}
2015-06-04 13:36:57 -07:00
}
added, err = voteSet.AddVote(vote)
2015-06-04 13:36:57 -07:00
return
}
func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet {
2015-06-04 13:36:57 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
2015-06-05 14:15:40 -07:00
return hvs.getVoteSet(round, types.VoteTypePrevote)
2015-06-04 13:36:57 -07:00
}
func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
2015-06-05 14:15:40 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
return hvs.getVoteSet(round, types.VoteTypePrecommit)
}
// Last round and blockID that has +2/3 prevotes for a particular block or nil.
2015-06-05 14:15:40 -07:00
// Returns -1 if no such round exists.
func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
2015-06-05 14:15:40 -07:00
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
for r := hvs.round; r >= 0; r-- {
rvs := hvs.getVoteSet(r, types.VoteTypePrevote)
polBlockID, ok := rvs.TwoThirdsMajority()
if ok {
return r, polBlockID
2015-06-05 14:15:40 -07:00
}
2015-06-04 13:36:57 -07:00
}
return -1, types.BlockID{}
2015-06-05 14:15:40 -07:00
}
func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
2015-06-04 13:36:57 -07:00
rvs, ok := hvs.roundVoteSets[round]
if !ok {
return nil
}
switch type_ {
case types.VoteTypePrevote:
return rvs.Prevotes
case types.VoteTypePrecommit:
return rvs.Precommits
default:
2015-07-19 16:42:52 -07:00
PanicSanity(Fmt("Unexpected vote type %X", type_))
return nil
2015-06-04 13:36:57 -07:00
}
}
func (hvs *HeightVoteSet) String() string {
return hvs.StringIndented("")
}
func (hvs *HeightVoteSet) StringIndented(indent string) string {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
vsStrings := make([]string, 0, (len(hvs.roundVoteSets)+1)*2)
// rounds 0 ~ hvs.round inclusive
for round := 0; round <= hvs.round; round++ {
2015-06-04 13:36:57 -07:00
voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
vsStrings = append(vsStrings, voteSetString)
voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
vsStrings = append(vsStrings, voteSetString)
}
// all other peer catchup rounds
for round, roundVoteSet := range hvs.roundVoteSets {
if round <= hvs.round {
continue
}
voteSetString := roundVoteSet.Prevotes.StringShort()
vsStrings = append(vsStrings, voteSetString)
voteSetString = roundVoteSet.Precommits.StringShort()
vsStrings = append(vsStrings, voteSetString)
}
2015-06-04 13:36:57 -07:00
return Fmt(`HeightVoteSet{H:%v R:0~%v
%s %v
%s}`,
hvs.height, hvs.round,
2015-06-04 13:36:57 -07:00
indent, strings.Join(vsStrings, "\n"+indent+" "),
indent)
}
2016-09-05 17:33:02 -07:00
// If a peer claims that it has 2/3 majority for given blockKey, call this.
// NOTE: if there are too many peers, or too much peer churn,
// this can cause memory issues.
// TODO: implement ability to remove peers too
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID string, blockID types.BlockID) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) {
return
}
voteSet := hvs.getVoteSet(round, type_)
if voteSet == nil {
return
}
voteSet.SetPeerMaj23(peerID, blockID)
}