github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/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/electroneum/electroneum-sc/common" 25 "github.com/electroneum/electroneum-sc/common/hexutil" 26 "github.com/electroneum/electroneum-sc/consensus/istanbul" 27 qbfttypes "github.com/electroneum/electroneum-sc/consensus/istanbul/types" 28 "github.com/electroneum/electroneum-sc/core/types" 29 "github.com/electroneum/electroneum-sc/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("IBFT: invalid past target round", "target", round) 52 return 53 } 54 55 // Check if we should round-change due to a bad block proposal 56 hasBadProposal := false 57 if c.current.preparedBlock != nil { 58 hasBadProposal = c.current.hasBadProposal(c.current.preparedBlock.Hash()) 59 } 60 61 roundChange := qbfttypes.NewRoundChange(c.current.Sequence(), round, c.current.preparedRound, c.current.preparedBlock, hasBadProposal) 62 63 // Sign message 64 encodedPayload, err := roundChange.EncodePayloadForSigning() 65 if err != nil { 66 withMsg(logger, roundChange).Error("IBFT: failed to encode ROUND-CHANGE message", "err", err) 67 return 68 } 69 signature, err := c.backend.Sign(encodedPayload) 70 if err != nil { 71 withMsg(logger, roundChange).Error("IBFT: failed to sign ROUND-CHANGE message", "err", err) 72 return 73 } 74 roundChange.SetSignature(signature) 75 76 // Extend ROUND-CHANGE message with PREPARE justification 77 if c.QBFTPreparedPrepares != nil { 78 roundChange.Justification = c.QBFTPreparedPrepares 79 withMsg(logger, roundChange).Debug("IBFT: extended ROUND-CHANGE message with PREPARE justification", "justification", roundChange.Justification) 80 } 81 82 // RLP-encode message 83 data, err := rlp.EncodeToBytes(roundChange) 84 if err != nil { 85 withMsg(logger, roundChange).Error("IBFT: failed to encode ROUND-CHANGE message", "err", err) 86 return 87 } 88 89 withMsg(logger, roundChange).Info("IBFT: broadcast ROUND-CHANGE message", "payload", hexutil.Encode(data)) 90 91 // Broadcast RLP-encoded message 92 if err = c.backend.Broadcast(c.valSet, roundChange.Code(), data); err != nil { 93 withMsg(logger, roundChange).Error("IBFT: failed to broadcast ROUND-CHANGE message", "err", err) 94 return 95 } 96 } 97 98 // handleRoundChange is called when receiving a ROUND-CHANGE message from another validator 99 // - accumulates ROUND-CHANGE messages until reaching quorum for a given round 100 // - when quorum of ROUND-CHANGE messages is reached then 101 func (c *core) handleRoundChange(roundChange *qbfttypes.RoundChange) error { 102 logger := c.currentLogger(true, roundChange) 103 104 view := roundChange.View() 105 currentRound := c.currentView().Round 106 107 // number of validators we received ROUND-CHANGE from for a round higher than the current one 108 num := c.roundChangeSet.higherRoundMessages(currentRound) 109 110 // number of validators we received ROUND-CHANGE from for the current round 111 currentRoundMessages := c.roundChangeSet.getRCMessagesForGivenRound(currentRound) 112 113 logger.Info("IBFT: handle ROUND-CHANGE message", "higherRoundChanges.count", num, "currentRoundChanges.count", currentRoundMessages) 114 115 // Add ROUND-CHANGE message to message set 116 if view.Round.Cmp(currentRound) >= 0 { 117 var prepareMessages []*qbfttypes.Prepare = nil 118 var pr *big.Int = nil 119 var pb *types.Block = nil 120 if roundChange.PreparedRound != nil && roundChange.PreparedBlock != nil && roundChange.Justification != nil && len(roundChange.Justification) > 0 { 121 prepareMessages = roundChange.Justification 122 pr = roundChange.PreparedRound 123 pb = roundChange.PreparedBlock 124 } 125 err := c.roundChangeSet.Add(view.Round, roundChange, pr, pb, prepareMessages, c.QuorumSize()) 126 if err != nil { 127 logger.Warn("IBFT: failed to add ROUND-CHANGE message", "err", err) 128 return err 129 } 130 } 131 132 // number of validators we received ROUND-CHANGE from for a round higher than the current one 133 num = c.roundChangeSet.higherRoundMessages(currentRound) 134 135 // number of validators we received ROUND-CHANGE from for the current round 136 currentRoundMessages = c.roundChangeSet.getRCMessagesForGivenRound(currentRound) 137 138 logger = logger.New("higherRoundChanges.count", num, "currentRoundChanges.count", currentRoundMessages) 139 140 if num == c.valSet.F()+1 { 141 // We received F+1 ROUND-CHANGE messages (this may happen before our timeout exprired) 142 // we start new round and broadcast ROUND-CHANGE message 143 newRound := c.roundChangeSet.getMinRoundChange(currentRound) 144 145 logger.Info("IBFT: received F+1 ROUND-CHANGE messages", "F", c.valSet.F()) 146 147 c.startNewRound(newRound) 148 c.broadcastRoundChange(newRound) 149 } else if currentRoundMessages >= c.QuorumSize() && c.IsProposer() && c.current.preprepareSent.Cmp(currentRound) < 0 { 150 logger.Info("IBFT: received quorum of ROUND-CHANGE messages") 151 152 // We received quorum of ROUND-CHANGE for current round and we are proposer 153 154 // If we have received a quorum of PREPARE message 155 // then we propose the same block proposal again if not we 156 // propose the block proposal that we generated 157 // 158 // If we have received a quorum of PREPARE messages with hadBlockProposal=false, 159 // propose the same block again. If hadBlockProposal=true, propose the block that we generated 160 _, proposal := c.highestPrepared(currentRound) 161 if proposal == nil || c.backend.HasBadProposal(proposal.Hash()) { 162 proposal = c.current.pendingRequest.Proposal 163 } 164 165 // Prepare justification for ROUND-CHANGE messages 166 roundChangeMessages := c.roundChangeSet.roundChanges[currentRound.Uint64()] 167 rcSignedPayloads := make([]*qbfttypes.SignedRoundChangePayload, 0) 168 for _, m := range roundChangeMessages.Values() { 169 rcMsg := m.(*qbfttypes.RoundChange) 170 rcSignedPayloads = append(rcSignedPayloads, &rcMsg.SignedRoundChangePayload) 171 } 172 173 prepareMessages := c.roundChangeSet.prepareMessages[currentRound.Uint64()] 174 if err := isJustified(proposal, rcSignedPayloads, prepareMessages, c.QuorumSize()); err != nil { 175 logger.Error("IBFT: invalid ROUND-CHANGE message justification", "err", err) 176 return nil 177 } 178 179 r := &Request{ 180 Proposal: proposal, 181 RCMessages: roundChangeMessages, 182 PrepareMessages: prepareMessages, 183 } 184 c.sendPreprepareMsg(r) 185 } else { 186 logger.Debug("IBFT: accepted ROUND-CHANGE messages") 187 } 188 return nil 189 } 190 191 // highestPrepared returns the highest Prepared Round and the corresponding Prepared Block 192 func (c *core) highestPrepared(round *big.Int) (*big.Int, istanbul.Proposal) { 193 return c.roundChangeSet.highestPreparedRound[round.Uint64()], c.roundChangeSet.highestPreparedBlock[round.Uint64()] 194 } 195 196 // ---------------------------------------------------------------------------- 197 198 func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet { 199 return &roundChangeSet{ 200 validatorSet: valSet, 201 roundChanges: make(map[uint64]*qbftMsgSet), 202 prepareMessages: make(map[uint64][]*qbfttypes.Prepare), 203 highestPreparedRound: make(map[uint64]*big.Int), 204 highestPreparedBlock: make(map[uint64]istanbul.Proposal), 205 mu: new(sync.Mutex), 206 } 207 } 208 209 type roundChangeSet struct { 210 validatorSet istanbul.ValidatorSet 211 roundChanges map[uint64]*qbftMsgSet 212 prepareMessages map[uint64][]*qbfttypes.Prepare 213 highestPreparedRound map[uint64]*big.Int 214 highestPreparedBlock map[uint64]istanbul.Proposal 215 mu *sync.Mutex 216 } 217 218 func (rcs *roundChangeSet) NewRound(r *big.Int) { 219 rcs.mu.Lock() 220 defer rcs.mu.Unlock() 221 round := r.Uint64() 222 if rcs.roundChanges[round] == nil { 223 rcs.roundChanges[round] = newQBFTMsgSet(rcs.validatorSet) 224 } 225 if rcs.prepareMessages[round] == nil { 226 rcs.prepareMessages[round] = make([]*qbfttypes.Prepare, 0) 227 } 228 } 229 230 // Add adds the round and message into round change set 231 func (rcs *roundChangeSet) Add(r *big.Int, msg qbfttypes.QBFTMessage, preparedRound *big.Int, preparedBlock istanbul.Proposal, prepareMessages []*qbfttypes.Prepare, quorumSize int) error { 232 rcs.mu.Lock() 233 defer rcs.mu.Unlock() 234 235 round := r.Uint64() 236 if rcs.roundChanges[round] == nil { 237 rcs.roundChanges[round] = newQBFTMsgSet(rcs.validatorSet) 238 } 239 if err := rcs.roundChanges[round].Add(msg); err != nil { 240 return err 241 } 242 243 if preparedRound != nil && (rcs.highestPreparedRound[round] == nil || preparedRound.Cmp(rcs.highestPreparedRound[round]) > 0) { 244 roundChange := msg.(*qbfttypes.RoundChange) 245 if hasMatchingRoundChangeAndPrepares(roundChange, prepareMessages, quorumSize, roundChange.HasBadProposal) == nil { 246 rcs.highestPreparedRound[round] = preparedRound 247 rcs.highestPreparedBlock[round] = preparedBlock 248 rcs.prepareMessages[round] = prepareMessages 249 } 250 } 251 252 return nil 253 } 254 255 // higherRoundMessages returns the count of validators we received a ROUND-CHANGE message from 256 // for any round greater than the given round 257 func (rcs *roundChangeSet) higherRoundMessages(round *big.Int) int { 258 rcs.mu.Lock() 259 defer rcs.mu.Unlock() 260 261 addresses := make(map[common.Address]struct{}) 262 for k, rms := range rcs.roundChanges { 263 if k > round.Uint64() { 264 for addr := range rms.messages { 265 addresses[addr] = struct{}{} 266 } 267 } 268 } 269 return len(addresses) 270 } 271 272 // getRCMessagesForGivenRound return the count ROUND-CHANGE messages 273 // received for a given round 274 func (rcs *roundChangeSet) getRCMessagesForGivenRound(round *big.Int) int { 275 rcs.mu.Lock() 276 defer rcs.mu.Unlock() 277 278 if rms := rcs.roundChanges[round.Uint64()]; rms != nil { 279 return len(rms.messages) 280 } 281 return 0 282 } 283 284 // getMinRoundChange returns the minimum round greater than the given round 285 func (rcs *roundChangeSet) getMinRoundChange(round *big.Int) *big.Int { 286 rcs.mu.Lock() 287 defer rcs.mu.Unlock() 288 289 var keys []int 290 for k := range rcs.roundChanges { 291 if k > round.Uint64() { 292 keys = append(keys, int(k)) 293 } 294 } 295 sort.Ints(keys) 296 if len(keys) == 0 { 297 return round 298 } 299 return big.NewInt(int64(keys[0])) 300 } 301 302 // ClearLowerThan deletes the messages for round earlier than the given round 303 func (rcs *roundChangeSet) ClearLowerThan(round *big.Int) { 304 rcs.mu.Lock() 305 defer rcs.mu.Unlock() 306 307 for k, rms := range rcs.roundChanges { 308 if len(rms.Values()) == 0 || k < round.Uint64() { 309 delete(rcs.roundChanges, k) 310 delete(rcs.highestPreparedRound, k) 311 delete(rcs.highestPreparedBlock, k) 312 delete(rcs.prepareMessages, k) 313 } 314 } 315 } 316 317 // MaxRound returns the max round which the number of messages is equal or larger than num 318 func (rcs *roundChangeSet) MaxRound(num int) *big.Int { 319 rcs.mu.Lock() 320 defer rcs.mu.Unlock() 321 322 var maxRound *big.Int 323 for k, rms := range rcs.roundChanges { 324 if rms.Size() < num { 325 continue 326 } 327 r := big.NewInt(int64(k)) 328 if maxRound == nil || maxRound.Cmp(r) < 0 { 329 maxRound = r 330 } 331 } 332 return maxRound 333 }