github.com/ConsenSys/Quorum@v20.10.0+incompatible/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 "github.com/ethereum/go-ethereum/common" 21 "github.com/ethereum/go-ethereum/consensus/istanbul" 22 ) 23 24 // Start implements core.Engine.Start 25 func (c *core) Start() error { 26 // Start a new round from last sequence + 1 27 c.startNewRound(common.Big0) 28 29 // Tests will handle events itself, so we have to make subscribeEvents() 30 // be able to call in test. 31 c.subscribeEvents() 32 go c.handleEvents() 33 34 return nil 35 } 36 37 // Stop implements core.Engine.Stop 38 func (c *core) Stop() error { 39 c.stopTimer() 40 c.unsubscribeEvents() 41 42 // Make sure the handler goroutine exits 43 c.handlerWg.Wait() 44 return nil 45 } 46 47 // ---------------------------------------------------------------------------- 48 49 // Subscribe both internal and external events 50 func (c *core) subscribeEvents() { 51 c.events = c.backend.EventMux().Subscribe( 52 // external events 53 istanbul.RequestEvent{}, 54 istanbul.MessageEvent{}, 55 // internal events 56 backlogEvent{}, 57 ) 58 c.timeoutSub = c.backend.EventMux().Subscribe( 59 timeoutEvent{}, 60 ) 61 c.finalCommittedSub = c.backend.EventMux().Subscribe( 62 istanbul.FinalCommittedEvent{}, 63 ) 64 } 65 66 // Unsubscribe all events 67 func (c *core) unsubscribeEvents() { 68 c.events.Unsubscribe() 69 c.timeoutSub.Unsubscribe() 70 c.finalCommittedSub.Unsubscribe() 71 } 72 73 func (c *core) handleEvents() { 74 // Clear state 75 defer func() { 76 c.current = nil 77 c.handlerWg.Done() 78 }() 79 80 c.handlerWg.Add(1) 81 for { 82 select { 83 case event, ok := <-c.events.Chan(): 84 if !ok { 85 return 86 } 87 // A real event arrived, process interesting content 88 switch ev := event.Data.(type) { 89 case istanbul.RequestEvent: 90 r := &istanbul.Request{ 91 Proposal: ev.Proposal, 92 } 93 err := c.handleRequest(r) 94 if err == errFutureMessage { 95 c.storeRequestMsg(r) 96 } 97 case istanbul.MessageEvent: 98 if err := c.handleMsg(ev.Payload); err == nil { 99 c.backend.Gossip(c.valSet, ev.Payload) 100 } 101 case backlogEvent: 102 // No need to check signature for internal messages 103 if err := c.handleCheckedMsg(ev.msg, ev.src); err == nil { 104 p, err := ev.msg.Payload() 105 if err != nil { 106 c.logger.Warn("Get message payload failed", "err", err) 107 continue 108 } 109 c.backend.Gossip(c.valSet, p) 110 } 111 } 112 case _, ok := <-c.timeoutSub.Chan(): 113 if !ok { 114 return 115 } 116 c.handleTimeoutMsg() 117 case event, ok := <-c.finalCommittedSub.Chan(): 118 if !ok { 119 return 120 } 121 switch event.Data.(type) { 122 case istanbul.FinalCommittedEvent: 123 c.handleFinalCommitted() 124 } 125 } 126 } 127 } 128 129 // sendEvent sends events to mux 130 func (c *core) sendEvent(ev interface{}) { 131 c.backend.EventMux().Post(ev) 132 } 133 134 func (c *core) handleMsg(payload []byte) error { 135 logger := c.logger.New() 136 137 // Decode message and check its signature 138 msg := new(message) 139 if err := msg.FromPayload(payload, c.validateFn); err != nil { 140 logger.Error("Failed to decode message from payload", "err", err) 141 return err 142 } 143 144 // Only accept message if the address is valid 145 _, src := c.valSet.GetByAddress(msg.Address) 146 if src == nil { 147 logger.Error("Invalid address in message", "msg", msg) 148 return istanbul.ErrUnauthorizedAddress 149 } 150 151 return c.handleCheckedMsg(msg, src) 152 } 153 154 func (c *core) handleCheckedMsg(msg *message, src istanbul.Validator) error { 155 logger := c.logger.New("address", c.address, "from", src) 156 157 // Store the message if it's a future message 158 testBacklog := func(err error) error { 159 if err == errFutureMessage { 160 c.storeBacklog(msg, src) 161 } 162 163 return err 164 } 165 166 switch msg.Code { 167 case msgPreprepare: 168 return testBacklog(c.handlePreprepare(msg, src)) 169 case msgPrepare: 170 return testBacklog(c.handlePrepare(msg, src)) 171 case msgCommit: 172 return testBacklog(c.handleCommit(msg, src)) 173 case msgRoundChange: 174 return testBacklog(c.handleRoundChange(msg, src)) 175 default: 176 logger.Error("Invalid message", "msg", msg) 177 } 178 179 return errInvalidMessage 180 } 181 182 func (c *core) handleTimeoutMsg() { 183 // If we're not waiting for round change yet, we can try to catch up 184 // the max round with F+1 round change message. We only need to catch up 185 // if the max round is larger than current round. 186 if !c.waitingForRoundChange { 187 maxRound := c.roundChangeSet.MaxRound(c.valSet.F() + 1) 188 if maxRound != nil && maxRound.Cmp(c.current.Round()) > 0 { 189 c.sendRoundChange(maxRound) 190 return 191 } 192 } 193 194 lastProposal, _ := c.backend.LastProposal() 195 if lastProposal != nil && lastProposal.Number().Cmp(c.current.Sequence()) >= 0 { 196 c.logger.Trace("round change timeout, catch up latest sequence", "number", lastProposal.Number().Uint64()) 197 c.startNewRound(common.Big0) 198 } else { 199 c.sendNextRoundChange() 200 } 201 }