github.com/ConsenSys/Quorum@v20.10.0+incompatible/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 "sync" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/consensus/istanbul" 25 ) 26 27 // sendNextRoundChange sends the ROUND CHANGE message with current round + 1 28 func (c *core) sendNextRoundChange() { 29 cv := c.currentView() 30 c.sendRoundChange(new(big.Int).Add(cv.Round, common.Big1)) 31 } 32 33 // sendRoundChange sends the ROUND CHANGE message with the given round 34 func (c *core) sendRoundChange(round *big.Int) { 35 logger := c.logger.New("state", c.state) 36 37 cv := c.currentView() 38 if cv.Round.Cmp(round) >= 0 { 39 logger.Error("Cannot send out the round change", "current round", cv.Round, "target round", round) 40 return 41 } 42 43 c.catchUpRound(&istanbul.View{ 44 // The round number we'd like to transfer to. 45 Round: new(big.Int).Set(round), 46 Sequence: new(big.Int).Set(cv.Sequence), 47 }) 48 49 // Now we have the new round number and sequence number 50 cv = c.currentView() 51 rc := &istanbul.Subject{ 52 View: cv, 53 Digest: common.Hash{}, 54 } 55 56 payload, err := Encode(rc) 57 if err != nil { 58 logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err) 59 return 60 } 61 62 c.broadcast(&message{ 63 Code: msgRoundChange, 64 Msg: payload, 65 }) 66 } 67 68 func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error { 69 logger := c.logger.New("state", c.state, "from", src.Address().Hex()) 70 71 // Decode ROUND CHANGE message 72 var rc *istanbul.Subject 73 if err := msg.Decode(&rc); err != nil { 74 logger.Error("Failed to decode ROUND CHANGE", "err", err) 75 return errInvalidMessage 76 } 77 78 if err := c.checkMessage(msgRoundChange, rc.View); err != nil { 79 return err 80 } 81 82 cv := c.currentView() 83 roundView := rc.View 84 85 // Add the ROUND CHANGE message to its message set and return how many 86 // messages we've got with the same round number and sequence number. 87 num, err := c.roundChangeSet.Add(roundView.Round, msg) 88 if err != nil { 89 logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err) 90 return err 91 } 92 93 // Once we received f+1 ROUND CHANGE messages, those messages form a weak certificate. 94 // If our round number is smaller than the certificate's round number, we would 95 // try to catch up the round number. 96 if c.waitingForRoundChange && num == c.valSet.F()+1 { 97 if cv.Round.Cmp(roundView.Round) < 0 { 98 c.sendRoundChange(roundView.Round) 99 } 100 return nil 101 } else if num == c.QuorumSize() && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) { 102 // We've received 2f+1/Ceil(2N/3) ROUND CHANGE messages, start a new round immediately. 103 c.startNewRound(roundView.Round) 104 return nil 105 } else if cv.Round.Cmp(roundView.Round) < 0 { 106 // Only gossip the message with current round to other validators. 107 return errIgnored 108 } 109 return nil 110 } 111 112 // ---------------------------------------------------------------------------- 113 114 func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet { 115 return &roundChangeSet{ 116 validatorSet: valSet, 117 roundChanges: make(map[uint64]*messageSet), 118 mu: new(sync.Mutex), 119 } 120 } 121 122 type roundChangeSet struct { 123 validatorSet istanbul.ValidatorSet 124 roundChanges map[uint64]*messageSet 125 mu *sync.Mutex 126 } 127 128 // Add adds the round and message into round change set 129 func (rcs *roundChangeSet) Add(r *big.Int, msg *message) (int, error) { 130 rcs.mu.Lock() 131 defer rcs.mu.Unlock() 132 133 round := r.Uint64() 134 if rcs.roundChanges[round] == nil { 135 rcs.roundChanges[round] = newMessageSet(rcs.validatorSet) 136 } 137 err := rcs.roundChanges[round].Add(msg) 138 if err != nil { 139 return 0, err 140 } 141 return rcs.roundChanges[round].Size(), nil 142 } 143 144 // Clear deletes the messages with smaller round 145 func (rcs *roundChangeSet) Clear(round *big.Int) { 146 rcs.mu.Lock() 147 defer rcs.mu.Unlock() 148 149 for k, rms := range rcs.roundChanges { 150 if len(rms.Values()) == 0 || k < round.Uint64() { 151 delete(rcs.roundChanges, k) 152 } 153 } 154 } 155 156 // MaxRound returns the max round which the number of messages is equal or larger than num 157 func (rcs *roundChangeSet) MaxRound(num int) *big.Int { 158 rcs.mu.Lock() 159 defer rcs.mu.Unlock() 160 161 var maxRound *big.Int 162 for k, rms := range rcs.roundChanges { 163 if rms.Size() < num { 164 continue 165 } 166 r := big.NewInt(int64(k)) 167 if maxRound == nil || maxRound.Cmp(r) < 0 { 168 maxRound = r 169 } 170 } 171 return maxRound 172 }