github.com/ConsenSys/Quorum@v20.10.0+incompatible/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/ethereum/go-ethereum/consensus/istanbul" 21 "gopkg.in/karalabe/cookiejar.v2/collections/prque" 22 ) 23 24 var ( 25 // msgPriority is defined for calculating processing priority to speedup consensus 26 // msgPreprepare > msgCommit > msgPrepare 27 msgPriority = map[uint64]int{ 28 msgPreprepare: 1, 29 msgCommit: 2, 30 msgPrepare: 3, 31 } 32 ) 33 34 // checkMessage checks the message state 35 // return errInvalidMessage if the message is invalid 36 // return errFutureMessage if the message view is larger than current view 37 // return errOldMessage if the message view is smaller than current view 38 func (c *core) checkMessage(msgCode uint64, view *istanbul.View) error { 39 if view == nil || view.Sequence == nil || view.Round == nil { 40 return errInvalidMessage 41 } 42 43 if msgCode == msgRoundChange { 44 if view.Sequence.Cmp(c.currentView().Sequence) > 0 { 45 return errFutureMessage 46 } else if view.Cmp(c.currentView()) < 0 { 47 return errOldMessage 48 } 49 return nil 50 } 51 52 if view.Cmp(c.currentView()) > 0 { 53 return errFutureMessage 54 } 55 56 if view.Cmp(c.currentView()) < 0 { 57 return errOldMessage 58 } 59 60 if c.waitingForRoundChange { 61 return errFutureMessage 62 } 63 64 // StateAcceptRequest only accepts msgPreprepare 65 // other messages are future messages 66 if c.state == StateAcceptRequest { 67 if msgCode > msgPreprepare { 68 return errFutureMessage 69 } 70 return nil 71 } 72 73 // For states(StatePreprepared, StatePrepared, StateCommitted), 74 // can accept all message types if processing with same view 75 return nil 76 } 77 78 func (c *core) storeBacklog(msg *message, src istanbul.Validator) { 79 logger := c.logger.New("from", src, "state", c.state) 80 81 if src.Address() == c.Address() { 82 logger.Warn("Backlog from self") 83 return 84 } 85 86 logger.Trace("Store future message") 87 88 c.backlogsMu.Lock() 89 defer c.backlogsMu.Unlock() 90 91 logger.Debug("Retrieving backlog queue", "for", src.Address(), "backlogs_size", len(c.backlogs)) 92 backlog := c.backlogs[src.Address()] 93 if backlog == nil { 94 backlog = prque.New() 95 } 96 switch msg.Code { 97 case msgPreprepare: 98 var p *istanbul.Preprepare 99 err := msg.Decode(&p) 100 if err == nil { 101 backlog.Push(msg, toPriority(msg.Code, p.View)) 102 } 103 // for msgRoundChange, msgPrepare and msgCommit cases 104 default: 105 var p *istanbul.Subject 106 err := msg.Decode(&p) 107 if err == nil { 108 backlog.Push(msg, toPriority(msg.Code, p.View)) 109 } 110 } 111 c.backlogs[src.Address()] = backlog 112 } 113 114 func (c *core) processBacklog() { 115 c.backlogsMu.Lock() 116 defer c.backlogsMu.Unlock() 117 118 for srcAddress, backlog := range c.backlogs { 119 if backlog == nil { 120 continue 121 } 122 _, src := c.valSet.GetByAddress(srcAddress) 123 if src == nil { 124 // validator is not available 125 delete(c.backlogs, srcAddress) 126 continue 127 } 128 logger := c.logger.New("from", src, "state", c.state) 129 isFuture := false 130 131 // We stop processing if 132 // 1. backlog is empty 133 // 2. The first message in queue is a future message 134 for !(backlog.Empty() || isFuture) { 135 m, prio := backlog.Pop() 136 msg := m.(*message) 137 var view *istanbul.View 138 switch msg.Code { 139 case msgPreprepare: 140 var m *istanbul.Preprepare 141 err := msg.Decode(&m) 142 if err == nil { 143 view = m.View 144 } 145 // for msgRoundChange, msgPrepare and msgCommit cases 146 default: 147 var sub *istanbul.Subject 148 err := msg.Decode(&sub) 149 if err == nil { 150 view = sub.View 151 } 152 } 153 if view == nil { 154 logger.Debug("Nil view", "msg", msg) 155 continue 156 } 157 // Push back if it's a future message 158 err := c.checkMessage(msg.Code, view) 159 if err != nil { 160 if err == errFutureMessage { 161 logger.Trace("Stop processing backlog", "msg", msg) 162 backlog.Push(msg, prio) 163 isFuture = true 164 break 165 } 166 logger.Trace("Skip the backlog event", "msg", msg, "err", err) 167 continue 168 } 169 logger.Trace("Post backlog event", "msg", msg) 170 171 go c.sendEvent(backlogEvent{ 172 src: src, 173 msg: msg, 174 }) 175 } 176 } 177 } 178 179 func toPriority(msgCode uint64, view *istanbul.View) float32 { 180 if msgCode == msgRoundChange { 181 // For msgRoundChange, set the message priority based on its sequence 182 return -float32(view.Sequence.Uint64() * 1000) 183 } 184 // FIXME: round will be reset as 0 while new sequence 185 // 10 * Round limits the range of message code is from 0 to 9 186 // 1000 * Sequence limits the range of round is from 0 to 99 187 return -float32(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode])) 188 }