github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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 "bytes" 21 "errors" 22 "io/ioutil" 23 "math/big" 24 "reflect" 25 26 "github.com/kisexp/xdchain/common" 27 "github.com/kisexp/xdchain/consensus" 28 "github.com/kisexp/xdchain/consensus/istanbul" 29 qbfttypes "github.com/kisexp/xdchain/consensus/istanbul/qbft/types" 30 "github.com/kisexp/xdchain/core/types" 31 "github.com/kisexp/xdchain/p2p" 32 lru "github.com/hashicorp/golang-lru" 33 ) 34 35 const ( 36 NewBlockMsg = 0x07 37 istanbulMsg = 0x11 38 ) 39 40 var ( 41 // errDecodeFailed is returned when decode message fails 42 errDecodeFailed = errors.New("fail to decode istanbul message") 43 44 // errPayloadReadFailed is returned when qbft message read fails 45 errPayloadReadFailed = errors.New("unable to read payload from message") 46 ) 47 48 // Protocol implements consensus.Engine.Protocol 49 func (sb *Backend) Protocol() consensus.Protocol { 50 return consensus.IstanbulProtocol 51 } 52 53 func (sb *Backend) decode(msg p2p.Msg) ([]byte, common.Hash, error) { 54 var data []byte 55 if sb.IsQBFTConsensus() { 56 data = make([]byte, msg.Size) 57 if _, err := msg.Payload.Read(data); err != nil { 58 return nil, common.Hash{}, errPayloadReadFailed 59 } 60 } else { 61 if err := msg.Decode(&data); err != nil { 62 return nil, common.Hash{}, errDecodeFailed 63 } 64 } 65 return data, istanbul.RLPHash(data), nil 66 } 67 68 // HandleMsg implements consensus.Handler.HandleMsg 69 func (sb *Backend) HandleMsg(addr common.Address, msg p2p.Msg) (bool, error) { 70 sb.coreMu.Lock() 71 defer sb.coreMu.Unlock() 72 if _, ok := qbfttypes.MessageCodes()[msg.Code]; ok || msg.Code == istanbulMsg { 73 if !sb.coreStarted { 74 return true, istanbul.ErrStoppedEngine 75 } 76 77 data, hash, err := sb.decode(msg) 78 if err != nil { 79 return true, errDecodeFailed 80 } 81 // Mark peer's message 82 ms, ok := sb.recentMessages.Get(addr) 83 var m *lru.ARCCache 84 if ok { 85 m, _ = ms.(*lru.ARCCache) 86 } else { 87 m, _ = lru.NewARC(inmemoryMessages) 88 sb.recentMessages.Add(addr, m) 89 } 90 m.Add(hash, true) 91 92 // Mark self known message 93 if _, ok := sb.knownMessages.Get(hash); ok { 94 return true, nil 95 } 96 sb.knownMessages.Add(hash, true) 97 98 go sb.istanbulEventMux.Post(istanbul.MessageEvent{ 99 Code: msg.Code, 100 Payload: data, 101 }) 102 return true, nil 103 } 104 //https://github.com/ConsenSys/quorum/pull/539 105 //https://github.com/ConsenSys/quorum/issues/389 106 if msg.Code == NewBlockMsg && sb.core != nil && sb.core.IsProposer() { // eth.NewBlockMsg: import cycle 107 // this case is to safeguard the race of similar block which gets propagated from other node while this node is proposing 108 // as p2p.Msg can only be decoded once (get EOF for any subsequence read), we need to make sure the payload is restored after we decode it 109 sb.logger.Debug("BFT: received NewBlockMsg", "size", msg.Size, "payload.type", reflect.TypeOf(msg.Payload), "sender", addr) 110 if reader, ok := msg.Payload.(*bytes.Reader); ok { 111 payload, err := ioutil.ReadAll(reader) 112 if err != nil { 113 return true, err 114 } 115 reader.Reset(payload) // ready to be decoded 116 defer reader.Reset(payload) // restore so main eth/handler can decode 117 var request struct { // this has to be same as eth/protocol.go#newBlockData as we are reading NewBlockMsg 118 Block *types.Block 119 TD *big.Int 120 } 121 if err := msg.Decode(&request); err != nil { 122 sb.logger.Error("BFT: unable to decode the NewBlockMsg", "error", err) 123 return false, nil 124 } 125 newRequestedBlock := request.Block 126 if newRequestedBlock.Header().MixDigest == types.IstanbulDigest && sb.core.IsCurrentProposal(newRequestedBlock.Hash()) { 127 sb.logger.Debug("BFT: block already proposed", "hash", newRequestedBlock.Hash(), "sender", addr) 128 return true, nil 129 } 130 } 131 } 132 return false, nil 133 } 134 135 // SetBroadcaster implements consensus.Handler.SetBroadcaster 136 func (sb *Backend) SetBroadcaster(broadcaster consensus.Broadcaster) { 137 sb.broadcaster = broadcaster 138 } 139 140 func (sb *Backend) NewChainHead() error { 141 sb.coreMu.RLock() 142 defer sb.coreMu.RUnlock() 143 if !sb.coreStarted { 144 return istanbul.ErrStoppedEngine 145 } 146 go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{}) 147 return nil 148 }