github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/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 backend 18 19 import ( 20 "errors" 21 "reflect" 22 23 "github.com/ethereum/go-ethereum/common" 24 "github.com/ethereum/go-ethereum/consensus" 25 "github.com/ethereum/go-ethereum/consensus/istanbul" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/event" 28 "github.com/ethereum/go-ethereum/p2p" 29 lru "github.com/hashicorp/golang-lru" 30 ) 31 32 // If you want to add a code, you need to increment the Lengths Array size! 33 const ( 34 istanbulConsensusMsg = 0x11 35 istanbulAnnounceMsg = 0x12 36 istanbulValEnodesShareMsg = 0x13 37 istanbulFwdMsg = 0x14 38 istanbulDelegateSign = 0x15 39 ) 40 41 var ( 42 // errDecodeFailed is returned when decode message fails 43 errDecodeFailed = errors.New("fail to decode istanbul message") 44 ) 45 46 // Protocol implements consensus.Engine.Protocol 47 func (sb *Backend) Protocol() consensus.Protocol { 48 return consensus.Protocol{ 49 Name: "istanbul", 50 Versions: []uint{64}, 51 Lengths: []uint64{22}, 52 Primary: true, 53 } 54 } 55 56 func (sb *Backend) isIstanbulMsg(msg p2p.Msg) bool { 57 return (msg.Code == istanbulConsensusMsg) || (msg.Code == istanbulAnnounceMsg) || (msg.Code == istanbulValEnodesShareMsg) || (msg.Code == istanbulFwdMsg) || (msg.Code == istanbulDelegateSign) 58 } 59 60 // HandleMsg implements consensus.Handler.HandleMsg 61 func (sb *Backend) HandleMsg(addr common.Address, msg p2p.Msg, peer consensus.Peer) (bool, error) { 62 sb.coreMu.Lock() 63 defer sb.coreMu.Unlock() 64 65 sb.logger.Trace("HandleMsg called", "address", addr, "m", msg, "peer", peer.Node()) 66 67 if sb.isIstanbulMsg(msg) { 68 if (!sb.coreStarted && !sb.config.Proxy) && (msg.Code == istanbulConsensusMsg) { 69 return true, istanbul.ErrStoppedEngine 70 } 71 72 var data []byte 73 if err := msg.Decode(&data); err != nil { 74 if err == errUnauthorized { 75 sb.logger.Debug("Failed to decode message payload", "err", err) 76 } else { 77 sb.logger.Error("Failed to decode message payload", "err", err) 78 } 79 return true, errDecodeFailed 80 } 81 82 if msg.Code == istanbulDelegateSign { 83 if sb.shouldHandleDelegateSign() { 84 go sb.delegateSignFeed.Send(istanbul.MessageEvent{Payload: data}) 85 return true, nil 86 } 87 88 return true, errors.New("No proxy or proxied validator found") 89 } 90 91 hash := istanbul.RLPHash(data) 92 93 // Mark peer's message 94 ms, ok := sb.recentMessages.Get(addr) 95 var m *lru.ARCCache 96 if ok { 97 m, _ = ms.(*lru.ARCCache) 98 } else { 99 m, _ = lru.NewARC(inmemoryMessages) 100 sb.recentMessages.Add(addr, m) 101 } 102 m.Add(hash, true) 103 104 // Mark self known message 105 if _, ok := sb.knownMessages.Get(hash); ok { 106 return true, nil 107 } 108 sb.knownMessages.Add(hash, true) 109 110 if msg.Code == istanbulConsensusMsg { 111 err := sb.handleConsensusMsg(peer, data) 112 return true, err 113 } else if msg.Code == istanbulFwdMsg { 114 err := sb.handleFwdMsg(peer, data) 115 return true, err 116 } else if msg.Code == istanbulAnnounceMsg { 117 go sb.handleIstAnnounce(data) 118 } else if msg.Code == istanbulValEnodesShareMsg { 119 go sb.handleValEnodesShareMsg(data) 120 } 121 122 return true, nil 123 } 124 return false, nil 125 } 126 127 // Handle an incoming consensus msg 128 func (sb *Backend) handleConsensusMsg(peer consensus.Peer, payload []byte) error { 129 if sb.config.Proxy { 130 // Verify that this message is not from the proxied peer 131 if reflect.DeepEqual(peer, sb.proxiedPeer) { 132 sb.logger.Warn("Got a consensus message from the proxied validator. Ignoring it") 133 return nil 134 } 135 136 // Need to forward the message to the proxied validator 137 sb.logger.Trace("Forwarding consensus message to proxied validator") 138 if sb.proxiedPeer != nil { 139 go sb.proxiedPeer.Send(istanbulConsensusMsg, payload) 140 } 141 } else { // The case when this node is a validator 142 go sb.istanbulEventMux.Post(istanbul.MessageEvent{ 143 Payload: payload, 144 }) 145 } 146 147 return nil 148 } 149 150 // Handle an incoming forward msg 151 func (sb *Backend) handleFwdMsg(peer consensus.Peer, payload []byte) error { 152 // Ignore the message if this node it not a proxy 153 if !sb.config.Proxy { 154 sb.logger.Warn("Got a forward consensus message and this node is not a proxy. Ignoring it") 155 return nil 156 } 157 158 // Verify that it's coming from the proxied peer 159 if !reflect.DeepEqual(peer, sb.proxiedPeer) { 160 sb.logger.Warn("Got a forward consensus message from a non proxied valiator. Ignoring it") 161 return nil 162 } 163 164 istMsg := new(istanbul.Message) 165 166 // An Istanbul FwdMsg doesn't have a signature since it's coming from a trusted peer and 167 // the wrapped message is already signed by the proxied validator. 168 if err := istMsg.FromPayload(payload, nil); err != nil { 169 sb.logger.Error("Failed to decode message from payload", "err", err) 170 return err 171 } 172 173 var fwdMsg *istanbul.ForwardMessage 174 err := istMsg.Decode(&fwdMsg) 175 if err != nil { 176 sb.logger.Error("Failed to decode a ForwardMessage", "err", err) 177 return err 178 } 179 180 sb.logger.Debug("Forwarding a consensus message") 181 go sb.Gossip(fwdMsg.DestAddresses, fwdMsg.Msg, istanbulConsensusMsg, false) 182 return nil 183 } 184 185 func (sb *Backend) shouldHandleDelegateSign() bool { 186 return sb.IsProxy() || sb.IsProxiedValidator() 187 } 188 189 // SubscribeNewDelegateSignEvent subscribes a channel to any new delegate sign messages 190 func (sb *Backend) SubscribeNewDelegateSignEvent(ch chan<- istanbul.MessageEvent) event.Subscription { 191 return sb.delegateSignScope.Track(sb.delegateSignFeed.Subscribe(ch)) 192 } 193 194 // SetBroadcaster implements consensus.Handler.SetBroadcaster 195 func (sb *Backend) SetBroadcaster(broadcaster consensus.Broadcaster) { 196 sb.broadcaster = broadcaster 197 } 198 199 // SetP2PServer implements consensus.Handler.SetP2PServer 200 func (sb *Backend) SetP2PServer(p2pserver consensus.P2PServer) { 201 sb.p2pserver = p2pserver 202 } 203 204 // This function is called by miner/worker.go whenever it's mainLoop gets a newWork event. 205 func (sb *Backend) NewWork() error { 206 sb.coreMu.RLock() 207 defer sb.coreMu.RUnlock() 208 if !sb.coreStarted { 209 return istanbul.ErrStoppedEngine 210 } 211 212 go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{}) 213 return nil 214 } 215 216 // This function is called by all nodes. 217 // At the end of each epoch, this function will 218 // 1) Output if it is or isn't an elected validator if it has mining turned on. 219 // 2) Refresh the validator connections if it's a proxy or non proxied validator 220 func (sb *Backend) NewChainHead(newBlock *types.Block) { 221 if istanbul.IsLastBlockOfEpoch(newBlock.Number().Uint64(), sb.config.Epoch) { 222 sb.coreMu.RLock() 223 defer sb.coreMu.RUnlock() 224 225 valset := sb.getValidators(newBlock.Number().Uint64(), newBlock.Hash()) 226 227 // Output whether this validator was or wasn't elected for the 228 // new epoch's validator set 229 if sb.coreStarted { 230 _, val := valset.GetByAddress(sb.ValidatorAddress()) 231 sb.logger.Info("Validator Election Results", "address", sb.ValidatorAddress(), "elected", (val != nil), "number", newBlock.Number().Uint64()) 232 233 sb.newEpochCh <- struct{}{} 234 } 235 236 // If this is a proxy or a non proxied validator and a 237 // new epoch just started, then refresh the validator enode table 238 sb.logger.Trace("At end of epoch and going to refresh validator peers", "new block number", newBlock.Number().Uint64()) 239 sb.RefreshValPeers(valset) 240 } 241 } 242 243 func (sb *Backend) RegisterPeer(peer consensus.Peer, isProxiedPeer bool) { 244 // TODO - For added security, we may want the node keys of the proxied validators to be 245 // registered with the proxy, and verify that all newly connected proxied peer has 246 // the correct node key 247 if sb.config.Proxy && isProxiedPeer { 248 sb.proxiedPeer = peer 249 } else if sb.config.Proxied { 250 if sb.proxyNode != nil && peer.Node().ID() == sb.proxyNode.node.ID() { 251 sb.proxyNode.peer = peer 252 } else { 253 sb.logger.Error("Unauthorized connected peer to the proxied validator", "peer node", peer.Node().String()) 254 } 255 } 256 } 257 258 func (sb *Backend) UnregisterPeer(peer consensus.Peer, isProxiedPeer bool) { 259 if sb.config.Proxy && isProxiedPeer && reflect.DeepEqual(sb.proxiedPeer, peer) { 260 sb.proxiedPeer = nil 261 } else if sb.config.Proxied { 262 if sb.proxyNode != nil && peer.Node().ID() == sb.proxyNode.node.ID() { 263 sb.proxyNode.peer = nil 264 } 265 } 266 }