Skip to content

Commit

Permalink
HTTP implementation of voluntary exit pool endpoints (#12777)
Browse files Browse the repository at this point in the history
* impl

* protos

* tests

* review

* test fix
  • Loading branch information
rkapka authored Aug 23, 2023
1 parent da244c9 commit f0d5425
Show file tree
Hide file tree
Showing 15 changed files with 789 additions and 1,007 deletions.
4 changes: 0 additions & 4 deletions beacon-chain/rpc/apimiddleware/endpoint_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ func (_ *BeaconEndpointFactory) Paths() []string {
"/eth/v1/beacon/blinded_blocks/{block_id}",
"/eth/v1/beacon/pool/attester_slashings",
"/eth/v1/beacon/pool/proposer_slashings",
"/eth/v1/beacon/pool/voluntary_exits",
"/eth/v1/beacon/pool/bls_to_execution_changes",
"/eth/v1/beacon/pool/sync_committees",
"/eth/v1/beacon/pool/bls_to_execution_changes",
Expand Down Expand Up @@ -139,9 +138,6 @@ func (_ *BeaconEndpointFactory) Create(path string) (*apimiddleware.Endpoint, er
case "/eth/v1/beacon/pool/proposer_slashings":
endpoint.PostRequest = &ProposerSlashingJson{}
endpoint.GetResponse = &ProposerSlashingsPoolResponseJson{}
case "/eth/v1/beacon/pool/voluntary_exits":
endpoint.PostRequest = &SignedVoluntaryExitJson{}
endpoint.GetResponse = &VoluntaryExitsPoolResponseJson{}
case "/eth/v1/beacon/pool/bls_to_execution_changes":
endpoint.PostRequest = &SubmitBLSToExecutionChangesRequest{}
endpoint.GetResponse = &BLSToExecutionChangesPoolResponseJson{}
Expand Down
4 changes: 0 additions & 4 deletions beacon-chain/rpc/apimiddleware/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,6 @@ type ProposerSlashingsPoolResponseJson struct {
Data []*ProposerSlashingJson `json:"data"`
}

type VoluntaryExitsPoolResponseJson struct {
Data []*SignedVoluntaryExitJson `json:"data"`
}

type SubmitSyncCommitteeSignaturesRequestJson struct {
Data []*SyncCommitteeMessageJson `json:"data"`
}
Expand Down
100 changes: 94 additions & 6 deletions beacon-chain/rpc/eth/beacon/handlers_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ package beacon
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"

"github.com/go-playground/validator/v10"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/feed/operation"
corehelpers "github.com/prysmaticlabs/prysm/v4/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
state_native "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
Expand Down Expand Up @@ -71,12 +75,13 @@ func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttestations")
defer span.End()

if r.Body == http.NoBody {
var req SubmitAttestationsRequest
err := json.NewDecoder(r.Body).Decode(&req.Data)
switch {
case err == io.EOF:
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
var req SubmitAttestationsRequest
if err := json.NewDecoder(r.Body).Decode(&req.Data); err != nil {
case err != nil:
http2.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
Expand Down Expand Up @@ -142,11 +147,11 @@ func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {

if corehelpers.IsAggregated(att) {
if err = s.AttestationsPool.SaveAggregatedAttestation(att); err != nil {
log.WithError(err).Error("could not save aggregated att")
log.WithError(err).Error("could not save aggregated attestation")
}
} else {
if err = s.AttestationsPool.SaveUnaggregatedAttestation(att); err != nil {
log.WithError(err).Error("could not save unaggregated att")
log.WithError(err).Error("could not save unaggregated attestation")
}
}
}
Expand All @@ -168,3 +173,86 @@ func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {
http2.WriteError(w, failuresErr)
}
}

// ListVoluntaryExits retrieves voluntary exits known by the node but
// not necessarily incorporated into any block.
func (s *Server) ListVoluntaryExits(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "beacon.ListVoluntaryExits")
defer span.End()

sourceExits, err := s.VoluntaryExitsPool.PendingExits()
if err != nil {
http2.HandleError(w, "Could not get exits from the pool: "+err.Error(), http.StatusInternalServerError)
return
}
exits := make([]*shared.SignedVoluntaryExit, len(sourceExits))
for i, e := range sourceExits {
exits[i] = shared.SignedVoluntaryExitFromConsensus(e)
}

http2.WriteJson(w, &ListVoluntaryExitsResponse{Data: exits})
}

// SubmitVoluntaryExit submits SignedVoluntaryExit object to node's pool
// and if passes validation node MUST broadcast it to network.
func (s *Server) SubmitVoluntaryExit(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitVoluntaryExit")
defer span.End()

var req shared.SignedVoluntaryExit
err := json.NewDecoder(r.Body).Decode(&req)
switch {
case err == io.EOF:
http2.HandleError(w, "No data submitted", http.StatusBadRequest)
return
case err != nil:
http2.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
validate := validator.New()
if err := validate.Struct(req); err != nil {
http2.HandleError(w, err.Error(), http.StatusBadRequest)
return
}

exit, err := req.ToConsensus()
if err != nil {
http2.HandleError(w, "Could not convert request exit to consensus exit: "+err.Error(), http.StatusBadRequest)
return
}

headState, err := s.ChainInfoFetcher.HeadState(ctx)
if err != nil {
http2.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError)
return
}
epochStart, err := slots.EpochStart(exit.Exit.Epoch)
if err != nil {
http2.HandleError(w, "Could not get epoch start: "+err.Error(), http.StatusInternalServerError)
return
}
headState, err = transition.ProcessSlotsIfPossible(ctx, headState, epochStart)
if err != nil {
http2.HandleError(w, "Could not process slots: "+err.Error(), http.StatusInternalServerError)
return
}
val, err := headState.ValidatorAtIndexReadOnly(exit.Exit.ValidatorIndex)
if err != nil {
if outOfRangeErr, ok := err.(*state_native.ValidatorIndexOutOfRangeError); ok {
http2.HandleError(w, "Could not get exiting validator: "+outOfRangeErr.Error(), http.StatusBadRequest)
return
}
http2.HandleError(w, "Could not get validator: "+err.Error(), http.StatusInternalServerError)
return
}
if err = blocks.VerifyExitAndSignature(val, headState.Slot(), headState.Fork(), exit, headState.GenesisValidatorsRoot()); err != nil {
http2.HandleError(w, "Invalid exit: "+err.Error(), http.StatusBadRequest)
return
}

s.VoluntaryExitsPool.InsertVoluntaryExit(exit)
if err = s.Broadcaster.Broadcast(ctx, exit); err != nil {
http2.HandleError(w, "Could not broadcast exit: "+err.Error(), http.StatusInternalServerError)
return
}
}
Loading

0 comments on commit f0d5425

Please sign in to comment.