github.com/phillinzzz/newBsc@v1.1.6/eth/protocols/diff/handler.go (about)

     1  package diff
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/phillinzzz/newBsc/core"
     8  	"github.com/phillinzzz/newBsc/metrics"
     9  	"github.com/phillinzzz/newBsc/p2p"
    10  	"github.com/phillinzzz/newBsc/p2p/enode"
    11  	"github.com/phillinzzz/newBsc/p2p/enr"
    12  	"github.com/phillinzzz/newBsc/rlp"
    13  )
    14  
    15  const (
    16  	// softResponseLimit is the target maximum size of replies to data retrievals.
    17  	softResponseLimit = 2 * 1024 * 1024
    18  
    19  	// maxDiffLayerServe is the maximum number of diff layers to serve.
    20  	maxDiffLayerServe = 1024
    21  )
    22  
    23  var requestTracker = NewTracker(time.Minute)
    24  
    25  // Handler is a callback to invoke from an outside runner after the boilerplate
    26  // exchanges have passed.
    27  type Handler func(peer *Peer) error
    28  
    29  type Backend interface {
    30  	// Chain retrieves the blockchain object to serve data.
    31  	Chain() *core.BlockChain
    32  
    33  	// RunPeer is invoked when a peer joins on the `eth` protocol. The handler
    34  	// should do any peer maintenance work, handshakes and validations. If all
    35  	// is passed, control should be given back to the `handler` to process the
    36  	// inbound messages going forward.
    37  	RunPeer(peer *Peer, handler Handler) error
    38  
    39  	PeerInfo(id enode.ID) interface{}
    40  
    41  	Handle(peer *Peer, packet Packet) error
    42  }
    43  
    44  // MakeProtocols constructs the P2P protocol definitions for `diff`.
    45  func MakeProtocols(backend Backend, dnsdisc enode.Iterator) []p2p.Protocol {
    46  	// Filter the discovery iterator for nodes advertising diff support.
    47  	dnsdisc = enode.Filter(dnsdisc, func(n *enode.Node) bool {
    48  		var diff enrEntry
    49  		return n.Load(&diff) == nil
    50  	})
    51  
    52  	protocols := make([]p2p.Protocol, len(ProtocolVersions))
    53  	for i, version := range ProtocolVersions {
    54  		version := version // Closure
    55  
    56  		protocols[i] = p2p.Protocol{
    57  			Name:    ProtocolName,
    58  			Version: version,
    59  			Length:  protocolLengths[version],
    60  			Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
    61  				return backend.RunPeer(NewPeer(version, p, rw), func(peer *Peer) error {
    62  					defer peer.Close()
    63  					return Handle(backend, peer)
    64  				})
    65  			},
    66  			NodeInfo: func() interface{} {
    67  				return nodeInfo(backend.Chain())
    68  			},
    69  			PeerInfo: func(id enode.ID) interface{} {
    70  				return backend.PeerInfo(id)
    71  			},
    72  			Attributes:     []enr.Entry{&enrEntry{}},
    73  			DialCandidates: dnsdisc,
    74  		}
    75  	}
    76  	return protocols
    77  }
    78  
    79  // Handle is the callback invoked to manage the life cycle of a `diff` peer.
    80  // When this function terminates, the peer is disconnected.
    81  func Handle(backend Backend, peer *Peer) error {
    82  	for {
    83  		if err := handleMessage(backend, peer); err != nil {
    84  			peer.Log().Debug("Message handling failed in `diff`", "err", err)
    85  			return err
    86  		}
    87  	}
    88  }
    89  
    90  // handleMessage is invoked whenever an inbound message is received from a
    91  // remote peer on the `diff` protocol. The remote connection is torn down upon
    92  // returning any error.
    93  func handleMessage(backend Backend, peer *Peer) error {
    94  	// Read the next message from the remote peer, and ensure it's fully consumed
    95  	msg, err := peer.rw.ReadMsg()
    96  	if err != nil {
    97  		return err
    98  	}
    99  	if msg.Size > maxMessageSize {
   100  		return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize)
   101  	}
   102  	defer msg.Discard()
   103  	start := time.Now()
   104  	// Track the emount of time it takes to serve the request and run the handler
   105  	if metrics.Enabled {
   106  		h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code)
   107  		defer func(start time.Time) {
   108  			sampler := func() metrics.Sample {
   109  				return metrics.ResettingSample(
   110  					metrics.NewExpDecaySample(1028, 0.015),
   111  				)
   112  			}
   113  			metrics.GetOrRegisterHistogramLazy(h, nil, sampler).Update(time.Since(start).Microseconds())
   114  		}(start)
   115  	}
   116  	// Handle the message depending on its contents
   117  	switch {
   118  	case msg.Code == GetDiffLayerMsg:
   119  		res := new(GetDiffLayersPacket)
   120  		if err := msg.Decode(res); err != nil {
   121  			return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
   122  		}
   123  		diffs := answerDiffLayersQuery(backend, res)
   124  
   125  		p2p.Send(peer.rw, FullDiffLayerMsg, &FullDiffLayersPacket{
   126  			RequestId:        res.RequestId,
   127  			DiffLayersPacket: diffs,
   128  		})
   129  		return nil
   130  
   131  	case msg.Code == DiffLayerMsg:
   132  		// A batch of trie nodes arrived to one of our previous requests
   133  		res := new(DiffLayersPacket)
   134  		if err := msg.Decode(res); err != nil {
   135  			return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
   136  		}
   137  		return backend.Handle(peer, res)
   138  	case msg.Code == FullDiffLayerMsg:
   139  		// A batch of trie nodes arrived to one of our previous requests
   140  		res := new(FullDiffLayersPacket)
   141  		if err := msg.Decode(res); err != nil {
   142  			return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
   143  		}
   144  		if fulfilled := requestTracker.Fulfil(peer.id, peer.version, FullDiffLayerMsg, res.RequestId); fulfilled {
   145  			return backend.Handle(peer, res)
   146  		}
   147  		return fmt.Errorf("%w: %v", errUnexpectedMsg, msg.Code)
   148  	default:
   149  		return fmt.Errorf("%w: %v", errInvalidMsgCode, msg.Code)
   150  	}
   151  }
   152  
   153  func answerDiffLayersQuery(backend Backend, query *GetDiffLayersPacket) []rlp.RawValue {
   154  	// Gather blocks until the fetch or network limits is reached
   155  	var (
   156  		bytes      int
   157  		diffLayers []rlp.RawValue
   158  	)
   159  	// Need avoid transfer huge package
   160  	for lookups, hash := range query.BlockHashes {
   161  		if bytes >= softResponseLimit || len(diffLayers) >= maxDiffLayerServe ||
   162  			lookups >= 2*maxDiffLayerServe {
   163  			break
   164  		}
   165  		if data := backend.Chain().GetDiffLayerRLP(hash); len(data) != 0 {
   166  			diffLayers = append(diffLayers, data)
   167  			bytes += len(data)
   168  		}
   169  	}
   170  	return diffLayers
   171  }
   172  
   173  // NodeInfo represents a short summary of the `diff` sub-protocol metadata
   174  // known about the host peer.
   175  type NodeInfo struct{}
   176  
   177  // nodeInfo retrieves some `diff` protocol metadata about the running host node.
   178  func nodeInfo(_ *core.BlockChain) *NodeInfo {
   179  	return &NodeInfo{}
   180  }