github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/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 "math/big" 21 "sort" 22 "sync" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/common/prque" 26 "github.com/ethereum/go-ethereum/consensus/istanbul" 27 "github.com/ethereum/go-ethereum/log" 28 ) 29 30 var ( 31 // msgPriority is defined for calculating processing priority to speedup consensus 32 // istanbul.MsgPreprepare > istanbul.MsgCommit > istanbul.MsgPrepare 33 msgPriority = map[uint64]int{ 34 istanbul.MsgPreprepare: 1, 35 istanbul.MsgCommit: 2, 36 istanbul.MsgPrepare: 3, 37 } 38 39 // Do not accept messages for views more than this many sequences in the future. 40 acceptMaxFutureSequence = big.NewInt(10) 41 acceptMaxFutureMsgsFromOneValidator = 1000 42 acceptMaxFutureMessages = 10 * 1000 43 acceptMaxFutureMessagesPruneBatch = 100 44 ) 45 46 // checkMessage checks the message state 47 // return errInvalidMessage if the message is invalid 48 // return errFutureMessage if the message view is larger than current view 49 // return errOldMessage if the message view is smaller than current view 50 func (c *core) checkMessage(msgCode uint64, msgView *istanbul.View) error { 51 if msgView == nil || msgView.Sequence == nil || msgView.Round == nil { 52 return errInvalidMessage 53 } 54 55 if msgView.Cmp(c.current.View()) < 0 { 56 return errOldMessage 57 } else if msgView.Sequence.Cmp(c.current.Sequence()) > 0 { 58 // sequence is bigger, definitely future message 59 return errFutureMessage 60 } else { 61 // same sequence && msgRound >= currentRound 62 63 // Accept all RoundChange (also future rounds) 64 // but check again desired round 65 if msgCode == istanbul.MsgRoundChange { 66 if msgView.Round.Cmp(c.current.DesiredRound()) < 0 { 67 return errOldMessage 68 } 69 return nil 70 } 71 72 // TODO we should check directly against the desired round 73 // there's no sense in accepting (or storing) messages on the range [currentRound, desiredRound] 74 75 if msgView.Round.Cmp(c.current.View().Round) > 0 || c.current.State() == StateWaitingForNewRound { 76 return errFutureMessage 77 } 78 79 // StateAcceptRequest only accepts istanbul.MsgPreprepare 80 // other messages are future messages 81 if c.current.State() == StateAcceptRequest && msgCode != istanbul.MsgPreprepare { 82 return errFutureMessage 83 } 84 85 // For states(StatePreprepared, StatePrepared, StateCommitted), 86 // can accept all message types if processing with same view 87 return nil 88 } 89 } 90 91 // MsgBacklog represent a backlog of future messages 92 // It works by: 93 // - allowing storing messages with "store()" 94 // - call eventListener when a backlog message becomes "present" 95 // - updates its notion of time/state with updateState() 96 type MsgBacklog interface { 97 // store atttemps to store the message in the backlog 98 // it might not do so, if the message is too far in the future 99 store(msg *istanbul.Message) 100 101 // updateState updates the notion of time/state of the backlog, 102 // as a side effect it will call the eventListener for all backlog 103 // messages that belong to the current "state" 104 updateState(view *istanbul.View, state State) 105 } 106 107 type msgBacklogImpl struct { 108 backlogBySeq map[uint64]*prque.Prque 109 msgCountBySrc map[common.Address]int 110 msgCount int 111 112 currentView *istanbul.View 113 currentState State 114 115 backlogsMu *sync.Mutex 116 msgProcessor func(*istanbul.Message) 117 checkMessage func(msgCode uint64, msgView *istanbul.View) error 118 logger log.Logger 119 } 120 121 func newMsgBacklog(msgProcessor func(*istanbul.Message), checkMessage func(msgCode uint64, msgView *istanbul.View) error) MsgBacklog { 122 initialView := &istanbul.View{ 123 Round: big.NewInt(0), 124 Sequence: big.NewInt(1), 125 } 126 127 return &msgBacklogImpl{ 128 backlogBySeq: make(map[uint64]*prque.Prque), 129 msgCountBySrc: make(map[common.Address]int), 130 msgCount: 0, 131 132 currentView: initialView, 133 currentState: StateAcceptRequest, 134 135 msgProcessor: msgProcessor, 136 checkMessage: checkMessage, 137 backlogsMu: new(sync.Mutex), 138 logger: log.New("type", "MsgBacklog"), 139 } 140 } 141 142 func (c *msgBacklogImpl) store(msg *istanbul.Message) { 143 logger := c.logger.New("func", "store", "from", msg.Address) 144 145 view, err := extractMessageView(msg) 146 147 if err != nil { 148 return 149 } 150 151 c.backlogsMu.Lock() 152 defer c.backlogsMu.Unlock() 153 154 // Never accept messages too far into the future 155 if view.Sequence.Cmp(new(big.Int).Add(c.currentView.Sequence, acceptMaxFutureSequence)) > 0 { 156 logger.Debug("Dropping message", "reason", "too far in the future", "m", msg) 157 return 158 } 159 160 if view.Round.Cmp(maxRoundForPriorityQueue) >= 0 { 161 logger.Debug("Dropping message", "reason", "round exceeds PQ bounds check", "m", msg) 162 return 163 } 164 165 // Check and inc per-validator future message limit 166 if c.msgCountBySrc[msg.Address] > acceptMaxFutureMsgsFromOneValidator { 167 logger.Debug("Dropping message", "reason", "exceeds per-address cap") 168 return 169 } 170 171 logger.Trace("Store future message", "m", msg) 172 c.msgCountBySrc[msg.Address]++ 173 c.msgCount++ 174 175 // Add message to per-seq list 176 backlogForSeq := c.backlogBySeq[view.Sequence.Uint64()] 177 if backlogForSeq == nil { 178 backlogForSeq = prque.New(nil) 179 c.backlogBySeq[view.Sequence.Uint64()] = backlogForSeq 180 } 181 182 backlogForSeq.Push(msg, toPriority(msg.Code, view)) 183 184 // After insert, remove messages if we have more than "acceptMaxFutureMessages" 185 c.removeMessagesOverflow() 186 } 187 188 // removeMessagesOverflow will remove messages if necessary to maintain the number of messages <= acceptMaxFutureMessages 189 // For that, it will remove messages that further on the future 190 func (c *msgBacklogImpl) removeMessagesOverflow() { 191 // Keep backlog below total max size by pruning future-most sequence first 192 // (we always leave one sequence's entire messages and rely on per-validator limits) 193 if c.msgCount > acceptMaxFutureMessages { 194 backlogSeqs := c.getSortedBacklogSeqs() 195 for i := len(backlogSeqs) - 1; i > 0; i-- { 196 seq := backlogSeqs[i] 197 if seq <= c.currentView.Sequence.Uint64() || 198 c.msgCount < (acceptMaxFutureMessages-acceptMaxFutureMessagesPruneBatch) { 199 break 200 } 201 c.clearBacklogForSeq(seq) 202 } 203 } 204 } 205 206 // Return slice of sequences present in backlog sorted in ascending order 207 // Call with backlogsMu held. 208 func (c *msgBacklogImpl) getSortedBacklogSeqs() []uint64 { 209 backlogSeqs := make([]uint64, len(c.backlogBySeq)) 210 i := 0 211 for k := range c.backlogBySeq { 212 backlogSeqs[i] = k 213 i++ 214 } 215 sort.Slice(backlogSeqs, func(i, j int) bool { 216 return backlogSeqs[i] < backlogSeqs[j] 217 }) 218 return backlogSeqs 219 } 220 221 // clearBacklogForSeq will remove all entries in the backlog 222 // for the given seq 223 func (c *msgBacklogImpl) clearBacklogForSeq(seq uint64) { 224 c.processBacklogForSeq(seq, func(_ *istanbul.Message) bool { return true }) 225 } 226 227 // processBacklogForSeq will call process() with each entry of the backlog 228 // for the given seq, until process return "false". 229 // The entry on which process() returned false will remain in the backlog 230 func (c *msgBacklogImpl) processBacklogForSeq(seq uint64, process func(*istanbul.Message) bool) { 231 backlogForSeq := c.backlogBySeq[seq] 232 if backlogForSeq == nil { 233 return 234 } 235 236 backlogSize := backlogForSeq.Size() 237 for i := 0; i < backlogSize; i++ { 238 m, priority := backlogForSeq.Pop() 239 msg := m.(*istanbul.Message) 240 241 shouldStop := process(msg) 242 243 if shouldStop { 244 backlogForSeq.Push(m, priority) 245 break 246 } 247 248 c.msgCountBySrc[msg.Address]-- 249 if c.msgCountBySrc[msg.Address] == 0 { 250 delete(c.msgCountBySrc, msg.Address) 251 } 252 c.msgCount-- 253 } 254 255 if backlogForSeq.Size() == 0 { 256 delete(c.backlogBySeq, seq) 257 } 258 } 259 260 func (c *msgBacklogImpl) updateState(view *istanbul.View, state State) { 261 c.backlogsMu.Lock() 262 defer c.backlogsMu.Unlock() 263 264 c.currentState = state 265 c.currentView = view 266 267 c.processBacklog() 268 } 269 270 func (c *msgBacklogImpl) processBacklog() { 271 272 logger := c.logger.New("func", "processBacklog", "cur_seq", c.currentView.Sequence, "cur_round", c.currentView.Round) 273 processedMsgsConsidered, processedMsgsEnqueued, processedMsgsFuture := 0, 0, 0 274 275 for _, seq := range c.getSortedBacklogSeqs() { 276 277 if seq < c.currentView.Sequence.Uint64() { 278 // Earlier sequence. Prune all messages. 279 c.clearBacklogForSeq(seq) 280 } else if seq == c.currentView.Sequence.Uint64() { 281 // Current sequence. Process all in order. 282 c.processBacklogForSeq(seq, func(msg *istanbul.Message) bool { 283 processedMsgsConsidered++ 284 285 view, err := extractMessageView(msg) 286 287 if err != nil { 288 logger.Warn("Error decoding msg", "err", err) 289 return false 290 } 291 if view == nil { 292 logger.Warn("Nil view") 293 return false 294 } 295 296 logger := logger.New("m", msg, "msg_view", view) 297 298 err = c.checkMessage(msg.Code, view) 299 300 if err == errFutureMessage { 301 logger.Debug("Future message in backlog for seq, pushing back to the backlog") 302 processedMsgsFuture++ 303 return true 304 } 305 306 if err == nil { 307 logger.Trace("Post backlog event") 308 processedMsgsEnqueued++ 309 go c.msgProcessor(msg) 310 } else { 311 logger.Trace("Skip the backlog event", "err", err) 312 } 313 return false 314 }) 315 } 316 } 317 318 if processedMsgsConsidered > 0 { 319 logger.Info("Processing istanbul backlog", "considered", processedMsgsConsidered, "future", processedMsgsFuture, "enqueued", processedMsgsEnqueued) 320 } 321 } 322 323 // A safe maximum for round that prevents overflow 324 var ( 325 maxRoundForPriorityQueue = big.NewInt(1 << (63 - 5)) 326 ) 327 328 func toPriority(msgCode uint64, view *istanbul.View) int64 { 329 if msgCode == istanbul.MsgRoundChange { 330 // msgRoundChange comes first 331 return 0 332 } 333 // 10 * Round limits the range possible message codes to [0, 9] 334 // Caller must check for integer overflow. 335 return -int64(view.Round.Uint64()*10 + uint64(msgPriority[msgCode])) 336 } 337 338 func extractMessageView(msg *istanbul.Message) (*istanbul.View, error) { 339 var v *istanbul.View 340 switch msg.Code { 341 case istanbul.MsgPreprepare: 342 var p *istanbul.Preprepare 343 err := msg.Decode(&p) 344 if err != nil { 345 return nil, err 346 } 347 v = p.View 348 case istanbul.MsgPrepare: 349 var p *istanbul.Subject 350 err := msg.Decode(&p) 351 if err != nil { 352 return nil, err 353 } 354 v = p.View 355 case istanbul.MsgCommit: 356 var cs *istanbul.CommittedSubject 357 err := msg.Decode(&cs) 358 if err != nil { 359 return nil, err 360 } 361 v = cs.Subject.View 362 case istanbul.MsgRoundChange: 363 var p *istanbul.RoundChange 364 err := msg.Decode(&p) 365 if err != nil { 366 return nil, err 367 } 368 v = p.View 369 } 370 return v, nil 371 }