package sync import ( "context" "fmt" "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p" "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/types" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/math" "github.com/OffchainLabs/prysm/v7/monitoring/tracing" "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" libp2pcore "github.com/libp2p/go-libp2p/core" ) // lightClientBootstrapRPCHandler handles the /eth2/beacon_chain/req/light_client_bootstrap/1/ RPC request. func (s *Service) lightClientBootstrapRPCHandler(ctx context.Context, msg any, stream libp2pcore.Stream) error { ctx, span := trace.StartSpan(ctx, "sync.lightClientBootstrapRPCHandler") defer span.End() ctx, cancel := context.WithTimeout(ctx, ttfbTimeout) defer cancel() logger := log.WithField("handler", p2p.LightClientBootstrapName[1:]) SetRPCStreamDeadlines(stream) if err := s.rateLimiter.validateRequest(stream, 1); err != nil { logger.WithError(err).Error("Cannot validate request") return err } s.rateLimiter.add(stream, 1) rawMsg, ok := msg.(*[fieldparams.RootLength]byte) if !ok { logger.Error("Message is not *types.LightClientBootstrapReq") return fmt.Errorf("message is not type %T", &[fieldparams.RootLength]byte{}) } blkRoot := *rawMsg bootstrap, err := s.lcStore.LightClientBootstrap(ctx, blkRoot) if err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("Cannot bootstrap light client") return err } if bootstrap == nil { s.writeErrorResponseToStream(responseCodeResourceUnavailable, types.ErrResourceUnavailable.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error(fmt.Sprintf("nil bootstrap for root %#x", blkRoot)) return err } SetStreamWriteDeadline(stream, defaultWriteDuration) if err = WriteLightClientBootstrapChunk(stream, s.cfg.clock, s.cfg.p2p.Encoding(), bootstrap); err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("WriteLightClientBootstrapChunk") return err } logger.Info("lightClientBootstrapRPCHandler completed") closeStream(stream, logger) return nil } // lightClientUpdatesByRangeRPCHandler handles the /eth2/beacon_chain/req/light_client_updates_by_range/1/ RPC request. func (s *Service) lightClientUpdatesByRangeRPCHandler(ctx context.Context, msg any, stream libp2pcore.Stream) error { ctx, span := trace.StartSpan(ctx, "sync.lightClientUpdatesByRangeRPCHandler") defer span.End() ctx, cancel := context.WithTimeout(ctx, ttfbTimeout) defer cancel() logger := log.WithField("handler", p2p.LightClientUpdatesByRangeName[1:]) remotePeer := stream.Conn().RemotePeer() SetRPCStreamDeadlines(stream) if err := s.rateLimiter.validateRequest(stream, 1); err != nil { logger.WithError(err).Error("Cannot validate request") return err } s.rateLimiter.add(stream, 1) r, ok := msg.(*eth.LightClientUpdatesByRangeRequest) if !ok { logger.Error("Message is not *eth.LightClientUpdatesByRangeReq") return fmt.Errorf("message is not type %T", ð.LightClientUpdatesByRangeRequest{}) } if r.Count == 0 { s.writeErrorResponseToStream(responseCodeInvalidRequest, "count is 0", stream) s.downscorePeer(remotePeer, "lightClientUpdatesByRangeRPCHandlerCount0") logger.Error("Count is 0") return nil } if r.Count > params.BeaconConfig().MaxRequestLightClientUpdates { r.Count = params.BeaconConfig().MaxRequestLightClientUpdates } endPeriod, err := math.Add64(r.StartPeriod, r.Count-1) if err != nil { s.writeErrorResponseToStream(responseCodeInvalidRequest, err.Error(), stream) s.downscorePeer(remotePeer, "lightClientUpdatesByRangeRPCHandlerEndPeriodOverflow") tracing.AnnotateError(span, err) logger.WithError(err).Error("End period overflows") return err } logger.Infof("LC: requesting updates by range (StartPeriod: %d, EndPeriod: %d)", r.StartPeriod, r.StartPeriod+r.Count-1) headBlock, err := s.cfg.chain.HeadBlock(ctx) if err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("Cannot retrieve head block") return err } updates, err := s.lcStore.LightClientUpdates(ctx, r.StartPeriod, endPeriod, headBlock) if err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("Cannot retrieve light client updates") return err } if len(updates) == 0 { s.writeErrorResponseToStream(responseCodeResourceUnavailable, types.ErrResourceUnavailable.Error(), stream) tracing.AnnotateError(span, err) logger.Debugf("No update available for start period %d", r.StartPeriod) return nil } for _, u := range updates { SetStreamWriteDeadline(stream, defaultWriteDuration) if err = WriteLightClientUpdateChunk(stream, s.cfg.clock, s.cfg.p2p.Encoding(), u); err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("WriteLightClientUpdateChunk") return err } s.rateLimiter.add(stream, 1) } logger.Info("lightClientUpdatesByRangeRPCHandler completed") closeStream(stream, logger) return nil } // lightClientFinalityUpdateRPCHandler handles the /eth2/beacon_chain/req/light_client_finality_update/1/ RPC request. func (s *Service) lightClientFinalityUpdateRPCHandler(ctx context.Context, _ any, stream libp2pcore.Stream) error { ctx, span := trace.StartSpan(ctx, "sync.lightClientFinalityUpdateRPCHandler") defer span.End() _, cancel := context.WithTimeout(ctx, ttfbTimeout) defer cancel() logger := log.WithField("handler", p2p.LightClientFinalityUpdateName[1:]) SetRPCStreamDeadlines(stream) if err := s.rateLimiter.validateRequest(stream, 1); err != nil { logger.WithError(err).Error("Cannot validate request") return err } s.rateLimiter.add(stream, 1) if s.lcStore.LastFinalityUpdate() == nil { s.writeErrorResponseToStream(responseCodeResourceUnavailable, types.ErrResourceUnavailable.Error(), stream) logger.Error("No finality update available") return nil } SetStreamWriteDeadline(stream, defaultWriteDuration) if err := WriteLightClientFinalityUpdateChunk(stream, s.cfg.clock, s.cfg.p2p.Encoding(), s.lcStore.LastFinalityUpdate()); err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("WriteLightClientFinalityUpdateChunk") return err } logger.Info("lightClientFinalityUpdateRPCHandler completed") closeStream(stream, logger) return nil } // lightClientOptimisticUpdateRPCHandler handles the /eth2/beacon_chain/req/light_client_optimistic_update/1/ RPC request. func (s *Service) lightClientOptimisticUpdateRPCHandler(ctx context.Context, _ any, stream libp2pcore.Stream) error { ctx, span := trace.StartSpan(ctx, "sync.lightClientOptimisticUpdateRPCHandler") defer span.End() _, cancel := context.WithTimeout(ctx, ttfbTimeout) defer cancel() logger := log.WithField("handler", p2p.LightClientOptimisticUpdateName[1:]) logger.Info("lightClientOptimisticUpdateRPCHandler invoked") SetRPCStreamDeadlines(stream) if err := s.rateLimiter.validateRequest(stream, 1); err != nil { logger.WithError(err).Error("Cannot validate request") return err } s.rateLimiter.add(stream, 1) if s.lcStore.LastOptimisticUpdate() == nil { s.writeErrorResponseToStream(responseCodeResourceUnavailable, types.ErrResourceUnavailable.Error(), stream) logger.Error("No optimistic update available") return nil } SetStreamWriteDeadline(stream, defaultWriteDuration) if err := WriteLightClientOptimisticUpdateChunk(stream, s.cfg.clock, s.cfg.p2p.Encoding(), s.lcStore.LastOptimisticUpdate()); err != nil { s.writeErrorResponseToStream(responseCodeServerError, types.ErrGeneric.Error(), stream) tracing.AnnotateError(span, err) logger.WithError(err).Error("WriteLightClientOptimisticUpdateChunk") return err } logger.Info("lightClientOptimisticUpdateRPCHandler completed") closeStream(stream, logger) return nil }