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 }