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