github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/core/roundchange.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from quorum/consensus/istanbul/core/roundchange.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package core 22 23 import ( 24 "math/big" 25 "sync" 26 27 "github.com/klaytn/klaytn/common" 28 "github.com/klaytn/klaytn/consensus/istanbul" 29 ) 30 31 // sendNextRoundChange sends the ROUND CHANGE message with current round + 1 32 func (c *core) sendNextRoundChange(loc string) { 33 if c.backend.NodeType() != common.CONSENSUSNODE { 34 return 35 } 36 logger.Warn("[RC] sendNextRoundChange happened", "where", loc) 37 c.sendRoundChange(new(big.Int).Add(c.currentView().Round, common.Big1)) 38 } 39 40 // sendRoundChange sends the ROUND CHANGE message with the given round 41 func (c *core) sendRoundChange(round *big.Int) { 42 logger := c.logger.NewWith("state", c.state) 43 44 cv := c.currentView() 45 if cv.Round.Cmp(round) >= 0 { 46 logger.Warn("[RC] Skip sending out the round change message", "current round", cv.Round, 47 "target round", round) 48 return 49 } 50 51 logger.Warn("[RC] Prepare messages received before catchUpRound", 52 "len(prepares)", c.current.Prepares.Size(), "messages", c.current.Prepares.GetMessages()) 53 logger.Warn("[RC] Commit messages received before catchUpRound", 54 "len(commits)", c.current.Commits.Size(), "messages", c.current.Commits.GetMessages()) 55 56 c.catchUpRound(&istanbul.View{ 57 // The round number we'd like to transfer to. 58 Round: new(big.Int).Set(round), 59 Sequence: new(big.Int).Set(cv.Sequence), 60 }) 61 62 lastProposal, _ := c.backend.LastProposal() 63 64 // Now we have the new round number and sequence number 65 cv = c.currentView() 66 rc := &istanbul.Subject{ 67 View: cv, 68 Digest: common.Hash{}, 69 PrevHash: lastProposal.Hash(), 70 } 71 72 payload, err := Encode(rc) 73 if err != nil { 74 logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err) 75 return 76 } 77 78 c.broadcast(&message{ 79 Hash: rc.PrevHash, 80 Code: msgRoundChange, 81 Msg: payload, 82 }) 83 } 84 85 func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error { 86 logger := c.logger.NewWith("state", c.state, "from", src.Address().Hex()) 87 88 // Decode ROUND CHANGE message 89 var rc *istanbul.Subject 90 if err := msg.Decode(&rc); err != nil { 91 logger.Error("Failed to decode message", "code", msg.Code, "err", err) 92 return errInvalidMessage 93 } 94 95 // TODO-Klaytn-Istanbul: establish round change messaging policy and then apply it 96 //if !c.valSet.CheckInSubList(msg.Hash, rc.View, src.Address()) { 97 // return errNotFromCommittee 98 //} 99 100 if err := c.checkMessage(msgRoundChange, rc.View); err != nil { 101 return err 102 } 103 104 cv := c.currentView() 105 roundView := rc.View 106 107 // Add the ROUND CHANGE message to its message set and return how many 108 // messages we've got with the same round number and sequence number. 109 num, err := c.roundChangeSet.Add(roundView.Round, msg) 110 if err != nil { 111 logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err) 112 return err 113 } 114 115 var numCatchUp, numStartNewRound int 116 n := RequiredMessageCount(c.valSet) 117 f := int(c.valSet.F()) 118 // N ROUND CHANGE messages can start new round. 119 numStartNewRound = n 120 // F + 1 ROUND CHANGE messages can start catch up the round. 121 numCatchUp = f + 1 122 123 if num == numStartNewRound && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) { 124 // We've received enough ROUND CHANGE messages, start a new round immediately. 125 logger.Warn("[RC] Prepare messages received before startNewRound", "round", cv.Round.String(), 126 "len(prepares)", c.current.Prepares.Size(), "messages", c.current.Prepares.GetMessages()) 127 logger.Warn("[RC] Commit messages received before startNewRound", "round", cv.Round.String(), 128 "len(commits)", c.current.Commits.Size(), "messages", c.current.Commits.GetMessages()) 129 logger.Warn("[RC] Received 2f+1 Round Change Messages. Starting new round", 130 "currentRound", cv.Round.String(), "newRound", roundView.Round.String()) 131 c.startNewRound(roundView.Round) 132 return nil 133 } else if c.waitingForRoundChange && num == numCatchUp { 134 // Once we received enough ROUND CHANGE messages, those messages form a weak certificate. 135 // If our round number is smaller than the certificate's round number, we would 136 // try to catch up the round number. 137 if cv.Round.Cmp(roundView.Round) < 0 { 138 logger.Warn("[RC] Send round change because we have f+1 round change messages", 139 "currentRound", cv.Round.String(), "newRound", roundView.Round.String()) 140 c.sendRoundChange(roundView.Round) 141 } 142 return nil 143 } else if cv.Round.Cmp(roundView.Round) < 0 { 144 // Only gossip the message with current round to other validators. 145 logger.Warn("[RC] Received round is bigger but not enough number of messages. Message ignored", 146 "currentRound", cv.Round.String(), "newRound", roundView.Round.String(), "numRC", num) 147 return errIgnored 148 } 149 return nil 150 } 151 152 // ---------------------------------------------------------------------------- 153 154 func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet { 155 return &roundChangeSet{ 156 validatorSet: valSet, 157 roundChanges: make(map[uint64]*messageSet), 158 mu: new(sync.Mutex), 159 } 160 } 161 162 type roundChangeSet struct { 163 validatorSet istanbul.ValidatorSet 164 roundChanges map[uint64]*messageSet 165 mu *sync.Mutex 166 } 167 168 // Add adds the round and message into round change set 169 func (rcs *roundChangeSet) Add(r *big.Int, msg *message) (int, error) { 170 rcs.mu.Lock() 171 defer rcs.mu.Unlock() 172 173 round := r.Uint64() 174 if rcs.roundChanges[round] == nil { 175 rcs.roundChanges[round] = newMessageSet(rcs.validatorSet) 176 } 177 err := rcs.roundChanges[round].Add(msg) 178 if err != nil { 179 return 0, err 180 } 181 return rcs.roundChanges[round].Size(), nil 182 } 183 184 // Clear deletes the messages with smaller round 185 func (rcs *roundChangeSet) Clear(round *big.Int) { 186 rcs.mu.Lock() 187 defer rcs.mu.Unlock() 188 189 for k, rms := range rcs.roundChanges { 190 if len(rms.Values()) == 0 || k < round.Uint64() { 191 delete(rcs.roundChanges, k) 192 } 193 } 194 } 195 196 // MaxRound returns the max round which the number of messages is equal or larger than num 197 func (rcs *roundChangeSet) MaxRound(num int) *big.Int { 198 rcs.mu.Lock() 199 defer rcs.mu.Unlock() 200 201 var maxRound *big.Int 202 for k, rms := range rcs.roundChanges { 203 if rms.Size() < num { 204 continue 205 } 206 r := big.NewInt(int64(k)) 207 if maxRound == nil || maxRound.Cmp(r) < 0 { 208 maxRound = r 209 } 210 } 211 return maxRound 212 }