github.com/hyperion-hyn/go-ethereum@v2.4.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 82 for { 83 select { 84 case event, ok := <-c.events.Chan(): 85 if !ok { 86 return 87 } 88 // A real event arrived, process interesting content 89 switch ev := event.Data.(type) { 90 case istanbul.RequestEvent: 91 r := &istanbul.Request{ 92 Proposal: ev.Proposal, 93 } 94 err := c.handleRequest(r) 95 if err == errFutureMessage { 96 c.storeRequestMsg(r) 97 } 98 case istanbul.MessageEvent: 99 if err := c.handleMsg(ev.Payload); err == nil { 100 c.backend.Gossip(c.valSet, ev.Payload) 101 } 102 case backlogEvent: 103 // No need to check signature for internal messages 104 if err := c.handleCheckedMsg(ev.msg, ev.src); err == nil { 105 p, err := ev.msg.Payload() 106 if err != nil { 107 c.logger.Warn("Get message payload failed", "err", err) 108 continue 109 } 110 c.backend.Gossip(c.valSet, p) 111 } 112 } 113 case _, ok := <-c.timeoutSub.Chan(): 114 if !ok { 115 return 116 } 117 c.handleTimeoutMsg() 118 case event, ok := <-c.finalCommittedSub.Chan(): 119 if !ok { 120 return 121 } 122 switch event.Data.(type) { 123 case istanbul.FinalCommittedEvent: 124 c.handleFinalCommitted() 125 } 126 } 127 } 128 } 129 130 // sendEvent sends events to mux 131 func (c *core) sendEvent(ev interface{}) { 132 c.backend.EventMux().Post(ev) 133 } 134 135 func (c *core) handleMsg(payload []byte) error { 136 logger := c.logger.New() 137 138 // Decode message and check its signature 139 msg := new(message) 140 if err := msg.FromPayload(payload, c.validateFn); err != nil { 141 logger.Error("Failed to decode message from payload", "err", err) 142 return err 143 } 144 145 // Only accept message if the address is valid 146 _, src := c.valSet.GetByAddress(msg.Address) 147 if src == nil { 148 logger.Error("Invalid address in message", "msg", msg) 149 return istanbul.ErrUnauthorizedAddress 150 } 151 152 return c.handleCheckedMsg(msg, src) 153 } 154 155 func (c *core) handleCheckedMsg(msg *message, src istanbul.Validator) error { 156 logger := c.logger.New("address", c.address, "from", src) 157 158 // Store the message if it's a future message 159 testBacklog := func(err error) error { 160 if err == errFutureMessage { 161 c.storeBacklog(msg, src) 162 } 163 164 return err 165 } 166 167 switch msg.Code { 168 case msgPreprepare: 169 return testBacklog(c.handlePreprepare(msg, src)) 170 case msgPrepare: 171 return testBacklog(c.handlePrepare(msg, src)) 172 case msgCommit: 173 return testBacklog(c.handleCommit(msg, src)) 174 case msgRoundChange: 175 return testBacklog(c.handleRoundChange(msg, src)) 176 default: 177 logger.Error("Invalid message", "msg", msg) 178 } 179 180 return errInvalidMessage 181 } 182 183 func (c *core) handleTimeoutMsg() { 184 // If we're not waiting for round change yet, we can try to catch up 185 // the max round with F+1 round change message. We only need to catch up 186 // if the max round is larger than current round. 187 if !c.waitingForRoundChange { 188 maxRound := c.roundChangeSet.MaxRound(c.valSet.F() + 1) 189 if maxRound != nil && maxRound.Cmp(c.current.Round()) > 0 { 190 c.sendRoundChange(maxRound) 191 return 192 } 193 } 194 195 lastProposal, _ := c.backend.LastProposal() 196 if lastProposal != nil && lastProposal.Number().Cmp(c.current.Sequence()) >= 0 { 197 c.logger.Trace("round change timeout, catch up latest sequence", "number", lastProposal.Number().Uint64()) 198 c.startNewRound(common.Big0) 199 } else { 200 c.sendNextRoundChange() 201 } 202 }