diff --git a/consensus/reactor.go b/consensus/reactor.go index bd67c685..fcdcbbd9 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -696,31 +696,47 @@ func (conR *ConsensusReactor) gossipVotesForHeight(logger log.Logger, rs *cstype return true } } - - if prs.Step <= cstypes.RoundStepCommit && prs.Round != -1 && prs.Round <= rs.Round { - // If there are POLPrevotes to send... - if prs.ProposalPOLRound != -1 { - if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { - if ps.PickSendVote(polPrevotes) { - logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send", - "round", prs.ProposalPOLRound) - return true - } + // If there are POL prevotes to send... + if prs.Step <= cstypes.RoundStepPropose && prs.Round != -1 && prs.Round <= rs.Round && prs.ProposalPOLRound != -1 { + if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { + if ps.PickSendVote(polPrevotes) { + logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send", + "round", prs.ProposalPOLRound) + return true } } - - // If there are prevotes to send... + } + // If there are prevotes to send... + if prs.Step <= cstypes.RoundStepPrevoteWait && prs.Round != -1 && prs.Round <= rs.Round { if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) { logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round) return true } - - // If there are precommits to send... + } + // If there are precommits to send... + if prs.Step <= cstypes.RoundStepPrecommitWait && prs.Round != -1 && prs.Round <= rs.Round { if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) { logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round) return true } } + // If there are prevotes to send...Needed because of validBlock mechanism + if prs.Round != -1 && prs.Round <= rs.Round { + if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) { + logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round) + return true + } + } + // If there are POLPrevotes to send... + if prs.ProposalPOLRound != -1 { + if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { + if ps.PickSendVote(polPrevotes) { + logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send", + "round", prs.ProposalPOLRound) + return true + } + } + } return false } diff --git a/consensus/state.go b/consensus/state.go index d19814d2..e15965a7 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -992,7 +992,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) { blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority() - // If we don't have a polka, we must precommit nil + // If we don't have a polka, we must precommit nil. if !ok { if cs.LockedBlock != nil { cs.Logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit while we're locked. Precommitting nil") @@ -1003,10 +1003,10 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) { return } - // At this point +2/3 prevoted for a particular block or nil + // At this point +2/3 prevoted for a particular block or nil. cs.eventBus.PublishEventPolka(cs.RoundStateEvent()) - // the latest POLRound should be this round + // the latest POLRound should be this round. polRound, _ := cs.Votes.POLInfo() if polRound < round { cmn.PanicSanity(cmn.Fmt("This POLRound should be %v but got %", round, polRound)) @@ -1307,19 +1307,20 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v // NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) - // Update ValidBlock + // Update Valid* if we can. prevotes := cs.Votes.Prevotes(cs.Round) - blockID, ok := prevotes.TwoThirdsMajority() - if ok && !blockID.IsZero() && (cs.ValidRound < cs.Round) { - // update valid value + blockID, hasTwoThirds := prevotes.TwoThirdsMajority() + if hasTwoThirds && !blockID.IsZero() && (cs.ValidRound < cs.Round) { if cs.ProposalBlock.HashesTo(blockID.Hash) { cs.ValidRound = cs.Round cs.ValidBlock = cs.ProposalBlock cs.ValidBlockParts = cs.ProposalBlockParts } - //TODO: In case there is +2/3 majority in Prevotes set for some block and cs.ProposalBlock contains different block, - //either proposer is faulty or voting power of faulty processes is more than 1/3. We should - //trigger in the future accountability procedure at this point. + // TODO: In case there is +2/3 majority in Prevotes set for some + // block and cs.ProposalBlock contains different block, either + // proposer is faulty or voting power of faulty processes is more + // than 1/3. We should trigger in the future accountability + // procedure at this point. } if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() { @@ -1412,33 +1413,43 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, case types.VoteTypePrevote: prevotes := cs.Votes.Prevotes(vote.Round) cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort()) - blockID, ok := prevotes.TwoThirdsMajority() - // First, unlock if prevotes is a valid POL. - // >> lockRound < POLRound <= unlockOrChangeLockRound (see spec) - // NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound), - // we'll still enterNewRound(H,vote.R) and enterPrecommit(H,vote.R) to process it - // there. - if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) { - if ok && !cs.LockedBlock.HashesTo(blockID.Hash) { + + // If +2/3 prevotes for a block or nil for *any* round: + if blockID, ok := prevotes.TwoThirdsMajority(); ok { + + // First, unlock if prevotes is a valid POL. + // `lockRound < POLRound <= unlockOrChangeLockRound (see spec)` + // NOTE: If `lockRound < POLRound` but `!(POLRound <= + // unlockOrChangeLockRound)`, we'll still enterNewRound(H,vote.R) + // and enterPrecommit(H,vote.R) to process it there. + if (cs.LockedBlock != nil) && + (cs.LockedRound < vote.Round) && + (vote.Round <= cs.Round) && + !cs.LockedBlock.HashesTo(blockID.Hash) { + cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round) cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()) } - } - // Update ValidBlock - if ok && !blockID.IsZero() && (cs.ValidRound < vote.Round) && (vote.Round <= cs.Round) { - // update valid value - if cs.ProposalBlock.HashesTo(blockID.Hash) { + + // Update Valid* if we can. + if !blockID.IsZero() && + (cs.ValidRound < vote.Round) && + (vote.Round <= cs.Round) && + cs.ProposalBlock.HashesTo(blockID.Hash) { + cs.ValidRound = vote.Round cs.ValidBlock = cs.ProposalBlock cs.ValidBlockParts = cs.ProposalBlockParts + // TODO: We might want to update ValidBlock also in case we + // don't have that block yet, and obtain the required block + // using gossiping } - //TODO: We might want to update ValidBlock also in case we don't have that block yet, - // and obtain the required block using gossiping } + // 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 @@ -1454,6 +1465,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, cs.enterPrevote(height, cs.Round) } } + case types.VoteTypePrecommit: precommits := cs.Votes.Precommits(vote.Round) cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort()) diff --git a/consensus/types/state.go b/consensus/types/state.go index 0c3626cc..0c4166a4 100644 --- a/consensus/types/state.go +++ b/consensus/types/state.go @@ -67,9 +67,9 @@ type RoundState struct { LockedRound int LockedBlock *types.Block LockedBlockParts *types.PartSet - ValidRound int - ValidBlock *types.Block - ValidBlockParts *types.PartSet + ValidRound int // Last known round with POL for non-nil valid block. + ValidBlock *types.Block // Last known block of POL mentioned above. + ValidBlockParts *types.PartSet // Last known block parts of POL metnioned above. Votes *HeightVoteSet CommitRound int // LastCommit *types.VoteSet // Last precommits at Height-1