removed commitTime from VoteSet.

This commit is contained in:
Jae Kwon 2014-10-21 23:30:18 -07:00
parent 89418ee7af
commit bccf0afe00
4 changed files with 190 additions and 190 deletions

View File

@ -92,6 +92,12 @@ func calcRoundInfo(startTime time.Time) (round uint16, roundStartTime time.Time,
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type RoundAction struct {
Height uint32 // The block height for which consensus is reaching for.
Round uint16 // The round number at given height.
Action RoundActionType // Action to perform.
}
type ConsensusReactor struct { type ConsensusReactor struct {
sw *p2p.Switch sw *p2p.Switch
quit chan struct{} quit chan struct{}
@ -241,9 +247,9 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
} }
if added { if added {
// Maybe send HasVotesMessage // Maybe send HasVotesMessage
// TODO optimize. It would be better to just acks for each vote!
voteAddCounter++ voteAddCounter++
if voteAddCounter%hasVotesThreshold == 0 { if voteAddCounter%hasVotesThreshold == 0 {
// TODO optimize.
msg := &HasVotesMessage{ msg := &HasVotesMessage{
Height: rs.Height, Height: rs.Height,
Round: rs.Round, Round: rs.Round,
@ -274,56 +280,50 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
} }
} }
//------------------------------------- //--------------------------------------
type RoundAction struct {
Height uint32 // The block height for which consensus is reaching for.
Round uint16 // The round number at given height.
Action RoundActionType // Action to perform.
}
// Source of all round state transitions (and votes). // Source of all round state transitions (and votes).
// It can be preemptively woken up via a message to
// doActionCh.
func (conR *ConsensusReactor) stepTransitionRoutine() { func (conR *ConsensusReactor) stepTransitionRoutine() {
// Schedule the next action by pushing a RoundAction{} to conR.doActionCh
// when it is due.
scheduleNextAction := func() { scheduleNextAction := func() {
// Figure out which height/round/step we're at,
// then schedule an action for when it is due.
rs := conR.conS.GetRoundState() rs := conR.conS.GetRoundState()
_, _, roundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime) _, _, roundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime)
switch rs.Step { go func() {
case RoundStepStart: switch rs.Step {
// It's a new RoundState. case RoundStepStart:
if elapsedRatio < 0 { // It's a new RoundState.
// startTime is in the future. if elapsedRatio < 0 {
time.Sleep(time.Duration(-1.0*elapsedRatio) * roundDuration) // startTime is in the future.
time.Sleep(time.Duration(-1.0*elapsedRatio) * roundDuration)
}
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPropose}
case RoundStepPropose:
// Wake up when it's time to vote.
time.Sleep(time.Duration(roundDeadlinePrevote-elapsedRatio) * roundDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrevote}
case RoundStepPrevote:
// Wake up when it's time to precommit.
time.Sleep(time.Duration(roundDeadlinePrecommit-elapsedRatio) * roundDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrecommit}
case RoundStepPrecommit:
// Wake up when the round is over.
time.Sleep(time.Duration(1.0-elapsedRatio) * roundDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionNextRound}
case RoundStepCommit:
panic("Should not happen: RoundStepCommit waits until +2/3 commits.")
case RoundStepCommitWait:
// Wake up when it's time to finalize commit.
if rs.CommitTime.IsZero() {
panic("RoundStepCommitWait requires rs.CommitTime")
}
time.Sleep(rs.CommitTime.Sub(time.Now()) + finalizeDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionFinalize}
default:
panic("Should not happen")
} }
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPropose} }()
case RoundStepPropose:
// Wake up when it's time to vote.
time.Sleep(time.Duration(roundDeadlinePrevote-elapsedRatio) * roundDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrevote}
case RoundStepPrevote:
// Wake up when it's time to precommit.
time.Sleep(time.Duration(roundDeadlinePrecommit-elapsedRatio) * roundDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrecommit}
case RoundStepPrecommit:
// Wake up when the round is over.
time.Sleep(time.Duration(1.0-elapsedRatio) * roundDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionNextRound}
case RoundStepCommit:
panic("Should not happen: RoundStepCommit waits until +2/3 commits.")
case RoundStepCommitWait:
// Wake up when it's time to finalize commit.
if rs.CommitTime.IsZero() {
panic("RoundStepCommitWait requires rs.CommitTime")
}
time.Sleep(rs.CommitTime.Sub(time.Now()) + finalizeDuration)
conR.doActionCh <- RoundAction{rs.Height, rs.Round, RoundActionFinalize}
default:
panic("Should not happen")
}
} }
scheduleNextAction() scheduleNextAction()
@ -435,7 +435,7 @@ ACTION_LOOP:
if rs.Step >= RoundStepCommitWait { if rs.Step >= RoundStepCommitWait {
continue ACTION_LOOP continue ACTION_LOOP
} }
// First we must commit. // Commit first we haven't already.
if rs.Step < RoundStepCommit { if rs.Step < RoundStepCommit {
// NOTE: Duplicated in RoundActionCommit. // NOTE: Duplicated in RoundActionCommit.
hash := conR.conS.RunActionCommit(rs.Height) hash := conR.conS.RunActionCommit(rs.Height)
@ -451,7 +451,7 @@ ACTION_LOOP:
panic("This shouldn't happen") panic("This shouldn't happen")
} }
} }
// Now wait for more commit votes. // Wait for more commit votes.
conR.conS.RunActionCommitWait(rs.Height) conR.conS.RunActionCommitWait(rs.Height)
scheduleNextAction() scheduleNextAction()
continue ACTION_LOOP continue ACTION_LOOP
@ -474,7 +474,16 @@ ACTION_LOOP:
} }
} }
//------------------------------------- func (conR *ConsensusReactor) signAndBroadcastVote(rs *RoundState, vote *Vote) {
if rs.PrivValidator != nil {
rs.PrivValidator.Sign(vote)
conR.conS.AddVote(vote)
msg := p2p.TypedMessage{msgTypeVote, vote}
conR.sw.Broadcast(VoteCh, msg)
}
}
//--------------------------------------
func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) { func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) {
@ -540,8 +549,6 @@ OUTER_LOOP:
} }
} }
//-------------------------------------
func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) { func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) {
OUTER_LOOP: OUTER_LOOP:
for { for {
@ -621,16 +628,6 @@ OUTER_LOOP:
} }
} }
// Signs a vote document and broadcasts it.
func (conR *ConsensusReactor) signAndBroadcastVote(rs *RoundState, vote *Vote) {
if rs.PrivValidator != nil {
rs.PrivValidator.Sign(vote)
conR.conS.AddVote(vote)
msg := p2p.TypedMessage{msgTypeVote, vote}
conR.sw.Broadcast(VoteCh, msg)
}
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Read only when returned by PeerState.GetRoundState(). // Read only when returned by PeerState.GetRoundState().

View File

@ -222,30 +222,7 @@ func (cs *ConsensusState) SetPrivValidator(priv *PrivValidator) {
cs.PrivValidator = priv cs.PrivValidator = priv
} }
func (cs *ConsensusState) SetProposal(proposal *Proposal) error { //-----------------------------------------------------------------------------
cs.mtx.Lock()
defer cs.mtx.Unlock()
// Already have one
if cs.Proposal != nil {
return nil
}
// Invalid.
if proposal.Height != cs.Height || proposal.Round != cs.Round {
return nil
}
// Verify signature
if !cs.Validators.Proposer().Verify(proposal) {
return ErrInvalidProposalSignature
}
cs.Proposal = proposal
cs.ProposalBlockPartSet = NewPartSetFromMetadata(proposal.BlockPartsTotal, proposal.BlockPartsHash)
cs.ProposalPOLPartSet = NewPartSetFromMetadata(proposal.POLPartsTotal, proposal.POLPartsHash)
return nil
}
func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) { func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
cs.mtx.Lock() cs.mtx.Lock()
@ -321,61 +298,6 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
cs.ProposalPOLPartSet = polPartSet cs.ProposalPOLPartSet = polPartSet
} }
// NOTE: block is not necessarily valid.
func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part *Part) (added bool, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
// Blocks might be reused, so round mismatch is OK
if cs.Height != height {
return false, nil
}
// We're not expecting a block part.
if cs.ProposalBlockPartSet != nil {
return false, nil // TODO: bad peer? Return error?
}
added, err = cs.ProposalBlockPartSet.AddPart(part)
if err != nil {
return added, err
}
if added && cs.ProposalBlockPartSet.IsComplete() {
var n int64
var err error
cs.ProposalBlock = ReadBlock(cs.ProposalBlockPartSet.GetReader(), &n, &err)
return true, err
}
return true, nil
}
// NOTE: POL is not necessarily valid.
func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *Part) (added bool, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round {
return false, nil
}
// We're not expecting a POL part.
if cs.ProposalPOLPartSet != nil {
return false, nil // TODO: bad peer? Return error?
}
added, err = cs.ProposalPOLPartSet.AddPart(part)
if err != nil {
return added, err
}
if added && cs.ProposalPOLPartSet.IsComplete() {
var n int64
var err error
cs.ProposalPOL = ReadPOL(cs.ProposalPOLPartSet.GetReader(), &n, &err)
return true, err
}
return true, nil
}
func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) []byte { func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) []byte {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
@ -399,24 +321,6 @@ func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) []byte {
} }
} }
func (cs *ConsensusState) AddVote(vote *Vote) (added bool, err error) {
switch vote.Type {
case VoteTypePrevote:
// Prevotes checks for height+round match.
return cs.Prevotes.Add(vote)
case VoteTypePrecommit:
// Precommits checks for height+round match.
return cs.Precommits.Add(vote)
case VoteTypeCommit:
// Commits checks for height match.
cs.Prevotes.Add(vote)
cs.Precommits.Add(vote)
return cs.Commits.Add(vote)
default:
panic("Unknown vote type")
}
}
// Lock the ProposalBlock if we have enough prevotes for it, // Lock the ProposalBlock if we have enough prevotes for it,
// or unlock an existing lock if +2/3 of prevotes were nil. // or unlock an existing lock if +2/3 of prevotes were nil.
// Returns a blockhash if a block was locked. // Returns a blockhash if a block was locked.
@ -428,7 +332,7 @@ func (cs *ConsensusState) RunActionPrecommit(height uint32, round uint16) []byte
} }
cs.Step = RoundStepPrecommit cs.Step = RoundStepPrecommit
if hash, _, ok := cs.Prevotes.TwoThirdsMajority(); ok { if hash, ok := cs.Prevotes.TwoThirdsMajority(); ok {
// Remember this POL. (hash may be nil) // Remember this POL. (hash may be nil)
cs.LockedPOL = cs.Prevotes.MakePOL() cs.LockedPOL = cs.Prevotes.MakePOL()
@ -477,7 +381,7 @@ func (cs *ConsensusState) RunActionCommit(height uint32) []byte {
} }
cs.Step = RoundStepCommit cs.Step = RoundStepCommit
if hash, _, ok := cs.Precommits.TwoThirdsMajority(); ok { if hash, ok := cs.Precommits.TwoThirdsMajority(); ok {
// There are some strange cases that shouldn't happen // There are some strange cases that shouldn't happen
// (unless voters are duplicitous). // (unless voters are duplicitous).
@ -534,9 +438,8 @@ func (cs *ConsensusState) RunActionCommitWait(height uint32) {
} }
cs.Step = RoundStepCommitWait cs.Step = RoundStepCommitWait
if _, commitTime, ok := cs.Commits.TwoThirdsMajority(); ok { if cs.Commits.HasTwoThirdsMajority() {
// Remember the commitTime. cs.CommitTime = time.Now()
cs.CommitTime = commitTime
} else { } else {
panic("RunActionCommitWait() expects +2/3 commits") panic("RunActionCommitWait() expects +2/3 commits")
} }
@ -553,6 +456,106 @@ func (cs *ConsensusState) RunActionFinalize(height uint32) {
cs.updateToState(cs.stagedState) cs.updateToState(cs.stagedState)
} }
//-----------------------------------------------------------------------------
func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
cs.mtx.Lock()
defer cs.mtx.Unlock()
// Already have one
if cs.Proposal != nil {
return nil
}
// Invalid.
if proposal.Height != cs.Height || proposal.Round != cs.Round {
return nil
}
// Verify signature
if !cs.Validators.Proposer().Verify(proposal) {
return ErrInvalidProposalSignature
}
cs.Proposal = proposal
cs.ProposalBlockPartSet = NewPartSetFromMetadata(proposal.BlockPartsTotal, proposal.BlockPartsHash)
cs.ProposalPOLPartSet = NewPartSetFromMetadata(proposal.POLPartsTotal, proposal.POLPartsHash)
return nil
}
// NOTE: block is not necessarily valid.
func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part *Part) (added bool, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
// Blocks might be reused, so round mismatch is OK
if cs.Height != height {
return false, nil
}
// We're not expecting a block part.
if cs.ProposalBlockPartSet != nil {
return false, nil // TODO: bad peer? Return error?
}
added, err = cs.ProposalBlockPartSet.AddPart(part)
if err != nil {
return added, err
}
if added && cs.ProposalBlockPartSet.IsComplete() {
var n int64
var err error
cs.ProposalBlock = ReadBlock(cs.ProposalBlockPartSet.GetReader(), &n, &err)
return true, err
}
return true, nil
}
// NOTE: POL is not necessarily valid.
func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *Part) (added bool, err error) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round {
return false, nil
}
// We're not expecting a POL part.
if cs.ProposalPOLPartSet != nil {
return false, nil // TODO: bad peer? Return error?
}
added, err = cs.ProposalPOLPartSet.AddPart(part)
if err != nil {
return added, err
}
if added && cs.ProposalPOLPartSet.IsComplete() {
var n int64
var err error
cs.ProposalPOL = ReadPOL(cs.ProposalPOLPartSet.GetReader(), &n, &err)
return true, err
}
return true, nil
}
func (cs *ConsensusState) AddVote(vote *Vote) (added bool, err error) {
switch vote.Type {
case VoteTypePrevote:
// Prevotes checks for height+round match.
return cs.Prevotes.Add(vote)
case VoteTypePrecommit:
// Precommits checks for height+round match.
return cs.Precommits.Add(vote)
case VoteTypeCommit:
// Commits checks for height match.
cs.Prevotes.Add(vote)
cs.Precommits.Add(vote)
return cs.Commits.Add(vote)
default:
panic("Unknown vote type")
}
}
func (cs *ConsensusState) stageBlock(block *Block) error { func (cs *ConsensusState) stageBlock(block *Block) error {
// Already staged? // Already staged?

View File

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"sync" "sync"
"time"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
@ -23,14 +22,14 @@ type VoteSet struct {
round uint16 round uint16
type_ byte type_ byte
mtx sync.Mutex mtx sync.Mutex
vset *state.ValidatorSet vset *state.ValidatorSet
votes map[uint64]*Vote votes map[uint64]*Vote
votesBitArray BitArray votesBitArray BitArray
votesByBlockHash map[string]uint64 votesByBlockHash map[string]uint64
totalVotes uint64 totalVotes uint64
twoThirdsMajority []byte twoThirdsMajority []byte
twoThirdsCommitTime time.Time twoThirdsExists bool
} }
// Constructs a new VoteSet struct used to accumulate votes for each round. // Constructs a new VoteSet struct used to accumulate votes for each round.
@ -105,7 +104,7 @@ func (vs *VoteSet) addVote(vote *Vote) (bool, error) {
if totalBlockHashVotes > vs.vset.TotalVotingPower()*2/3 && if totalBlockHashVotes > vs.vset.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= vs.vset.TotalVotingPower()*2/3 { (totalBlockHashVotes-val.VotingPower) <= vs.vset.TotalVotingPower()*2/3 {
vs.twoThirdsMajority = vote.BlockHash vs.twoThirdsMajority = vote.BlockHash
vs.twoThirdsCommitTime = time.Now() vs.twoThirdsExists = true
} }
return true, nil return true, nil
@ -149,18 +148,19 @@ func (vs *VoteSet) HasTwoThirdsMajority() bool {
} }
vs.mtx.Lock() vs.mtx.Lock()
defer vs.mtx.Unlock() defer vs.mtx.Unlock()
return !vs.twoThirdsCommitTime.IsZero() return vs.twoThirdsExists
} }
// Returns either a blockhash (or nil) that received +2/3 majority. // Returns either a blockhash (or nil) that received +2/3 majority.
// If there exists no such majority, returns (nil, false). // If there exists no such majority, returns (nil, false).
func (vs *VoteSet) TwoThirdsMajority() (hash []byte, commitTime time.Time, ok bool) { func (vs *VoteSet) TwoThirdsMajority() (hash []byte, ok bool) {
vs.mtx.Lock() vs.mtx.Lock()
defer vs.mtx.Unlock() defer vs.mtx.Unlock()
if vs.twoThirdsCommitTime.IsZero() { if vs.twoThirdsExists {
return nil, time.Time{}, false return vs.twoThirdsMajority, true
} else {
return nil, false
} }
return vs.twoThirdsMajority, vs.twoThirdsCommitTime, true
} }
func (vs *VoteSet) MakePOL() *POL { func (vs *VoteSet) MakePOL() *POL {
@ -169,7 +169,7 @@ func (vs *VoteSet) MakePOL() *POL {
} }
vs.mtx.Lock() vs.mtx.Lock()
defer vs.mtx.Unlock() defer vs.mtx.Unlock()
if vs.twoThirdsCommitTime.IsZero() { if !vs.twoThirdsExists {
return nil return nil
} }
majHash := vs.twoThirdsMajority // hash may be nil. majHash := vs.twoThirdsMajority // hash may be nil.

View File

@ -20,8 +20,8 @@ func TestAddVote(t *testing.T) {
if voteSet.BitArray().GetIndex(0) { if voteSet.BitArray().GetIndex(0) {
t.Errorf("Expected BitArray.GetIndex(0) to be false") t.Errorf("Expected BitArray.GetIndex(0) to be false")
} }
hash, commitTime, ok := voteSet.TwoThirdsMajority() hash, ok := voteSet.TwoThirdsMajority()
if hash != nil || !commitTime.IsZero() || ok { if hash != nil || ok {
t.Errorf("There should be no 2/3 majority") t.Errorf("There should be no 2/3 majority")
} }
@ -35,8 +35,8 @@ func TestAddVote(t *testing.T) {
if !voteSet.BitArray().GetIndex(0) { if !voteSet.BitArray().GetIndex(0) {
t.Errorf("Expected BitArray.GetIndex(0) to be true") t.Errorf("Expected BitArray.GetIndex(0) to be true")
} }
hash, commitTime, ok = voteSet.TwoThirdsMajority() hash, ok = voteSet.TwoThirdsMajority()
if hash != nil || !commitTime.IsZero() || ok { if hash != nil || ok {
t.Errorf("There should be no 2/3 majority") t.Errorf("There should be no 2/3 majority")
} }
} }
@ -50,8 +50,8 @@ func Test2_3Majority(t *testing.T) {
privAccounts[i].Sign(vote) privAccounts[i].Sign(vote)
voteSet.Add(vote) voteSet.Add(vote)
} }
hash, commitTime, ok := voteSet.TwoThirdsMajority() hash, ok := voteSet.TwoThirdsMajority()
if hash != nil || !commitTime.IsZero() || ok { if hash != nil || ok {
t.Errorf("There should be no 2/3 majority") t.Errorf("There should be no 2/3 majority")
} }
@ -59,8 +59,8 @@ func Test2_3Majority(t *testing.T) {
vote.BlockHash = CRandBytes(32) vote.BlockHash = CRandBytes(32)
privAccounts[6].Sign(vote) privAccounts[6].Sign(vote)
voteSet.Add(vote) voteSet.Add(vote)
hash, commitTime, ok = voteSet.TwoThirdsMajority() hash, ok = voteSet.TwoThirdsMajority()
if hash != nil || !commitTime.IsZero() || ok { if hash != nil || ok {
t.Errorf("There should be no 2/3 majority") t.Errorf("There should be no 2/3 majority")
} }
@ -68,8 +68,8 @@ func Test2_3Majority(t *testing.T) {
vote.BlockHash = nil vote.BlockHash = nil
privAccounts[7].Sign(vote) privAccounts[7].Sign(vote)
voteSet.Add(vote) voteSet.Add(vote)
hash, commitTime, ok = voteSet.TwoThirdsMajority() hash, ok = voteSet.TwoThirdsMajority()
if hash != nil || commitTime.IsZero() || !ok { if hash != nil || !ok {
t.Errorf("There should be 2/3 majority for nil") t.Errorf("There should be 2/3 majority for nil")
} }
@ -128,8 +128,8 @@ func TestAddCommitsToPrevoteVotes(t *testing.T) {
privAccounts[i].Sign(vote) privAccounts[i].Sign(vote)
voteSet.Add(vote) voteSet.Add(vote)
} }
hash, commitTime, ok := voteSet.TwoThirdsMajority() hash, ok := voteSet.TwoThirdsMajority()
if hash != nil || !commitTime.IsZero() || ok { if hash != nil || ok {
t.Errorf("There should be no 2/3 majority") t.Errorf("There should be no 2/3 majority")
} }
@ -174,8 +174,8 @@ func TestAddCommitsToPrevoteVotes(t *testing.T) {
} }
// We should have 2/3 majority // We should have 2/3 majority
hash, commitTime, ok = voteSet.TwoThirdsMajority() hash, ok = voteSet.TwoThirdsMajority()
if hash != nil || commitTime.IsZero() || !ok { if hash != nil || !ok {
t.Errorf("There should be 2/3 majority for nil") t.Errorf("There should be 2/3 majority for nil")
} }