diff --git a/beacon-chain/node/node.go b/beacon-chain/node/node.go index 3b08cc3105b..673cbdd7ada 100644 --- a/beacon-chain/node/node.go +++ b/beacon-chain/node/node.go @@ -1008,6 +1008,7 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error { BlobStorage: b.BlobStorage, TrackedValidatorsCache: b.trackedValidatorsCache, PayloadIDCache: b.payloadIDCache, + LCStore: b.lcStore, }) return b.services.RegisterService(rpcService) diff --git a/beacon-chain/rpc/BUILD.bazel b/beacon-chain/rpc/BUILD.bazel index dbb213a6559..19cc6384ed6 100644 --- a/beacon-chain/rpc/BUILD.bazel +++ b/beacon-chain/rpc/BUILD.bazel @@ -22,6 +22,7 @@ go_library( "//beacon-chain/db:go_default_library", "//beacon-chain/db/filesystem:go_default_library", "//beacon-chain/execution:go_default_library", + "//beacon-chain/light-client:go_default_library", "//beacon-chain/operations/attestations:go_default_library", "//beacon-chain/operations/blstoexec:go_default_library", "//beacon-chain/operations/slashings:go_default_library", diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 9dae9c032a3..c87068e0d49 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -872,6 +872,7 @@ func (s *Service) lightClientEndpoints(blocker lookup.Blocker, stater lookup.Sta HeadFetcher: s.cfg.HeadFetcher, ChainInfoFetcher: s.cfg.ChainInfoFetcher, BeaconDB: s.cfg.BeaconDB, + LCStore: s.cfg.LCStore, } const namespace = "lightclient" diff --git a/beacon-chain/rpc/eth/light-client/BUILD.bazel b/beacon-chain/rpc/eth/light-client/BUILD.bazel index 05112149cde..7b96ef1c2c0 100644 --- a/beacon-chain/rpc/eth/light-client/BUILD.bazel +++ b/beacon-chain/rpc/eth/light-client/BUILD.bazel @@ -15,6 +15,7 @@ go_library( "//beacon-chain/blockchain:go_default_library", "//beacon-chain/core/light-client:go_default_library", "//beacon-chain/db:go_default_library", + "//beacon-chain/light-client:go_default_library", "//beacon-chain/rpc/eth/shared:go_default_library", "//beacon-chain/rpc/lookup:go_default_library", "//beacon-chain/state:go_default_library", diff --git a/beacon-chain/rpc/eth/light-client/handlers.go b/beacon-chain/rpc/eth/light-client/handlers.go index 279a51acc64..230e3222162 100644 --- a/beacon-chain/rpc/eth/light-client/handlers.go +++ b/beacon-chain/rpc/eth/light-client/handlers.go @@ -3,13 +3,13 @@ package lightclient import ( "context" "fmt" - "math" "net/http" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/api" "github.com/prysmaticlabs/prysm/v5/api/server/structs" + lightclient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/eth/shared" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" @@ -45,18 +45,33 @@ func (s *Server) GetLightClientBootstrap(w http.ResponseWriter, req *http.Reques return } - bootstrap, err := newLightClientBootstrapFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), state, blk) + bootstrap, err := lightclient.NewLightClientBootstrapFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), state, blk) if err != nil { httputil.HandleError(w, "could not get light client bootstrap: "+err.Error(), http.StatusInternalServerError) return } - response := &structs.LightClientBootstrapResponse{ - Version: version.String(blk.Version()), - Data: bootstrap, - } - w.Header().Set(api.VersionHeader, version.String(version.Deneb)) - httputil.WriteJson(w, response) + w.Header().Set(api.VersionHeader, version.String(bootstrap.Version())) + + if httputil.RespondWithSsz(req) { + ssz, err := bootstrap.MarshalSSZ() + if err != nil { + httputil.HandleError(w, "could not marshal bootstrap to SSZ: "+err.Error(), http.StatusInternalServerError) + return + } + httputil.WriteSsz(w, ssz, "light_client_bootstrap.ssz") + } else { + data, err := structs.LightClientBootsrapFromConsensus(bootstrap) + if err != nil { + httputil.HandleError(w, "could not marshal bootstrap to JSON: "+err.Error(), http.StatusInternalServerError) + return + } + response := &structs.LightClientBootstrapResponse{ + Version: version.String(bootstrap.Version()), + Data: data, + } + httputil.WriteJson(w, response) + } } // GetLightClientUpdatesByRange - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/updates.yaml @@ -137,6 +152,7 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R return } + // TODO: we shouldn't append response objects in a loop updateResponse := &structs.LightClientUpdateResponse{ Version: version.String(update.Version()), Data: updateJson, @@ -150,105 +166,72 @@ func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.R // GetLightClientFinalityUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/finality_update.yaml func (s *Server) GetLightClientFinalityUpdate(w http.ResponseWriter, req *http.Request) { - ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientFinalityUpdate") + _, span := trace.StartSpan(req.Context(), "beacon.GetLightClientFinalityUpdate") defer span.End() - // Finality update needs super majority of sync committee signatures - minSyncCommitteeParticipants := float64(params.BeaconConfig().MinSyncCommitteeParticipants) - minSignatures := uint64(math.Ceil(minSyncCommitteeParticipants * 2 / 3)) + update := s.LCStore.LastLCFinalityUpdate - block, err := s.suitableBlock(ctx, minSignatures) - if !shared.WriteBlockFetchError(w, block, err) { + if update == nil { + httputil.HandleError(w, "No light client finality update available", http.StatusNotFound) return } - st, err := s.Stater.StateBySlot(ctx, block.Block().Slot()) - if err != nil { - httputil.HandleError(w, "Could not get state: "+err.Error(), http.StatusInternalServerError) - return - } + w.Header().Set(api.VersionHeader, version.String(update.Version())) - attestedRoot := block.Block().ParentRoot() - attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:]) - if !shared.WriteBlockFetchError(w, block, errors.Wrap(err, "could not get attested block")) { - return - } - attestedSlot := attestedBlock.Block().Slot() - attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot) - if err != nil { - httputil.HandleError(w, "Could not get attested state: "+err.Error(), http.StatusInternalServerError) - return - } - - var finalizedBlock interfaces.ReadOnlySignedBeaconBlock - finalizedCheckpoint := attestedState.FinalizedCheckpoint() - if finalizedCheckpoint == nil { - httputil.HandleError(w, "Attested state does not have a finalized checkpoint", http.StatusInternalServerError) - return - } - finalizedRoot := bytesutil.ToBytes32(finalizedCheckpoint.Root) - finalizedBlock, err = s.Blocker.Block(ctx, finalizedRoot[:]) - if !shared.WriteBlockFetchError(w, block, errors.Wrap(err, "could not get finalized block")) { - return - } - - update, err := newLightClientFinalityUpdateFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), st, block, attestedState, attestedBlock, finalizedBlock) - if err != nil { - httputil.HandleError(w, "Could not get light client finality update: "+err.Error(), http.StatusInternalServerError) - return - } - - response := &structs.LightClientFinalityUpdateResponse{ - Version: version.String(attestedState.Version()), - Data: update, + if httputil.RespondWithSsz(req) { + ssz, err := update.MarshalSSZ() + if err != nil { + httputil.HandleError(w, "could not marshal update to SSZ: "+err.Error(), http.StatusInternalServerError) + return + } + httputil.WriteSsz(w, ssz, "light_client_finality_update.ssz") + } else { + data, err := structs.LightClientFinalityUpdateFromConsensus(update) + if err != nil { + httputil.HandleError(w, "could not marshal update to JSON: "+err.Error(), http.StatusInternalServerError) + return + } + response := &structs.LightClientFinalityUpdateResponse{ + Version: version.String(update.Version()), + Data: data, + } + httputil.WriteJson(w, response) } - - httputil.WriteJson(w, response) } // GetLightClientOptimisticUpdate - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/optimistic_update.yaml func (s *Server) GetLightClientOptimisticUpdate(w http.ResponseWriter, req *http.Request) { - ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientOptimisticUpdate") + _, span := trace.StartSpan(req.Context(), "beacon.GetLightClientOptimisticUpdate") defer span.End() - block, err := s.suitableBlock(ctx, params.BeaconConfig().MinSyncCommitteeParticipants) - if !shared.WriteBlockFetchError(w, block, err) { - return - } - st, err := s.Stater.StateBySlot(ctx, block.Block().Slot()) - if err != nil { - httputil.HandleError(w, "could not get state: "+err.Error(), http.StatusInternalServerError) - return - } - attestedRoot := block.Block().ParentRoot() - attestedBlock, err := s.Blocker.Block(ctx, attestedRoot[:]) - if err != nil { - httputil.HandleError(w, "Could not get attested block: "+err.Error(), http.StatusInternalServerError) - return - } - if attestedBlock == nil { - httputil.HandleError(w, "Attested block is nil", http.StatusInternalServerError) - return - } - attestedSlot := attestedBlock.Block().Slot() - attestedState, err := s.Stater.StateBySlot(ctx, attestedSlot) - if err != nil { - httputil.HandleError(w, "Could not get attested state: "+err.Error(), http.StatusInternalServerError) - return - } + update := s.LCStore.LastLCOptimisticUpdate - update, err := newLightClientOptimisticUpdateFromBeaconState(ctx, s.ChainInfoFetcher.CurrentSlot(), st, block, attestedState, attestedBlock) - if err != nil { - httputil.HandleError(w, "Could not get light client optimistic update: "+err.Error(), http.StatusInternalServerError) + if update == nil { + httputil.HandleError(w, "No light client optimistic update available", http.StatusNotFound) return } - response := &structs.LightClientOptimisticUpdateResponse{ - Version: version.String(attestedState.Version()), - Data: update, - } + w.Header().Set(api.VersionHeader, version.String(update.Version())) - httputil.WriteJson(w, response) + if httputil.RespondWithSsz(req) { + ssz, err := update.MarshalSSZ() + if err != nil { + httputil.HandleError(w, "could not marshal update to SSZ: "+err.Error(), http.StatusInternalServerError) + return + } + httputil.WriteSsz(w, ssz, "light_client_optimistic_update.ssz") + } else { + data, err := structs.LightClientOptimisticUpdateFromConsensus(update) + if err != nil { + httputil.HandleError(w, "could not marshal update to JSON: "+err.Error(), http.StatusInternalServerError) + return + } + response := &structs.LightClientOptimisticUpdateResponse{ + Version: version.String(update.Version()), + Data: data, + } + httputil.WriteJson(w, response) + } } // suitableBlock returns the latest block that satisfies all criteria required for creating a new update diff --git a/beacon-chain/rpc/eth/light-client/server.go b/beacon-chain/rpc/eth/light-client/server.go index 84b061379fc..2157f2d0ce4 100644 --- a/beacon-chain/rpc/eth/light-client/server.go +++ b/beacon-chain/rpc/eth/light-client/server.go @@ -3,6 +3,7 @@ package lightclient import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" + light_client "github.com/prysmaticlabs/prysm/v5/beacon-chain/light-client" "github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/lookup" ) @@ -12,4 +13,5 @@ type Server struct { HeadFetcher blockchain.HeadFetcher ChainInfoFetcher blockchain.ChainInfoFetcher BeaconDB db.HeadAccessDatabase + LCStore *light_client.Store } diff --git a/beacon-chain/rpc/service.go b/beacon-chain/rpc/service.go index 4a56c0d4162..f9c86fac9bb 100644 --- a/beacon-chain/rpc/service.go +++ b/beacon-chain/rpc/service.go @@ -26,6 +26,7 @@ import ( "github.com/prysmaticlabs/prysm/v5/beacon-chain/db" "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/filesystem" "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution" + light_client "github.com/prysmaticlabs/prysm/v5/beacon-chain/light-client" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/blstoexec" "github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/slashings" @@ -140,6 +141,7 @@ type Config struct { BlobStorage *filesystem.BlobStorage TrackedValidatorsCache *cache.TrackedValidatorsCache PayloadIDCache *cache.PayloadIDCache + LCStore *light_client.Store } // NewService instantiates a new RPC service instance that will