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