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