github.com/okex/exchain@v1.8.0/libs/tendermint/consensus/consensus_vote.go (about) 1 package consensus 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "fmt" 7 cstypes "github.com/okex/exchain/libs/tendermint/consensus/types" 8 "github.com/okex/exchain/libs/tendermint/p2p" 9 "github.com/okex/exchain/libs/tendermint/types" 10 tmtime "github.com/okex/exchain/libs/tendermint/types/time" 11 "time" 12 ) 13 14 // Attempt to add the vote. if its a duplicate signature, dupeout the validator 15 func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { 16 added, err := cs.addVote(vote, peerID) 17 if err != nil { 18 // If the vote height is off, we'll just ignore it, 19 // But if it's a conflicting sig, add it to the cs.evpool. 20 // If it's otherwise invalid, punish peer. 21 // nolint: gocritic 22 if err == ErrVoteHeightMismatch { 23 return added, err 24 } else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok { 25 if cs.privValidatorPubKey == nil { 26 return false, errPubKeyIsNotSet 27 } 28 29 if bytes.Equal(vote.ValidatorAddress, cs.privValidatorPubKey.Address()) { 30 cs.Logger.Error( 31 "Found conflicting vote from ourselves. Did you unsafe_reset a validator?", 32 "height", 33 vote.Height, 34 "round", 35 vote.Round, 36 "type", 37 vote.Type) 38 return added, err 39 } 40 if GetActiveVC() && vote.Round == 0 && vote.HasVC { 41 return added, err 42 } 43 cs.evpool.AddEvidence(voteErr.DuplicateVoteEvidence) 44 return added, err 45 } else if err == types.ErrVoteNonDeterministicSignature { 46 cs.Logger.Debug("Vote has non-deterministic signature", "err", err) 47 } else { 48 // Either 49 // 1) bad peer OR 50 // 2) not a bad peer? this can also err sometimes with "Unexpected step" OR 51 // 3) tmkms use with multiple validators connecting to a single tmkms instance 52 // (https://github.com/tendermint/tendermint/issues/3839). 53 cs.Logger.Info("Error attempting to add vote", "err", err) 54 return added, ErrAddingVote 55 } 56 } 57 return added, nil 58 } 59 60 //----------------------------------------------------------------------------- 61 62 func (cs *State) addVote( 63 vote *types.Vote, 64 peerID p2p.ID) (added bool, err error) { 65 cs.Logger.Debug( 66 "addVote", 67 "voteHeight", 68 vote.Height, 69 "voteType", 70 vote.Type, 71 "valIndex", 72 vote.ValidatorIndex, 73 "csHeight", 74 cs.Height, 75 ) 76 77 // A precommit for the previous height? 78 // These come in while we wait timeoutCommit 79 if vote.Height+1 == cs.Height { 80 if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.PrecommitType) { 81 // TODO: give the reason .. 82 // fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.") 83 return added, ErrVoteHeightMismatch 84 } 85 added, err = cs.LastCommit.AddVote(vote) 86 if !added { 87 return added, err 88 } 89 90 cs.Logger.Info(fmt.Sprintf("Added to lastPrecommits: %v", cs.LastCommit.StringShort())) 91 cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}) 92 cs.evsw.FireEvent(types.EventVote, vote) 93 94 // if we can skip timeoutCommit and have all the votes now, 95 if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { 96 // go straight to new round (skip timeout commit) 97 // cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight) 98 cs.enterNewRound(cs.Height, 0) 99 } 100 101 return 102 } 103 104 // Height mismatch is ignored. 105 // Not necessarily a bad peer, but not favourable behaviour. 106 if vote.Height != cs.Height { 107 err = ErrVoteHeightMismatch 108 cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID) 109 return 110 } 111 112 height := cs.Height 113 added, err = cs.Votes.AddVote(vote, peerID) 114 if !added { 115 // Either duplicate, or error upon cs.Votes.AddByIndex() 116 return 117 } 118 119 cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}) 120 cs.evsw.FireEvent(types.EventVote, vote) 121 122 switch vote.Type { 123 case types.PrevoteType: 124 prevotes := cs.Votes.Prevotes(vote.Round) 125 cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort()) 126 127 // If +2/3 prevotes for a block or nil for *any* round: 128 if blockID, ok := prevotes.TwoThirdsMajority(); ok { 129 130 // There was a polka! 131 // If we're locked but this is a recent polka, unlock. 132 // If it matches our ProposalBlock, update the ValidBlock 133 134 // Unlock if `cs.LockedRound < vote.Round <= cs.Round` 135 // NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round 136 if (cs.LockedBlock != nil) && 137 (cs.LockedRound < vote.Round) && 138 (vote.Round <= cs.Round) && 139 !cs.LockedBlock.HashesTo(blockID.Hash) { 140 141 cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round) 142 cs.LockedRound = -1 143 cs.LockedBlock = nil 144 cs.LockedBlockParts = nil 145 cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()) 146 } 147 148 // Update Valid* if we can. 149 // NOTE: our proposal block may be nil or not what received a polka.. 150 if len(blockID.Hash) != 0 && (cs.ValidRound < vote.Round) && (vote.Round == cs.Round) { 151 152 if cs.ProposalBlock.HashesTo(blockID.Hash) { 153 cs.Logger.Info( 154 "Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round) 155 cs.ValidRound = vote.Round 156 cs.ValidBlock = cs.ProposalBlock 157 cs.ValidBlockParts = cs.ProposalBlockParts 158 } else { 159 cs.Logger.Info( 160 "Valid block we don't know about. Set ProposalBlock=nil", 161 "proposal", cs.ProposalBlock.Hash(), "blockID", blockID.Hash) 162 // We're getting the wrong block. 163 cs.ProposalBlock = nil 164 } 165 if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) { 166 cs.Logger.Info("addVote proposalBlockPart reset ,because of mismatch hash,", 167 "origin", hex.EncodeToString(cs.ProposalBlockParts.Hash()), "after", blockID.Hash) 168 cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader) 169 } 170 cs.evsw.FireEvent(types.EventValidBlock, &cs.RoundState) 171 cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()) 172 } 173 } 174 175 // If +2/3 prevotes for *anything* for future round: 176 switch { 177 case cs.Round < vote.Round && prevotes.HasTwoThirdsAny(): 178 // Round-skip if there is any 2/3+ of votes ahead of us 179 cs.enterNewRound(height, vote.Round) 180 case cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step: // current round 181 blockID, ok := prevotes.TwoThirdsMajority() 182 if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) { 183 cs.enterPrecommit(height, vote.Round) 184 } else if prevotes.HasTwoThirdsAny() { 185 cs.enterPrevoteWait(height, vote.Round) 186 } 187 case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round: 188 // If the proposal is now complete, enter prevote of cs.Round. 189 if cs.isProposalComplete() { 190 cs.enterPrevote(height, cs.Round) 191 } 192 } 193 194 case types.PrecommitType: 195 precommits := cs.Votes.Precommits(vote.Round) 196 cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort()) 197 198 blockID, ok := precommits.TwoThirdsMajority() 199 if ok { 200 // Executed as TwoThirdsMajority could be from a higher round 201 cs.enterNewRound(height, vote.Round) 202 cs.enterPrecommit(height, vote.Round) 203 if len(blockID.Hash) != 0 { 204 cs.enterCommit(height, vote.Round) 205 if cs.config.SkipTimeoutCommit && precommits.HasAll() { 206 cs.enterNewRound(cs.Height, 0) 207 } 208 } else { 209 cs.enterPrecommitWait(height, vote.Round) 210 } 211 } else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { 212 cs.enterNewRound(height, vote.Round) 213 cs.enterPrecommitWait(height, vote.Round) 214 } 215 216 default: 217 panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-amino should prevent this. 218 } 219 220 return added, err 221 } 222 223 // CONTRACT: cs.privValidator is not nil. 224 func (cs *State) signVote( 225 msgType types.SignedMsgType, 226 hash []byte, 227 header types.PartSetHeader, 228 ) (*types.Vote, error) { 229 // Flush the WAL. Otherwise, we may not recompute the same vote to sign, 230 // and the privValidator will refuse to sign anything. 231 cs.wal.FlushAndSync() 232 233 if cs.privValidatorPubKey == nil { 234 return nil, errPubKeyIsNotSet 235 } 236 addr := cs.privValidatorPubKey.Address() 237 valIdx, _ := cs.Validators.GetByAddress(addr) 238 239 vote := &types.Vote{ 240 ValidatorAddress: addr, 241 ValidatorIndex: valIdx, 242 Height: cs.Height, 243 Round: cs.Round, 244 Timestamp: cs.voteTime(), 245 Type: msgType, 246 BlockID: types.BlockID{Hash: hash, PartsHeader: header}, 247 HasVC: cs.HasVC, 248 } 249 250 err := cs.privValidator.SignVote(cs.state.ChainID, vote) 251 return vote, err 252 } 253 254 func (cs *State) voteTime() time.Time { 255 now := tmtime.Now() 256 minVoteTime := now 257 // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, 258 // even if cs.LockedBlock != nil. See https://docs.tendermint.com/master/spec/. 259 timeIotaMs := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMs) * time.Millisecond 260 if cs.LockedBlock != nil { 261 // See the BFT time spec https://docs.tendermint.com/master/spec/consensus/bft-time.html 262 minVoteTime = cs.LockedBlock.Time.Add(timeIotaMs) 263 } else if cs.ProposalBlock != nil { 264 minVoteTime = cs.ProposalBlock.Time.Add(timeIotaMs) 265 } 266 267 if now.After(minVoteTime) { 268 return now 269 } 270 return minVoteTime 271 } 272 273 // sign the vote and publish on internalMsgQueue 274 func (cs *State) signAddVote(msgType types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { 275 if cs.privValidator == nil { // the node does not have a key 276 return nil 277 } 278 279 if cs.privValidatorPubKey == nil { 280 // Vote won't be signed, but it's not critical. 281 cs.Logger.Error(fmt.Sprintf("signAddVote: %v", errPubKeyIsNotSet)) 282 return nil 283 } 284 285 // If the node not in the validator set, do nothing. 286 if !cs.Validators.HasAddress(cs.privValidatorPubKey.Address()) { 287 return nil 288 } 289 290 // TODO: pass pubKey to signVote 291 vote, err := cs.signVote(msgType, hash, header) 292 if err == nil { 293 //broadcast vote immediately 294 cs.evsw.FireEvent(types.EventSignVote, vote) 295 cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) 296 cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) 297 return vote 298 } 299 if !cs.replayMode { 300 cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err) 301 } 302 return nil 303 }