github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/core/handler.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from quorum/consensus/istanbul/core/handler.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package core 22 23 import ( 24 "github.com/klaytn/klaytn/common" 25 "github.com/klaytn/klaytn/consensus/istanbul" 26 ) 27 28 // Start implements core.Engine.Start 29 func (c *core) Start() error { 30 // Start a new round from last sequence + 1 31 c.startNewRound(common.Big0) 32 33 // Tests will handle events itself, so we have to make subscribeEvents() 34 // be able to call in test. 35 c.subscribeEvents() 36 go c.handleEvents() 37 38 return nil 39 } 40 41 // Stop implements core.Engine.Stop 42 func (c *core) Stop() error { 43 c.stopTimer() 44 c.unsubscribeEvents() 45 46 // Make sure the handler goroutine exits 47 c.handlerWg.Wait() 48 return nil 49 } 50 51 // ---------------------------------------------------------------------------- 52 53 // Subscribe both internal and external events 54 func (c *core) subscribeEvents() { 55 c.events = c.backend.EventMux().Subscribe( 56 // external events 57 istanbul.RequestEvent{}, 58 istanbul.MessageEvent{}, 59 // internal events 60 backlogEvent{}, 61 ) 62 c.timeoutSub = c.backend.EventMux().Subscribe( 63 timeoutEvent{}, 64 ) 65 c.finalCommittedSub = c.backend.EventMux().Subscribe( 66 istanbul.FinalCommittedEvent{}, 67 ) 68 } 69 70 // Unsubscribe all events 71 func (c *core) unsubscribeEvents() { 72 c.events.Unsubscribe() 73 c.timeoutSub.Unsubscribe() 74 c.finalCommittedSub.Unsubscribe() 75 } 76 77 func (c *core) handleEvents() { 78 // Clear state 79 defer func() { 80 c.current = nil 81 c.handlerWg.Done() 82 }() 83 84 c.handlerWg.Add(1) 85 86 for { 87 select { 88 case event, ok := <-c.events.Chan(): 89 if !ok { 90 return 91 } 92 // A real event arrived, process interesting content 93 switch ev := event.Data.(type) { 94 case istanbul.RequestEvent: 95 r := &istanbul.Request{ 96 Proposal: ev.Proposal, 97 } 98 err := c.handleRequest(r) 99 if err == errFutureMessage { 100 c.storeRequestMsg(r) 101 } 102 case istanbul.MessageEvent: 103 if err := c.handleMsg(ev.Payload); err == nil { 104 c.backend.GossipSubPeer(ev.Hash, c.valSet, ev.Payload) 105 // c.backend.Gossip(c.valSet, ev.Payload) 106 } 107 case backlogEvent: 108 _, src := c.valSet.GetByAddress(ev.src) 109 if src == nil { 110 c.logger.Error("Invalid address in valSet", "addr", ev.src) 111 continue 112 } 113 // No need to check signature for internal messages 114 if err := c.handleCheckedMsg(ev.msg, src); err == nil { 115 p, err := ev.msg.Payload() 116 if err != nil { 117 c.logger.Warn("Get message payload failed", "err", err) 118 continue 119 } 120 c.backend.GossipSubPeer(ev.Hash, c.valSet, p) 121 // c.backend.Gossip(c.valSet, p) 122 } 123 } 124 case ev, ok := <-c.timeoutSub.Chan(): 125 if !ok || ev.Data == nil { 126 logger.Error("Drop an empty message from timeout channel") 127 return 128 } 129 data, ok := ev.Data.(timeoutEvent) 130 if !ok || data.nextView == nil { 131 logger.Error("Invalid message from timeout channel", "msg", ev.Data) 132 return 133 } 134 c.handleTimeoutMsg(data.nextView) 135 case event, ok := <-c.finalCommittedSub.Chan(): 136 if !ok { 137 return 138 } 139 switch event.Data.(type) { 140 case istanbul.FinalCommittedEvent: 141 c.handleFinalCommitted() 142 } 143 } 144 } 145 } 146 147 // sendEvent sends events to mux 148 func (c *core) sendEvent(ev interface{}) { 149 c.backend.EventMux().Post(ev) 150 } 151 152 func (c *core) handleMsg(payload []byte) error { 153 logger := c.logger.NewWith() 154 155 // Decode message and check its signature 156 msg := new(message) 157 if err := msg.FromPayload(payload, c.validateFn); err != nil { 158 if c.backend.NodeType() == common.CONSENSUSNODE { 159 if err != istanbul.ErrUnauthorizedAddress { 160 logger.Error("Failed to decode message from payload", "err", err) 161 return err 162 } 163 164 msgView, msgDecodeErr := msg.GetView() 165 if msgDecodeErr != nil { 166 logger.Error("Failed to decode message while getting view information", "code", msg.Code, "err", msgDecodeErr) 167 return err 168 } 169 170 // Print view and address to help you analyze the node is valid or not. 171 // This information will help you to analyze whether the msg sender is valid or not. 172 // Furthermore, if the node is still syncing, there is a high probability that msg sender is a valid validator. 173 logger.Warn("Received Consensus msg is signed by an unauthorized address. It could happen when the node is unsynced temporarily.", "senderAddress", msg.Address, "nodeView", c.currentView().String(), "msgView", msgView.String()) 174 } 175 return err 176 } 177 178 // Only accept message if the address is valid 179 _, src := c.valSet.GetByAddress(msg.Address) 180 if src == nil { 181 logger.Error("Invalid address in message", "msg", msg) 182 return istanbul.ErrUnauthorizedAddress 183 } 184 185 return c.handleCheckedMsg(msg, src) 186 } 187 188 func (c *core) handleCheckedMsg(msg *message, src istanbul.Validator) error { 189 logger := c.logger.NewWith("address", c.address, "from", src) 190 191 // Store the message if it's a future message 192 testBacklog := func(err error) error { 193 if err == errFutureMessage { 194 c.storeBacklog(msg, src) 195 } 196 197 return err 198 } 199 200 switch msg.Code { 201 case msgPreprepare: 202 return testBacklog(c.handlePreprepare(msg, src)) 203 case msgPrepare: 204 return testBacklog(c.handlePrepare(msg, src)) 205 case msgCommit: 206 return testBacklog(c.handleCommit(msg, src)) 207 case msgRoundChange: 208 return testBacklog(c.handleRoundChange(msg, src)) 209 default: 210 logger.Error("Invalid message type", "msg", msg) 211 } 212 213 return errInvalidMessage 214 } 215 216 func (c *core) handleTimeoutMsg(nextView *istanbul.View) { 217 // TODO-Klaytn-Istanbul: EN/PN should not handle consensus msgs. 218 if c.backend.NodeType() != common.CONSENSUSNODE { 219 logger.Trace("PN/EN doesn't need to handle timeout messages", 220 "nodeType", c.backend.NodeType().String()) 221 return 222 } 223 224 lastProposal, _ := c.backend.LastProposal() 225 if lastProposal == nil { 226 logger.Error("Received timeout message but can't find the last proposal", "msgView", nextView.String()) 227 return 228 } 229 230 if lastProposal.Number().Cmp(nextView.Sequence) >= 0 { 231 logger.Debug("This timeoutMsg is outdated", 232 "blockNumber", lastProposal.Number().Uint64(), "msgView", nextView.String()) 233 return 234 } 235 236 // If we're not waiting for round change yet, we can try to catch up 237 // the max round with F+1 round change message. We only need to catch up 238 // if the max round is larger than current round. 239 if !c.waitingForRoundChange { 240 maxRound := c.roundChangeSet.MaxRound(c.valSet.F() + 1) 241 if maxRound != nil && maxRound.Cmp(c.current.Round()) > 0 { 242 logger.Warn("[RC] Send round change because of timeout event") 243 c.sendRoundChange(maxRound) 244 return 245 } 246 } 247 248 if lastProposal.Number().Cmp(c.current.Sequence()) >= 0 { 249 c.logger.Trace("round change timeout, catch up latest sequence", "number", lastProposal.Number().Uint64()) 250 c.startNewRound(common.Big0) 251 } else { 252 c.sendRoundChange(nextView.Round) 253 } 254 }