github.com/amazechain/amc@v0.1.3/internal/sync/rpc.go (about)

     1  package sync
     2  
     3  import (
     4  	"context"
     5  	"github.com/amazechain/amc/internal/p2p"
     6  	p2ptypes "github.com/amazechain/amc/internal/p2p/types"
     7  	"github.com/amazechain/amc/log"
     8  	"reflect"
     9  	"runtime/debug"
    10  	"time"
    11  
    12  	libp2pcore "github.com/libp2p/go-libp2p/core"
    13  	"github.com/libp2p/go-libp2p/core/network"
    14  	"github.com/libp2p/go-libp2p/core/protocol"
    15  	ssz "github.com/prysmaticlabs/fastssz"
    16  	"go.opencensus.io/trace"
    17  )
    18  
    19  // Time to first byte timeout. The maximum time to wait for first byte of
    20  // request response (time-to-first-byte). The client is expected to give up if
    21  // they don't receive the first byte within 5 seconds.
    22  //var ttfbTimeout = ttfbTimeout
    23  
    24  // respTimeout is the maximum time for complete response transfer.
    25  //var respTimeout = params.BeaconNetworkConfig().RespTimeout
    26  
    27  // rpcHandler is responsible for handling and responding to any incoming message.
    28  // This method may return an error to internal monitoring, but the error will
    29  // not be relayed to the peer.
    30  type rpcHandler func(context.Context, interface{}, libp2pcore.Stream) error
    31  
    32  // registerRPCHandlers for p2p RPC.
    33  func (s *Service) registerRPCHandlers() {
    34  	s.registerRPC(
    35  		p2p.RPCStatusTopicV1,
    36  		s.statusRPCHandler,
    37  	)
    38  	s.registerRPC(
    39  		p2p.RPCGoodByeTopicV1,
    40  		s.goodbyeRPCHandler,
    41  	)
    42  	s.registerRPC(
    43  		p2p.RPCPingTopicV1,
    44  		s.pingHandler,
    45  	)
    46  	s.registerRPC(
    47  		p2p.RPCBodiesDataTopicV1,
    48  		s.bodiesByRangeRPCHandler,
    49  	)
    50  }
    51  
    52  // Remove all Stream handlers
    53  func (s *Service) unregisterHandlers() {
    54  	fullBodiesRangeTopic := p2p.RPCBodiesDataTopicV1 + s.cfg.p2p.Encoding().ProtocolSuffix()
    55  	fullStatusTopic := p2p.RPCStatusTopicV1 + s.cfg.p2p.Encoding().ProtocolSuffix()
    56  	fullGoodByeTopic := p2p.RPCGoodByeTopicV1 + s.cfg.p2p.Encoding().ProtocolSuffix()
    57  	fullPingTopic := p2p.RPCPingTopicV1 + s.cfg.p2p.Encoding().ProtocolSuffix()
    58  
    59  	s.cfg.p2p.Host().RemoveStreamHandler(protocol.ID(fullBodiesRangeTopic))
    60  	s.cfg.p2p.Host().RemoveStreamHandler(protocol.ID(fullStatusTopic))
    61  	s.cfg.p2p.Host().RemoveStreamHandler(protocol.ID(fullGoodByeTopic))
    62  	s.cfg.p2p.Host().RemoveStreamHandler(protocol.ID(fullPingTopic))
    63  }
    64  
    65  // registerRPC for a given topic with an expected protobuf message type.
    66  func (s *Service) registerRPC(baseTopic string, handle rpcHandler) {
    67  	topic := baseTopic + s.cfg.p2p.Encoding().ProtocolSuffix()
    68  	s.cfg.p2p.SetStreamHandler(topic, func(stream network.Stream) {
    69  		defer func() {
    70  			if r := recover(); r != nil {
    71  				log.Error("Panic occurred", "topic", topic, "err", r)
    72  				log.Errorf("%s", debug.Stack())
    73  			}
    74  		}()
    75  		ctx, cancel := context.WithTimeout(s.ctx, ttfbTimeout)
    76  		defer cancel()
    77  
    78  		// Resetting after closing is a no-op so defer a reset in case something goes wrong.
    79  		// It's up to the handler to Close the stream (send an EOF) if
    80  		// it successfully writes a response. We don't blindly call
    81  		// Close here because we may have only written a partial
    82  		// response.
    83  		defer func() {
    84  			_err := stream.Reset()
    85  			_ = _err
    86  		}()
    87  
    88  		ctx, span := trace.StartSpan(ctx, "sync.rpc")
    89  		defer span.End()
    90  		span.AddAttributes(trace.StringAttribute("topic", topic))
    91  		span.AddAttributes(trace.StringAttribute("peer", stream.Conn().RemotePeer().Pretty()))
    92  		//log := log.WithField("peer", stream.Conn().RemotePeer().Pretty()).WithField("topic", string(stream.Protocol()))
    93  
    94  		// Check before hand that peer is valid.
    95  		if s.cfg.p2p.Peers().IsBad(stream.Conn().RemotePeer()) {
    96  			if err := s.sendGoodByeAndDisconnect(ctx, p2ptypes.GoodbyeCodeBanned, stream.Conn().RemotePeer()); err != nil {
    97  				log.Debug("Could not disconnect from peer", "peer", stream.Conn().RemotePeer().Pretty(), "topic", stream.Protocol(), "err", err)
    98  			}
    99  			return
   100  		}
   101  		// Validate request according to peer limits.
   102  		if err := s.rateLimiter.validateRawRpcRequest(stream); err != nil {
   103  			log.Debug("Could not validate rpc request from peer", "peer", stream.Conn().RemotePeer().Pretty(), "topic", stream.Protocol(), "err", err)
   104  			return
   105  		}
   106  		s.rateLimiter.addRawStream(stream)
   107  
   108  		if err := stream.SetReadDeadline(time.Now().Add(ttfbTimeout)); err != nil {
   109  			log.Debug("Could not set stream read deadline", "peer", stream.Conn().RemotePeer().Pretty(), "topic", stream.Protocol(), "err", err)
   110  			return
   111  		}
   112  
   113  		base, ok := p2p.RPCTopicMappings[baseTopic]
   114  		if !ok {
   115  			log.Errorf("Could not retrieve base message for topic %s", baseTopic)
   116  			return
   117  		}
   118  		t := reflect.TypeOf(base)
   119  		// Copy Base
   120  		base = reflect.New(t)
   121  
   122  		// Increment message received counter.
   123  		messageReceivedCounter.WithLabelValues(topic).Inc()
   124  
   125  		// Given we have an input argument that can be pointer or the actual object, this gives us
   126  		// a way to check for its reflect.Kind and based on the result, we can decode
   127  		// accordingly.
   128  		if t.Kind() == reflect.Ptr {
   129  			msg, ok := reflect.New(t.Elem()).Interface().(ssz.Unmarshaler)
   130  			if !ok {
   131  				log.Errorf("message of %T does not support marshaller interface", msg)
   132  				return
   133  			}
   134  			if err := s.cfg.p2p.Encoding().DecodeWithMaxLength(stream, msg); err != nil {
   135  				log.Debug("Could not decode stream message", "topic", topic, "err", err)
   136  				//tracing.AnnotateError(span, err)
   137  				s.cfg.p2p.Peers().Scorers().BadResponsesScorer().Increment(stream.Conn().RemotePeer())
   138  				return
   139  			}
   140  			if err := handle(ctx, msg, stream); err != nil {
   141  				messageFailedProcessingCounter.WithLabelValues(topic).Inc()
   142  				if err != p2ptypes.ErrWrongForkDigestVersion {
   143  					log.Debug("Could not handle p2p RPC", "err", err)
   144  				}
   145  				//tracing.AnnotateError(span, err)
   146  			}
   147  		} else {
   148  			nTyp := reflect.New(t)
   149  			msg, ok := nTyp.Interface().(ssz.Unmarshaler)
   150  			if !ok {
   151  				log.Errorf("message of %T does not support marshaller interface", msg)
   152  				return
   153  			}
   154  			if err := s.cfg.p2p.Encoding().DecodeWithMaxLength(stream, msg); err != nil {
   155  				log.Debug("Could not decode stream message", "topic", topic, "err", err)
   156  				//tracing.AnnotateError(span, err)
   157  				s.cfg.p2p.Peers().Scorers().BadResponsesScorer().Increment(stream.Conn().RemotePeer())
   158  				return
   159  			}
   160  			if err := handle(ctx, nTyp.Elem().Interface(), stream); err != nil {
   161  				messageFailedProcessingCounter.WithLabelValues(topic).Inc()
   162  				if err != p2ptypes.ErrWrongForkDigestVersion {
   163  					log.Debug("Could not handle p2p RPC", "topic", topic, "err", err)
   164  				}
   165  				//tracing.AnnotateError(span, err)
   166  			}
   167  		}
   168  	})
   169  }