github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/handler.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 22 "github.com/ethereum/go-ethereum/common" 23 "github.com/ethereum/go-ethereum/consensus/istanbul" 24 ) 25 26 // Start implements core.Engine.Start 27 func (c *core) Start() error { 28 29 roundState, err := c.createRoundState() 30 if err != nil { 31 return err 32 } 33 34 c.current = roundState 35 c.roundChangeSet = newRoundChangeSet(c.current.ValidatorSet()) 36 37 // Reset the Round Change timer for the current round to timeout. 38 // (If we've restored RoundState such that we are in StateWaitingForRoundChange, 39 // this may also start a timer to send a repeat round change message.) 40 c.resetRoundChangeTimer() 41 42 // Process backlog 43 c.processPendingRequests() 44 c.backlog.updateState(c.CurrentView(), c.current.State()) 45 46 // Tests will handle events itself, so we have to make subscribeEvents() 47 // be able to call in test. 48 c.subscribeEvents() 49 go c.handleEvents() 50 51 return nil 52 } 53 54 // Stop implements core.Engine.Stop 55 func (c *core) Stop() error { 56 c.stopAllTimers() 57 c.unsubscribeEvents() 58 59 // Make sure the handler goroutine exits 60 c.handlerWg.Wait() 61 62 c.current = nil 63 return nil 64 } 65 66 // ---------------------------------------------------------------------------- 67 68 // Subscribe both internal and external events 69 func (c *core) subscribeEvents() { 70 c.events = c.backend.EventMux().Subscribe( 71 // external events 72 istanbul.RequestEvent{}, 73 istanbul.MessageEvent{}, 74 // internal events 75 backlogEvent{}, 76 ) 77 c.timeoutSub = c.backend.EventMux().Subscribe( 78 timeoutAndMoveToNextRoundEvent{}, 79 resendRoundChangeEvent{}, 80 ) 81 c.finalCommittedSub = c.backend.EventMux().Subscribe( 82 istanbul.FinalCommittedEvent{}, 83 ) 84 } 85 86 // Unsubscribe all events 87 func (c *core) unsubscribeEvents() { 88 c.events.Unsubscribe() 89 c.timeoutSub.Unsubscribe() 90 c.finalCommittedSub.Unsubscribe() 91 } 92 93 func (c *core) handleEvents() { 94 // Clear state 95 defer func() { 96 c.handlerWg.Done() 97 }() 98 99 c.handlerWg.Add(1) 100 101 for { 102 logger := c.newLogger("func", "handleEvents") 103 select { 104 case event, ok := <-c.events.Chan(): 105 if !ok { 106 return 107 } 108 // A real event arrived, process interesting content 109 switch ev := event.Data.(type) { 110 case istanbul.RequestEvent: 111 r := &istanbul.Request{ 112 Proposal: ev.Proposal, 113 } 114 err := c.handleRequest(r) 115 if err == errFutureMessage { 116 c.storeRequestMsg(r) 117 } 118 case istanbul.MessageEvent: 119 if err := c.handleMsg(ev.Payload); err != nil { 120 logger.Debug("Error in handling istanbul message", "err", err) 121 } 122 case backlogEvent: 123 if payload, err := ev.msg.Payload(); err != nil { 124 logger.Error("Error in retrieving payload from istanbul message that was sent from a backlog event", "err", err) 125 } else { 126 if err := c.handleMsg(payload); err != nil { 127 logger.Debug("Error in handling istanbul message that was sent from a backlog event", "err", err) 128 } 129 } 130 } 131 case event, ok := <-c.timeoutSub.Chan(): 132 if !ok { 133 return 134 } 135 switch ev := event.Data.(type) { 136 case timeoutAndMoveToNextRoundEvent: 137 if err := c.handleTimeoutAndMoveToNextRound(ev.view); err != nil { 138 logger.Error("Error on handleTimeoutAndMoveToNextRound", "err", err) 139 } 140 case resendRoundChangeEvent: 141 if err := c.handleResendRoundChangeEvent(ev.view); err != nil { 142 logger.Error("Error on handleResendRoundChangeEvent", "err", err) 143 } 144 } 145 case event, ok := <-c.finalCommittedSub.Chan(): 146 if !ok { 147 return 148 } 149 switch event.Data.(type) { 150 case istanbul.FinalCommittedEvent: 151 if err := c.handleFinalCommitted(); err != nil { 152 logger.Error("Error on handleFinalCommit", "err", err) 153 } 154 } 155 } 156 } 157 } 158 159 // sendEvent sends events to mux 160 func (c *core) sendEvent(ev interface{}) { 161 c.backend.EventMux().Post(ev) 162 } 163 164 func (c *core) handleMsg(payload []byte) error { 165 logger := c.newLogger("func", "handleMsg") 166 167 // Decode message and check its signature 168 msg := new(istanbul.Message) 169 if err := msg.FromPayload(payload, c.validateFn); err != nil { 170 logger.Debug("Failed to decode message from payload", "err", err) 171 return err 172 } 173 174 // Only accept message if the address is valid 175 _, src := c.current.ValidatorSet().GetByAddress(msg.Address) 176 if src == nil { 177 logger.Error("Invalid address in message", "m", msg) 178 return istanbul.ErrUnauthorizedAddress 179 } 180 181 return c.handleCheckedMsg(msg, src) 182 } 183 184 func (c *core) handleCheckedMsg(msg *istanbul.Message, src istanbul.Validator) error { 185 logger := c.newLogger("func", "handleCheckedMsg", "from", msg.Address) 186 187 // Store the message if it's a future message 188 catchFutureMessages := func(err error) error { 189 if err == errFutureMessage { 190 // Store in backlog (if it's not from self) 191 if msg.Address != c.address { 192 c.backlog.store(msg) 193 } 194 } 195 return err 196 } 197 198 switch msg.Code { 199 case istanbul.MsgPreprepare: 200 return catchFutureMessages(c.handlePreprepare(msg)) 201 case istanbul.MsgPrepare: 202 return catchFutureMessages(c.handlePrepare(msg)) 203 case istanbul.MsgCommit: 204 return catchFutureMessages(c.handleCommit(msg)) 205 case istanbul.MsgRoundChange: 206 return catchFutureMessages(c.handleRoundChange(msg)) 207 default: 208 logger.Error("Invalid message", "m", msg) 209 } 210 211 return errInvalidMessage 212 } 213 214 func (c *core) handleTimeoutAndMoveToNextRound(timedOutView *istanbul.View) error { 215 logger := c.newLogger("func", "handleTimeoutAndMoveToNextRound", "timed_out_seq", timedOutView.Sequence, "timed_out_round", timedOutView.Round) 216 217 // Avoid races where message is enqueued then a later event advances sequence or desired round. 218 if c.current.Sequence().Cmp(timedOutView.Sequence) != 0 || c.current.DesiredRound().Cmp(timedOutView.Round) != 0 { 219 logger.Trace("Timed out but now on a different view") 220 return nil 221 } 222 223 logger.Debug("Timed out, trying to wait for next round") 224 nextRound := new(big.Int).Add(timedOutView.Round, common.Big1) 225 return c.waitForDesiredRound(nextRound) 226 } 227 228 func (c *core) handleResendRoundChangeEvent(desiredView *istanbul.View) error { 229 logger := c.newLogger("func", "handleResendRoundChangeEvent", "set_at_seq", desiredView.Sequence, "set_at_desiredRound", desiredView.Round) 230 231 // Avoid races where message is enqueued then a later event advances sequence or desired round. 232 if c.current.Sequence().Cmp(desiredView.Sequence) != 0 || c.current.DesiredRound().Cmp(desiredView.Round) != 0 { 233 logger.Trace("Timed out but now on a different view") 234 return nil 235 } 236 237 c.resendRoundChangeMessage() 238 return nil 239 }