github.com/pfcoder/quorum@v2.0.3-0.20180501191142-d4a1b0958135+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 backlog := c.backlogs[src] 92 if backlog == nil { 93 backlog = prque.New() 94 } 95 switch msg.Code { 96 case msgPreprepare: 97 var p *istanbul.Preprepare 98 err := msg.Decode(&p) 99 if err == nil { 100 backlog.Push(msg, toPriority(msg.Code, p.View)) 101 } 102 // for msgRoundChange, msgPrepare and msgCommit cases 103 default: 104 var p *istanbul.Subject 105 err := msg.Decode(&p) 106 if err == nil { 107 backlog.Push(msg, toPriority(msg.Code, p.View)) 108 } 109 } 110 c.backlogs[src] = backlog 111 } 112 113 func (c *core) processBacklog() { 114 c.backlogsMu.Lock() 115 defer c.backlogsMu.Unlock() 116 117 for src, backlog := range c.backlogs { 118 if backlog == nil { 119 continue 120 } 121 122 logger := c.logger.New("from", src, "state", c.state) 123 isFuture := false 124 125 // We stop processing if 126 // 1. backlog is empty 127 // 2. The first message in queue is a future message 128 for !(backlog.Empty() || isFuture) { 129 m, prio := backlog.Pop() 130 msg := m.(*message) 131 var view *istanbul.View 132 switch msg.Code { 133 case msgPreprepare: 134 var m *istanbul.Preprepare 135 err := msg.Decode(&m) 136 if err == nil { 137 view = m.View 138 } 139 // for msgRoundChange, msgPrepare and msgCommit cases 140 default: 141 var sub *istanbul.Subject 142 err := msg.Decode(&sub) 143 if err == nil { 144 view = sub.View 145 } 146 } 147 if view == nil { 148 logger.Debug("Nil view", "msg", msg) 149 continue 150 } 151 // Push back if it's a future message 152 err := c.checkMessage(msg.Code, view) 153 if err != nil { 154 if err == errFutureMessage { 155 logger.Trace("Stop processing backlog", "msg", msg) 156 backlog.Push(msg, prio) 157 isFuture = true 158 break 159 } 160 logger.Trace("Skip the backlog event", "msg", msg, "err", err) 161 continue 162 } 163 logger.Trace("Post backlog event", "msg", msg) 164 165 go c.sendEvent(backlogEvent{ 166 src: src, 167 msg: msg, 168 }) 169 } 170 } 171 } 172 173 func toPriority(msgCode uint64, view *istanbul.View) float32 { 174 if msgCode == msgRoundChange { 175 // For msgRoundChange, set the message priority based on its sequence 176 return -float32(view.Sequence.Uint64() * 1000) 177 } 178 // FIXME: round will be reset as 0 while new sequence 179 // 10 * Round limits the range of message code is from 0 to 9 180 // 1000 * Sequence limits the range of round is from 0 to 99 181 return -float32(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode])) 182 }