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