|
@ -83,7 +83,8 @@ type ConsensusState struct { |
|
|
// internal state
|
|
|
// internal state
|
|
|
mtx sync.RWMutex |
|
|
mtx sync.RWMutex |
|
|
cstypes.RoundState |
|
|
cstypes.RoundState |
|
|
state sm.State // State until height-1.
|
|
|
|
|
|
|
|
|
triggeredTimeoutPrecommit bool |
|
|
|
|
|
state sm.State // State until height-1.
|
|
|
|
|
|
|
|
|
// state changes may be triggered by: msgs from peers,
|
|
|
// state changes may be triggered by: msgs from peers,
|
|
|
// msgs from ourself, or by timeouts
|
|
|
// msgs from ourself, or by timeouts
|
|
@ -711,6 +712,7 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { |
|
|
cs.enterPrecommit(ti.Height, ti.Round) |
|
|
cs.enterPrecommit(ti.Height, ti.Round) |
|
|
case cstypes.RoundStepPrecommitWait: |
|
|
case cstypes.RoundStepPrecommitWait: |
|
|
cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()) |
|
|
cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()) |
|
|
|
|
|
cs.enterPrecommit(ti.Height, ti.Round) |
|
|
cs.enterNewRound(ti.Height, ti.Round+1) |
|
|
cs.enterNewRound(ti.Height, ti.Round+1) |
|
|
default: |
|
|
default: |
|
|
panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step)) |
|
|
panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step)) |
|
@ -772,6 +774,7 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) { |
|
|
cs.ProposalBlockParts = nil |
|
|
cs.ProposalBlockParts = nil |
|
|
} |
|
|
} |
|
|
cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping
|
|
|
cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping
|
|
|
|
|
|
cs.triggeredTimeoutPrecommit = false |
|
|
|
|
|
|
|
|
cs.eventBus.PublishEventNewRound(cs.RoundStateEvent()) |
|
|
cs.eventBus.PublishEventNewRound(cs.RoundStateEvent()) |
|
|
cs.metrics.Rounds.Set(float64(round)) |
|
|
cs.metrics.Rounds.Set(float64(round)) |
|
@ -782,7 +785,8 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) { |
|
|
waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) |
|
|
waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) |
|
|
if waitForTxs { |
|
|
if waitForTxs { |
|
|
if cs.config.CreateEmptyBlocksInterval > 0 { |
|
|
if cs.config.CreateEmptyBlocksInterval > 0 { |
|
|
cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round, cstypes.RoundStepNewRound) |
|
|
|
|
|
|
|
|
cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round, |
|
|
|
|
|
cstypes.RoundStepNewRound) |
|
|
} |
|
|
} |
|
|
go cs.proposalHeartbeat(height, round) |
|
|
go cs.proposalHeartbeat(height, round) |
|
|
} else { |
|
|
} else { |
|
@ -1013,6 +1017,7 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) { |
|
|
|
|
|
|
|
|
func (cs *ConsensusState) defaultDoPrevote(height int64, round int) { |
|
|
func (cs *ConsensusState) defaultDoPrevote(height int64, round int) { |
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
|
// If a block is locked, prevote that.
|
|
|
// If a block is locked, prevote that.
|
|
|
if cs.LockedBlock != nil { |
|
|
if cs.LockedBlock != nil { |
|
|
logger.Info("enterPrevote: Block was locked") |
|
|
logger.Info("enterPrevote: Block was locked") |
|
@ -1171,8 +1176,12 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) { |
|
|
func (cs *ConsensusState) enterPrecommitWait(height int64, round int) { |
|
|
func (cs *ConsensusState) enterPrecommitWait(height int64, round int) { |
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommitWait <= cs.Step) { |
|
|
|
|
|
logger.Debug(fmt.Sprintf("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) |
|
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.triggeredTimeoutPrecommit) { |
|
|
|
|
|
logger.Debug( |
|
|
|
|
|
fmt.Sprintf( |
|
|
|
|
|
"enterPrecommitWait(%v/%v): Invalid args. "+ |
|
|
|
|
|
"Current state is Height/Round: %v/%v/, triggeredTimeoutPrecommit:%v", |
|
|
|
|
|
height, round, cs.Height, cs.Round, cs.triggeredTimeoutPrecommit)) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
if !cs.Votes.Precommits(round).HasTwoThirdsAny() { |
|
|
if !cs.Votes.Precommits(round).HasTwoThirdsAny() { |
|
@ -1182,7 +1191,7 @@ func (cs *ConsensusState) enterPrecommitWait(height int64, round int) { |
|
|
|
|
|
|
|
|
defer func() { |
|
|
defer func() { |
|
|
// Done enterPrecommitWait:
|
|
|
// Done enterPrecommitWait:
|
|
|
cs.updateRoundStep(round, cstypes.RoundStepPrecommitWait) |
|
|
|
|
|
|
|
|
cs.triggeredTimeoutPrecommit = true |
|
|
cs.newStep() |
|
|
cs.newStep() |
|
|
}() |
|
|
}() |
|
|
|
|
|
|
|
@ -1495,6 +1504,9 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p |
|
|
if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() { |
|
|
if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() { |
|
|
// Move onto the next step
|
|
|
// Move onto the next step
|
|
|
cs.enterPrevote(height, cs.Round) |
|
|
cs.enterPrevote(height, cs.Round) |
|
|
|
|
|
if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added
|
|
|
|
|
|
cs.enterPrecommit(height, cs.Round) |
|
|
|
|
|
} |
|
|
} else if cs.Step == cstypes.RoundStepCommit { |
|
|
} else if cs.Step == cstypes.RoundStepCommit { |
|
|
// If we're waiting on the proposal block...
|
|
|
// If we're waiting on the proposal block...
|
|
|
cs.tryFinalizeCommit(height) |
|
|
cs.tryFinalizeCommit(height) |
|
@ -1609,7 +1621,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, |
|
|
// Update Valid* if we can.
|
|
|
// Update Valid* if we can.
|
|
|
// NOTE: our proposal block may be nil or not what received a polka..
|
|
|
// NOTE: our proposal block may be nil or not what received a polka..
|
|
|
// TODO: we may want to still update the ValidBlock and obtain it via gossipping
|
|
|
// TODO: we may want to still update the ValidBlock and obtain it via gossipping
|
|
|
if !blockID.IsZero() && |
|
|
|
|
|
|
|
|
if len(blockID.Hash) != 0 && |
|
|
(cs.ValidRound < vote.Round) && |
|
|
(cs.ValidRound < vote.Round) && |
|
|
(vote.Round <= cs.Round) && |
|
|
(vote.Round <= cs.Round) && |
|
|
cs.ProposalBlock.HashesTo(blockID.Hash) { |
|
|
cs.ProposalBlock.HashesTo(blockID.Hash) { |
|
@ -1621,14 +1633,14 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// If +2/3 prevotes for *anything* for this or future round:
|
|
|
|
|
|
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() { |
|
|
|
|
|
// Round-skip over to PrevoteWait or goto Precommit.
|
|
|
|
|
|
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us
|
|
|
|
|
|
|
|
|
// If +2/3 prevotes for *anything* for future round:
|
|
|
|
|
|
if cs.Round < vote.Round && prevotes.HasTwoThirdsAny() { |
|
|
|
|
|
// Round-skip if there is any 2/3+ of votes ahead of us
|
|
|
|
|
|
cs.enterNewRound(height, vote.Round) |
|
|
|
|
|
} else if cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step { // current round
|
|
|
if prevotes.HasTwoThirdsMajority() { |
|
|
if prevotes.HasTwoThirdsMajority() { |
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
} else { |
|
|
|
|
|
cs.enterPrevote(height, vote.Round) // if the vote is ahead of us
|
|
|
|
|
|
|
|
|
} else if prevotes.HasTwoThirdsAny() { |
|
|
cs.enterPrevoteWait(height, vote.Round) |
|
|
cs.enterPrevoteWait(height, vote.Round) |
|
|
} |
|
|
} |
|
|
} else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round { |
|
|
} else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round { |
|
@ -1641,21 +1653,25 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, |
|
|
case types.VoteTypePrecommit: |
|
|
case types.VoteTypePrecommit: |
|
|
precommits := cs.Votes.Precommits(vote.Round) |
|
|
precommits := cs.Votes.Precommits(vote.Round) |
|
|
cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort()) |
|
|
cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort()) |
|
|
|
|
|
|
|
|
blockID, ok := precommits.TwoThirdsMajority() |
|
|
blockID, ok := precommits.TwoThirdsMajority() |
|
|
if ok && len(blockID.Hash) != 0 { |
|
|
|
|
|
|
|
|
if ok { |
|
|
// Executed as TwoThirdsMajority could be from a higher round
|
|
|
// Executed as TwoThirdsMajority could be from a higher round
|
|
|
cs.enterNewRound(height, vote.Round) |
|
|
cs.enterNewRound(height, vote.Round) |
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
cs.enterCommit(height, vote.Round) |
|
|
|
|
|
|
|
|
|
|
|
if cs.config.SkipTimeoutCommit && precommits.HasAll() { |
|
|
|
|
|
cs.enterNewRound(cs.Height, 0) |
|
|
|
|
|
|
|
|
if len(blockID.Hash) != 0 { |
|
|
|
|
|
cs.enterCommit(height, vote.Round) |
|
|
|
|
|
if cs.config.SkipTimeoutCommit && precommits.HasAll() { |
|
|
|
|
|
cs.enterNewRound(cs.Height, 0) |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
cs.enterPrecommitWait(height, vote.Round) |
|
|
} |
|
|
} |
|
|
} else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { |
|
|
} else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { |
|
|
cs.enterNewRound(height, vote.Round) |
|
|
cs.enterNewRound(height, vote.Round) |
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
|
|
|
cs.enterPrecommitWait(height, vote.Round) |
|
|
cs.enterPrecommitWait(height, vote.Round) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
default: |
|
|
default: |
|
|
panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
|
|
|
panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
|
|
|
} |
|
|
} |
|
|