mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 21:08:10 -05:00
**What type of PR is this?** Tooling **What does the PR do?** Every call to `httputil.HandleError` must be followed by a `return` statement. It's easy to miss this during reviews, so having a static analyzer that enforces this will make our life easier. **Acknowledgements** - [x] I have read [CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md). - [x] I have included a uniquely named [changelog fragment file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd). - [x] I have added a description with sufficient context for reviewers to understand this PR. - [x] I have tested that my changes work as expected and I added a testing plan to the PR description (if applicable). --------- Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
236 lines
8.3 KiB
Go
236 lines
8.3 KiB
Go
package lightclient
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/OffchainLabs/prysm/v7/api"
|
|
"github.com/OffchainLabs/prysm/v7/api/server/structs"
|
|
lightclient "github.com/OffchainLabs/prysm/v7/beacon-chain/light-client"
|
|
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/shared"
|
|
"github.com/OffchainLabs/prysm/v7/config/params"
|
|
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
|
|
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
|
|
"github.com/OffchainLabs/prysm/v7/network/httputil"
|
|
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v7/time/slots"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/pkg/errors"
|
|
ssz "github.com/prysmaticlabs/fastssz"
|
|
)
|
|
|
|
// GetLightClientBootstrap - implements https://github.com/ethereum/beacon-APIs/blob/263f4ed6c263c967f13279c7a9f5629b51c5fc55/apis/beacon/light_client/bootstrap.yaml
|
|
func (s *Server) GetLightClientBootstrap(w http.ResponseWriter, req *http.Request) {
|
|
// Prepare
|
|
ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientBootstrap")
|
|
defer span.End()
|
|
|
|
// Get the block
|
|
blockRootParam, err := hexutil.Decode(req.PathValue("block_root"))
|
|
if err != nil {
|
|
httputil.HandleError(w, "Invalid block root: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
blockRoot := bytesutil.ToBytes32(blockRootParam)
|
|
bootstrap, err := s.LCStore.LightClientBootstrap(ctx, blockRoot)
|
|
if err != nil {
|
|
if errors.Is(err, lightclient.ErrLightClientBootstrapNotFound) {
|
|
httputil.HandleError(w, "Light client bootstrap not found", http.StatusNotFound)
|
|
} else {
|
|
httputil.HandleError(w, "Could not get light client bootstrap: "+err.Error(), http.StatusInternalServerError)
|
|
}
|
|
return
|
|
}
|
|
|
|
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)
|
|
} else {
|
|
data, err := structs.LightClientBootstrapFromConsensus(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
|
|
func (s *Server) GetLightClientUpdatesByRange(w http.ResponseWriter, req *http.Request) {
|
|
ctx, span := trace.StartSpan(req.Context(), "beacon.GetLightClientUpdatesByRange")
|
|
defer span.End()
|
|
|
|
config := params.BeaconConfig()
|
|
|
|
_, count, gotCount := shared.UintFromQuery(w, req, "count", true)
|
|
if !gotCount {
|
|
return
|
|
} else if count == 0 {
|
|
httputil.HandleError(w, fmt.Sprintf("Got invalid 'count' query variable '%d': count must be greater than 0", count), http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if count > config.MaxRequestLightClientUpdates {
|
|
count = config.MaxRequestLightClientUpdates
|
|
}
|
|
|
|
_, startPeriod, gotStartPeriod := shared.UintFromQuery(w, req, "start_period", true)
|
|
if !gotStartPeriod {
|
|
return
|
|
}
|
|
|
|
if startPeriod*uint64(config.EpochsPerSyncCommitteePeriod) < uint64(config.AltairForkEpoch) {
|
|
httputil.HandleError(w, "Invalid 'start_period': before Altair fork", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
endPeriod := startPeriod + count - 1
|
|
|
|
headBlock, err := s.HeadFetcher.HeadBlock(ctx)
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not get head block: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// get updates
|
|
updates, err := s.LCStore.LightClientUpdates(ctx, startPeriod, endPeriod, headBlock)
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not get light client updates from DB: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if httputil.RespondWithSsz(req) {
|
|
w.Header().Set("Content-Type", "application/octet-stream")
|
|
|
|
for _, update := range updates {
|
|
if ctx.Err() != nil {
|
|
httputil.HandleError(w, "Context error: "+ctx.Err().Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
updateSlot := update.AttestedHeader().Beacon().Slot
|
|
updateEpoch := slots.ToEpoch(updateSlot)
|
|
updateEntry := params.GetNetworkScheduleEntry(updateEpoch)
|
|
updateSSZ, err := update.MarshalSSZ()
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not marshal update to SSZ: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var chunkLength []byte
|
|
chunkLength = ssz.MarshalUint64(chunkLength, uint64(len(updateSSZ)+4))
|
|
if _, err := w.Write(chunkLength); err != nil {
|
|
httputil.HandleError(w, "Could not write chunk length: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if _, err := w.Write(updateEntry.ForkDigest[:]); err != nil {
|
|
httputil.HandleError(w, "Could not write fork digest: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if _, err := w.Write(updateSSZ); err != nil {
|
|
httputil.HandleError(w, "Could not write update SSZ: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
updatesResponses := make([]*structs.LightClientUpdateResponse, 0, len(updates))
|
|
|
|
for _, update := range updates {
|
|
if ctx.Err() != nil {
|
|
httputil.HandleError(w, "Context error: "+ctx.Err().Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
updateJson, err := structs.LightClientUpdateFromConsensus(update)
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not convert light client update: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
updateResponse := &structs.LightClientUpdateResponse{
|
|
Version: version.String(update.Version()),
|
|
Data: updateJson,
|
|
}
|
|
updatesResponses = append(updatesResponses, updateResponse)
|
|
}
|
|
|
|
httputil.WriteJson(w, updatesResponses)
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
_, span := trace.StartSpan(req.Context(), "beacon.GetLightClientFinalityUpdate")
|
|
defer span.End()
|
|
|
|
update := s.LCStore.LastFinalityUpdate()
|
|
if update == nil {
|
|
httputil.HandleError(w, "No light client finality update available", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.Header().Set(api.VersionHeader, version.String(update.Version()))
|
|
if httputil.RespondWithSsz(req) {
|
|
data, err := update.MarshalSSZ()
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not marshal finality update to SSZ: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
httputil.WriteSsz(w, data)
|
|
} else {
|
|
data, err := structs.LightClientFinalityUpdateFromConsensus(update)
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not convert light client finality update to API struct: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
response := &structs.LightClientFinalityUpdateResponse{
|
|
Version: version.String(update.Version()),
|
|
Data: data,
|
|
}
|
|
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) {
|
|
_, span := trace.StartSpan(req.Context(), "beacon.GetLightClientOptimisticUpdate")
|
|
defer span.End()
|
|
|
|
update := s.LCStore.LastOptimisticUpdate()
|
|
if update == nil {
|
|
httputil.HandleError(w, "No light client optimistic update available", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
w.Header().Set(api.VersionHeader, version.String(update.Version()))
|
|
if httputil.RespondWithSsz(req) {
|
|
data, err := update.MarshalSSZ()
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not marshal optimistic update to SSZ: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
httputil.WriteSsz(w, data)
|
|
} else {
|
|
data, err := structs.LightClientOptimisticUpdateFromConsensus(update)
|
|
if err != nil {
|
|
httputil.HandleError(w, "Could not convert light client optimistic update to API struct: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
response := &structs.LightClientOptimisticUpdateResponse{
|
|
Version: version.String(update.Version()),
|
|
Data: data,
|
|
}
|
|
httputil.WriteJson(w, response)
|
|
}
|
|
}
|