github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/qbft/core/roundchange.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "math/big" 21 "sort" 22 "sync" 23 24 "github.com/kisexp/xdchain/common" 25 "github.com/kisexp/xdchain/common/hexutil" 26 "github.com/kisexp/xdchain/consensus/istanbul" 27 qbfttypes "github.com/kisexp/xdchain/consensus/istanbul/qbft/types" 28 "github.com/kisexp/xdchain/core/types" 29 "github.com/kisexp/xdchain/rlp" 30 ) 31 32 // broadcastNextRoundChange sends the ROUND CHANGE message with current round + 1 33 func (c *core) broadcastNextRoundChange() { 34 cv := c.currentView() 35 c.broadcastRoundChange(new(big.Int).Add(cv.Round, common.Big1)) 36 } 37 38 // broadcastRoundChange is called when either 39 // - ROUND-CHANGE timeout expires (meaning either we have not received PRE-PREPARE message or we have not received a quorum of COMMIT messages) 40 // - 41 42 // It 43 // - Creates and sign ROUND-CHANGE message 44 // - broadcast the ROUND-CHANGE message with the given round 45 func (c *core) broadcastRoundChange(round *big.Int) { 46 logger := c.currentLogger(true, nil) 47 48 // Validates new round corresponds to current view 49 cv := c.currentView() 50 if cv.Round.Cmp(round) > 0 { 51 logger.Error("QBFT: invalid past target round", "target", round) 52 return 53 } 54 55 roundChange := qbfttypes.NewRoundChange(c.current.Sequence(), round, c.current.preparedRound, c.current.preparedBlock) 56 57 // Sign message 58 encodedPayload, err := roundChange.EncodePayloadForSigning() 59 if err != nil { 60 withMsg(logger, roundChange).Error("QBFT: failed to encode ROUND-CHANGE message", "err", err) 61 return 62 } 63 signature, err := c.backend.Sign(encodedPayload) 64 if err != nil { 65 withMsg(logger, roundChange).Error("QBFT: failed to sign ROUND-CHANGE message", "err", err) 66 return 67 } 68 roundChange.SetSignature(signature) 69 70 // Extend ROUND-CHANGE message with PREPARE justification 71 if c.QBFTPreparedPrepares != nil { 72 roundChange.Justification = c.QBFTPreparedPrepares 73 withMsg(logger, roundChange).Debug("QBFT: extended ROUND-CHANGE message with PREPARE justification", "justification", roundChange.Justification) 74 } 75 76 // RLP-encode message 77 data, err := rlp.EncodeToBytes(roundChange) 78 if err != nil { 79 withMsg(logger, roundChange).Error("QBFT: failed to encode ROUND-CHANGE message", "err", err) 80 return 81 } 82 83 withMsg(logger, roundChange).Info("QBFT: broadcast ROUND-CHANGE message", "payload", hexutil.Encode(data)) 84 85 // Broadcast RLP-encoded message 86 if err = c.backend.Broadcast(c.valSet, roundChange.Code(), data); err != nil { 87 withMsg(logger, roundChange).Error("QBFT: failed to broadcast ROUND-CHANGE message", "err", err) 88 return 89 } 90 } 91 92 // handleRoundChange is called when receiving a ROUND-CHANGE message from another validator 93 // - accumulates ROUND-CHANGE messages until reaching quorum for a given round 94 // - when quorum of ROUND-CHANGE messages is reached then 95 func (c *core) handleRoundChange(roundChange *qbfttypes.RoundChange) error { 96 logger := c.currentLogger(true, roundChange) 97 98 view := roundChange.View() 99 currentRound := c.currentView().Round 100 101 // number of validators we received ROUND-CHANGE from for a round higher than the current one 102 num := c.roundChangeSet.higherRoundMessages(currentRound) 103 104 // number of validators we received ROUND-CHANGE from for the current round 105 currentRoundMessages := c.roundChangeSet.getRCMessagesForGivenRound(currentRound) 106 107 logger.Info("QBFT: handle ROUND-CHANGE message", "higherRoundChanges.count", num, "currentRoundChanges.count", currentRoundMessages) 108 109 // Add ROUND-CHANGE message to message set 110 if view.Round.Cmp(currentRound) >= 0 { 111 var prepareMessages []*qbfttypes.Prepare = nil 112 var pr *big.Int = nil 113 var pb *types.Block = nil 114 if roundChange.PreparedRound != nil && roundChange.PreparedBlock != nil && roundChange.Justification != nil && len(roundChange.Justification) > 0 { 115 prepareMessages = roundChange.Justification 116 pr = roundChange.PreparedRound 117 pb = roundChange.PreparedBlock 118 } 119 err := c.roundChangeSet.Add(view.Round, roundChange, pr, pb, prepareMessages, c.QuorumSize()) 120 if err != nil { 121 logger.Warn("QBFT: failed to add ROUND-CHANGE message", "err", err) 122 return err 123 } 124 } 125 126 // number of validators we received ROUND-CHANGE from for a round higher than the current one 127 num = c.roundChangeSet.higherRoundMessages(currentRound) 128 129 // number of validators we received ROUND-CHANGE from for the current round 130 currentRoundMessages = c.roundChangeSet.getRCMessagesForGivenRound(currentRound) 131 132 logger = logger.New("higherRoundChanges.count", num, "currentRoundChanges.count", currentRoundMessages) 133 134 if num == c.valSet.F()+1 { 135 // We received F+1 ROUND-CHANGE messages (this may happen before our timeout exprired) 136 // we start new round and broadcast ROUND-CHANGE message 137 newRound := c.roundChangeSet.getMinRoundChange(currentRound) 138 139 logger.Info("QBFT: received F+1 ROUND-CHANGE messages", "F", c.valSet.F()) 140 141 c.startNewRound(newRound) 142 c.broadcastRoundChange(newRound) 143 } else if currentRoundMessages >= c.QuorumSize() && c.IsProposer() && c.current.preprepareSent.Cmp(currentRound) < 0 { 144 logger.Info("QBFT: received quorum of ROUND-CHANGE messages") 145 146 // We received quorum of ROUND-CHANGE for current round and we are proposer 147 148 // If we have received a quorum of PREPARE message 149 // then we propose the same block proposal again if not we 150 // propose the block proposal that we generated 151 _, proposal := c.highestPrepared(currentRound) 152 if proposal == nil { 153 proposal = c.current.pendingRequest.Proposal 154 } 155 156 // Prepare justification for ROUND-CHANGE messages 157 roundChangeMessages := c.roundChangeSet.roundChanges[currentRound.Uint64()] 158 rcSignedPayloads := make([]*qbfttypes.SignedRoundChangePayload, 0) 159 for _, m := range roundChangeMessages.Values() { 160 rcMsg := m.(*qbfttypes.RoundChange) 161 rcSignedPayloads = append(rcSignedPayloads, &rcMsg.SignedRoundChangePayload) 162 } 163 164 prepareMessages := c.roundChangeSet.prepareMessages[currentRound.Uint64()] 165 if err := isJustified(proposal, rcSignedPayloads, prepareMessages, c.QuorumSize()); err != nil { 166 logger.Error("QBFT: invalid ROUND-CHANGE message justification", "err", err) 167 return nil 168 } 169 170 r := &Request{ 171 Proposal: proposal, 172 RCMessages: roundChangeMessages, 173 PrepareMessages: prepareMessages, 174 } 175 c.sendPreprepareMsg(r) 176 } else { 177 logger.Debug("QBFT: accepted ROUND-CHANGE messages") 178 } 179 return nil 180 } 181 182 // highestPrepared returns the highest Prepared Round and the corresponding Prepared Block 183 func (c *core) highestPrepared(round *big.Int) (*big.Int, istanbul.Proposal) { 184 return c.roundChangeSet.highestPreparedRound[round.Uint64()], c.roundChangeSet.highestPreparedBlock[round.Uint64()] 185 } 186 187 // ---------------------------------------------------------------------------- 188 189 func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet { 190 return &roundChangeSet{ 191 validatorSet: valSet, 192 roundChanges: make(map[uint64]*qbftMsgSet), 193 prepareMessages: make(map[uint64][]*qbfttypes.Prepare), 194 highestPreparedRound: make(map[uint64]*big.Int), 195 highestPreparedBlock: make(map[uint64]istanbul.Proposal), 196 mu: new(sync.Mutex), 197 } 198 } 199 200 type roundChangeSet struct { 201 validatorSet istanbul.ValidatorSet 202 roundChanges map[uint64]*qbftMsgSet 203 prepareMessages map[uint64][]*qbfttypes.Prepare 204 highestPreparedRound map[uint64]*big.Int 205 highestPreparedBlock map[uint64]istanbul.Proposal 206 mu *sync.Mutex 207 } 208 209 func (rcs *roundChangeSet) NewRound(r *big.Int) { 210 rcs.mu.Lock() 211 defer rcs.mu.Unlock() 212 round := r.Uint64() 213 if rcs.roundChanges[round] == nil { 214 rcs.roundChanges[round] = newQBFTMsgSet(rcs.validatorSet) 215 } 216 if rcs.prepareMessages[round] == nil { 217 rcs.prepareMessages[round] = make([]*qbfttypes.Prepare, 0) 218 } 219 } 220 221 // Add adds the round and message into round change set 222 func (rcs *roundChangeSet) Add(r *big.Int, msg qbfttypes.QBFTMessage, preparedRound *big.Int, preparedBlock istanbul.Proposal, prepareMessages []*qbfttypes.Prepare, quorumSize int) error { 223 rcs.mu.Lock() 224 defer rcs.mu.Unlock() 225 226 round := r.Uint64() 227 if rcs.roundChanges[round] == nil { 228 rcs.roundChanges[round] = newQBFTMsgSet(rcs.validatorSet) 229 } 230 if err := rcs.roundChanges[round].Add(msg); err != nil { 231 return err 232 } 233 234 if preparedRound != nil && (rcs.highestPreparedRound[round] == nil || preparedRound.Cmp(rcs.highestPreparedRound[round]) > 0) { 235 roundChange := msg.(*qbfttypes.RoundChange) 236 if hasMatchingRoundChangeAndPrepares(roundChange, prepareMessages, quorumSize) == nil { 237 rcs.highestPreparedRound[round] = preparedRound 238 rcs.highestPreparedBlock[round] = preparedBlock 239 rcs.prepareMessages[round] = prepareMessages 240 } 241 } 242 243 return nil 244 } 245 246 // higherRoundMessages returns the count of validators we received a ROUND-CHANGE message from 247 // for any round greater than the given round 248 func (rcs *roundChangeSet) higherRoundMessages(round *big.Int) int { 249 rcs.mu.Lock() 250 defer rcs.mu.Unlock() 251 252 addresses := make(map[common.Address]struct{}) 253 for k, rms := range rcs.roundChanges { 254 if k > round.Uint64() { 255 for addr := range rms.messages { 256 addresses[addr] = struct{}{} 257 } 258 } 259 } 260 return len(addresses) 261 } 262 263 // getRCMessagesForGivenRound return the count ROUND-CHANGE messages 264 // received for a given round 265 func (rcs *roundChangeSet) getRCMessagesForGivenRound(round *big.Int) int { 266 rcs.mu.Lock() 267 defer rcs.mu.Unlock() 268 269 if rms := rcs.roundChanges[round.Uint64()]; rms != nil { 270 return len(rms.messages) 271 } 272 return 0 273 } 274 275 // getMinRoundChange returns the minimum round greater than the given round 276 func (rcs *roundChangeSet) getMinRoundChange(round *big.Int) *big.Int { 277 rcs.mu.Lock() 278 defer rcs.mu.Unlock() 279 280 var keys []int 281 for k := range rcs.roundChanges { 282 if k > round.Uint64() { 283 keys = append(keys, int(k)) 284 } 285 } 286 sort.Ints(keys) 287 if len(keys) == 0 { 288 return round 289 } 290 return big.NewInt(int64(keys[0])) 291 } 292 293 // ClearLowerThan deletes the messages for round earlier than the given round 294 func (rcs *roundChangeSet) ClearLowerThan(round *big.Int) { 295 rcs.mu.Lock() 296 defer rcs.mu.Unlock() 297 298 for k, rms := range rcs.roundChanges { 299 if len(rms.Values()) == 0 || k < round.Uint64() { 300 delete(rcs.roundChanges, k) 301 delete(rcs.highestPreparedRound, k) 302 delete(rcs.highestPreparedBlock, k) 303 delete(rcs.prepareMessages, k) 304 } 305 } 306 } 307 308 // MaxRound returns the max round which the number of messages is equal or larger than num 309 func (rcs *roundChangeSet) MaxRound(num int) *big.Int { 310 rcs.mu.Lock() 311 defer rcs.mu.Unlock() 312 313 var maxRound *big.Int 314 for k, rms := range rcs.roundChanges { 315 if rms.Size() < num { 316 continue 317 } 318 r := big.NewInt(int64(k)) 319 if maxRound == nil || maxRound.Cmp(r) < 0 { 320 maxRound = r 321 } 322 } 323 return maxRound 324 }