mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-30 17:22:13 +00:00
147 lines
4.0 KiB
Go
147 lines
4.0 KiB
Go
|
package consensus
|
||
|
|
||
|
import (
|
||
|
"strings"
|
||
|
"sync"
|
||
|
|
||
|
. "github.com/tendermint/tendermint/common"
|
||
|
sm "github.com/tendermint/tendermint/state"
|
||
|
"github.com/tendermint/tendermint/types"
|
||
|
)
|
||
|
|
||
|
type RoundVoteSet struct {
|
||
|
Prevotes *VoteSet
|
||
|
Precommits *VoteSet
|
||
|
}
|
||
|
|
||
|
// Keeps track of VoteSets for all the rounds of a height.
|
||
|
// We add the commit votes to all the affected rounds,
|
||
|
// and for new rounds carry over the commit set. Commits have
|
||
|
// an associated round, so the performance hit won't be O(rounds).
|
||
|
type HeightVoteSet struct {
|
||
|
height uint
|
||
|
valSet *sm.ValidatorSet
|
||
|
|
||
|
mtx sync.Mutex
|
||
|
round uint // max tracked round
|
||
|
roundVoteSets map[uint]RoundVoteSet // keys: [0...round]
|
||
|
commits *VoteSet // all commits for height
|
||
|
}
|
||
|
|
||
|
func NewHeightVoteSet(height uint, valSet *sm.ValidatorSet) *HeightVoteSet {
|
||
|
hvs := &HeightVoteSet{
|
||
|
height: height,
|
||
|
valSet: valSet,
|
||
|
roundVoteSets: make(map[uint]RoundVoteSet),
|
||
|
commits: NewVoteSet(height, 0, types.VoteTypeCommit, valSet),
|
||
|
}
|
||
|
hvs.SetRound(0)
|
||
|
return hvs
|
||
|
}
|
||
|
|
||
|
func (hvs *HeightVoteSet) Height() uint {
|
||
|
return hvs.height
|
||
|
}
|
||
|
|
||
|
func (hvs *HeightVoteSet) Round() uint {
|
||
|
hvs.mtx.Lock()
|
||
|
defer hvs.mtx.Unlock()
|
||
|
return hvs.round
|
||
|
}
|
||
|
|
||
|
// Create more RoundVoteSets up to round with all commits carried over.
|
||
|
func (hvs *HeightVoteSet) SetRound(round uint) {
|
||
|
hvs.mtx.Lock()
|
||
|
defer hvs.mtx.Unlock()
|
||
|
if hvs.round != 0 && (round < hvs.round+1) {
|
||
|
panic("SetRound() must increment hvs.round")
|
||
|
}
|
||
|
for r := hvs.round + 1; r <= round; r++ {
|
||
|
prevotes := NewVoteSet(hvs.height, r, types.VoteTypePrevote, hvs.valSet)
|
||
|
prevotes.AddFromCommits(hvs.commits)
|
||
|
precommits := NewVoteSet(hvs.height, r, types.VoteTypePrecommit, hvs.valSet)
|
||
|
precommits.AddFromCommits(hvs.commits)
|
||
|
hvs.roundVoteSets[r] = RoundVoteSet{
|
||
|
Prevotes: prevotes,
|
||
|
Precommits: precommits,
|
||
|
}
|
||
|
}
|
||
|
hvs.round = round
|
||
|
}
|
||
|
|
||
|
// CONTRACT: if err == nil, added == true
|
||
|
func (hvs *HeightVoteSet) AddByAddress(address []byte, vote *types.Vote) (added bool, index uint, err error) {
|
||
|
hvs.mtx.Lock()
|
||
|
defer hvs.mtx.Unlock()
|
||
|
voteSet := hvs.getVoteSet(vote.Round, vote.Type)
|
||
|
if voteSet == nil {
|
||
|
return
|
||
|
}
|
||
|
added, index, err = voteSet.AddByAddress(address, vote)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
// If vote is commit, also add to all prevote/precommit for future rounds.
|
||
|
if vote.Type == types.VoteTypeCommit {
|
||
|
for round := vote.Round + 1; round <= hvs.round; round++ {
|
||
|
voteSet := hvs.getVoteSet(round, types.VoteTypePrevote)
|
||
|
_, _, err = voteSet.AddByAddress(address, vote)
|
||
|
if err != nil {
|
||
|
// TODO slash for prevote after commit
|
||
|
log.Warn("Prevote after commit", "address", address, "vote", vote)
|
||
|
}
|
||
|
voteSet = hvs.getVoteSet(round, types.VoteTypePrecommit)
|
||
|
_, _, err = voteSet.AddByAddress(address, vote)
|
||
|
if err != nil {
|
||
|
// TODO slash for prevote after commit
|
||
|
log.Warn("Prevote after commit", "address", address, "vote", vote)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (hvs *HeightVoteSet) GetVoteSet(round uint, type_ byte) *VoteSet {
|
||
|
hvs.mtx.Lock()
|
||
|
defer hvs.mtx.Unlock()
|
||
|
return hvs.getVoteSet(round, type_)
|
||
|
}
|
||
|
|
||
|
func (hvs *HeightVoteSet) getVoteSet(round uint, type_ byte) *VoteSet {
|
||
|
if type_ == types.VoteTypeCommit {
|
||
|
return hvs.commits
|
||
|
}
|
||
|
rvs, ok := hvs.roundVoteSets[round]
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
switch type_ {
|
||
|
case types.VoteTypePrevote:
|
||
|
return rvs.Prevotes
|
||
|
case types.VoteTypePrecommit:
|
||
|
return rvs.Precommits
|
||
|
default:
|
||
|
panic(Fmt("Unexpected vote type %X", type_))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (hvs *HeightVoteSet) String() string {
|
||
|
return hvs.StringIndented("")
|
||
|
}
|
||
|
|
||
|
func (hvs *HeightVoteSet) StringIndented(indent string) string {
|
||
|
vsStrings := make([]string, 0, hvs.round*2+1)
|
||
|
vsStrings = append(vsStrings, hvs.commits.StringShort())
|
||
|
for round := uint(0); round <= hvs.round; round++ {
|
||
|
voteSetString := hvs.roundVoteSets[round].Prevotes.StringShort()
|
||
|
vsStrings = append(vsStrings, voteSetString)
|
||
|
voteSetString = hvs.roundVoteSets[round].Precommits.StringShort()
|
||
|
vsStrings = append(vsStrings, voteSetString)
|
||
|
}
|
||
|
return Fmt(`HeightVoteSet{H:%v R:0~%v
|
||
|
%s %v
|
||
|
%s}`,
|
||
|
indent, strings.Join(vsStrings, "\n"+indent+" "),
|
||
|
indent)
|
||
|
}
|