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  }