github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/core/backlog.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 "github.com/electroneum/electroneum-sc/consensus/istanbul" 21 qbfttypes "github.com/electroneum/electroneum-sc/consensus/istanbul/types" 22 "gopkg.in/karalabe/cookiejar.v2/collections/prque" 23 ) 24 25 var ( 26 // msgPriority is defined for calculating processing priority to speedup consensus 27 // msgPreprepare > msgCommit > msgPrepare 28 msgPriority = map[uint64]int{ 29 qbfttypes.PreprepareCode: 1, 30 qbfttypes.CommitCode: 2, 31 qbfttypes.PrepareCode: 3, 32 } 33 ) 34 35 // checkMessage checks that a message matches our current QBFT state 36 // 37 // In particular it ensures that 38 // - message has the expected round 39 // - message has the expected sequence 40 // - message type is expected given our current state 41 42 // return errInvalidMessage if the message is invalid 43 // return errFutureMessage if the message view is larger than current view 44 // return errOldMessage if the message view is smaller than current view 45 func (c *core) checkMessage(msgCode uint64, view *istanbul.View) error { 46 if view == nil || view.Sequence == nil || view.Round == nil { 47 return errInvalidMessage 48 } 49 50 if msgCode == qbfttypes.RoundChangeCode { 51 // if ROUND-CHANGE message 52 // check that 53 // - sequence matches our current sequence 54 // - round is in the future 55 if view.Sequence.Cmp(c.currentView().Sequence) > 0 { 56 return errFutureMessage 57 } else if view.Cmp(c.currentView()) < 0 { 58 return errOldMessage 59 } 60 return nil 61 } 62 63 // If not ROUND-CHANGE 64 // check that round and sequence equals our current round and sequence 65 if view.Cmp(c.currentView()) > 0 { 66 return errFutureMessage 67 } 68 69 if view.Cmp(c.currentView()) < 0 { 70 return errOldMessage 71 } 72 73 switch c.state { 74 case StateAcceptRequest: 75 // StateAcceptRequest only accepts msgPreprepare and msgRoundChange 76 // other messages are future messages 77 if msgCode > qbfttypes.PreprepareCode { 78 return errFutureMessage 79 } 80 return nil 81 case StatePreprepared: 82 // StatePreprepared only accepts msgPrepare and msgRoundChange 83 // message less than msgPrepare are invalid and greater are future messages 84 if msgCode < qbfttypes.PrepareCode { 85 return errInvalidMessage 86 } else if msgCode > qbfttypes.PrepareCode { 87 return errFutureMessage 88 } 89 return nil 90 case StatePrepared: 91 // StatePrepared only accepts msgCommit and msgRoundChange 92 // other messages are invalid messages 93 if msgCode < qbfttypes.CommitCode { 94 return errInvalidMessage 95 } 96 return nil 97 case StateCommitted: 98 // StateCommit rejects all messages other than msgRoundChange 99 return errInvalidMessage 100 } 101 return nil 102 } 103 104 // addToBacklog allows to postpone the processing of future messages 105 106 // it adds the message to backlog which is read on every state change 107 func (c *core) addToBacklog(msg qbfttypes.QBFTMessage) { 108 logger := c.currentLogger(true, msg) 109 110 src := msg.Source() 111 if src == c.Address() { 112 logger.Warn("IBFT: backlog from self") 113 return 114 } 115 116 logger.Trace("IBFT: new backlog message", "backlogs_size", len(c.backlogs)) 117 118 c.backlogsMu.Lock() 119 defer c.backlogsMu.Unlock() 120 121 backlog := c.backlogs[src] 122 if backlog == nil { 123 backlog = prque.New() 124 c.backlogs[src] = backlog 125 } 126 view := msg.View() 127 backlog.Push(msg, toPriority(msg.Code(), &view)) 128 } 129 130 // processBacklog lookup for future messages that have been backlogged and post it on 131 // the event channel so main handler loop can handle it 132 133 // It is called on every state change 134 func (c *core) processBacklog() { 135 c.backlogsMu.Lock() 136 defer c.backlogsMu.Unlock() 137 138 for srcAddress, backlog := range c.backlogs { 139 if backlog == nil { 140 continue 141 } 142 _, src := c.valSet.GetByAddress(srcAddress) 143 if src == nil { 144 // validator is not available 145 delete(c.backlogs, srcAddress) 146 continue 147 } 148 logger := c.logger.New("from", src, "state", c.state) 149 isFuture := false 150 151 logger.Trace("IBFT: process backlog") 152 153 // We stop processing if 154 // 1. backlog is empty 155 // 2. The first message in queue is a future message 156 for !(backlog.Empty() || isFuture) { 157 m, prio := backlog.Pop() 158 159 var code uint64 160 var view istanbul.View 161 var event backlogEvent 162 163 msg := m.(qbfttypes.QBFTMessage) 164 code = msg.Code() 165 view = msg.View() 166 event.msg = msg 167 168 // Push back if it's a future message 169 err := c.checkMessage(code, &view) 170 if err != nil { 171 if err == errFutureMessage { 172 // this is still a future message 173 logger.Trace("IBFT: stop processing backlog", "msg", m) 174 backlog.Push(m, prio) 175 isFuture = true 176 break 177 } 178 logger.Trace("IBFT: skip backlog message", "msg", m, "err", err) 179 continue 180 } 181 logger.Trace("IBFT: post backlog event", "msg", m) 182 183 event.src = src 184 go c.sendEvent(event) 185 } 186 } 187 } 188 189 func toPriority(msgCode uint64, view *istanbul.View) float32 { 190 if msgCode == qbfttypes.RoundChangeCode { 191 // For msgRoundChange, set the message priority based on its sequence 192 return -float32(view.Sequence.Uint64() * 1000) 193 } 194 // FIXME: round will be reset as 0 while new sequence 195 // 10 * Round limits the range of message code is from 0 to 9 196 // 1000 * Sequence limits the range of round is from 0 to 99 197 return -float32(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode])) 198 }