mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-08 18:51:19 +00:00
2806326155
Co-authored-by: Radosław Kapka <rkapka@wp.pl>
177 lines
5.6 KiB
Go
177 lines
5.6 KiB
Go
package validator
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/helpers"
|
|
"github.com/prysmaticlabs/prysm/v4/beacon-chain/rpc/eth/shared"
|
|
statenative "github.com/prysmaticlabs/prysm/v4/beacon-chain/state/state-native"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v4/consensus-types/validator"
|
|
http2 "github.com/prysmaticlabs/prysm/v4/network/http"
|
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/eth/v1"
|
|
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v4/time/slots"
|
|
"go.opencensus.io/trace"
|
|
)
|
|
|
|
type ValidatorCountResponse struct {
|
|
ExecutionOptimistic string `json:"execution_optimistic"`
|
|
Finalized string `json:"finalized"`
|
|
Data []*ValidatorCount `json:"data"`
|
|
}
|
|
|
|
type ValidatorCount struct {
|
|
Status string `json:"status"`
|
|
Count string `json:"count"`
|
|
}
|
|
|
|
// GetValidatorCount is a HTTP handler that serves the GET /eth/v1/beacon/states/{state_id}/validator_count endpoint.
|
|
// It returns the total validator count according to the given statuses provided as a query parameter.
|
|
//
|
|
// The state ID is expected to be a valid Beacon Chain state identifier.
|
|
// The status query parameter can be an array of strings with the following values: pending_initialized, pending_queued, active_ongoing,
|
|
// active_exiting, active_slashed, exited_unslashed, exited_slashed, withdrawal_possible, withdrawal_done, active, pending, exited, withdrawal.
|
|
// The response is a JSON object containing the total validator count for the specified statuses.
|
|
//
|
|
// Example usage:
|
|
//
|
|
// GET /eth/v1/beacon/states/12345/validator_count?status=active&status=pending
|
|
//
|
|
// The above request will return a JSON response like:
|
|
//
|
|
// {
|
|
// "execution_optimistic": "false",
|
|
// "finalized": "true",
|
|
// "data": [
|
|
// {
|
|
// "status": "active",
|
|
// "count": "13"
|
|
// },
|
|
// {
|
|
// "status": "pending",
|
|
// "count": "6"
|
|
// }
|
|
// ]
|
|
// }
|
|
func (vs *Server) GetValidatorCount(w http.ResponseWriter, r *http.Request) {
|
|
ctx, span := trace.StartSpan(r.Context(), "beacon.GetValidatorCount")
|
|
defer span.End()
|
|
|
|
stateID := mux.Vars(r)["state_id"]
|
|
|
|
isOptimistic, err := helpers.IsOptimistic(ctx, []byte(stateID), vs.OptimisticModeFetcher, vs.Stater, vs.ChainInfoFetcher, vs.BeaconDB)
|
|
if err != nil {
|
|
errJson := &http2.DefaultErrorJson{
|
|
Message: fmt.Sprintf("could not check if slot's block is optimistic: %v", err),
|
|
Code: http.StatusInternalServerError,
|
|
}
|
|
http2.WriteError(w, errJson)
|
|
return
|
|
}
|
|
|
|
st, err := vs.Stater.State(ctx, []byte(stateID))
|
|
if err != nil {
|
|
shared.WriteStateFetchError(w, err)
|
|
return
|
|
}
|
|
|
|
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
|
if err != nil {
|
|
errJson := &http2.DefaultErrorJson{
|
|
Message: fmt.Sprintf("could not calculate root of latest block header: %v", err),
|
|
Code: http.StatusInternalServerError,
|
|
}
|
|
http2.WriteError(w, errJson)
|
|
return
|
|
}
|
|
|
|
isFinalized := vs.FinalizationFetcher.IsFinalized(ctx, blockRoot)
|
|
|
|
var statusVals []validator.ValidatorStatus
|
|
for _, status := range r.URL.Query()["status"] {
|
|
statusVal, ok := ethpb.ValidatorStatus_value[strings.ToUpper(status)]
|
|
if !ok {
|
|
errJson := &http2.DefaultErrorJson{
|
|
Message: fmt.Sprintf("invalid status query parameter: %v", status),
|
|
Code: http.StatusBadRequest,
|
|
}
|
|
http2.WriteError(w, errJson)
|
|
return
|
|
}
|
|
|
|
statusVals = append(statusVals, validator.ValidatorStatus(statusVal))
|
|
}
|
|
|
|
// If no status was provided then consider all the statuses to return validator count for each status.
|
|
if len(statusVals) == 0 {
|
|
for _, val := range ethpb.ValidatorStatus_value {
|
|
statusVals = append(statusVals, validator.ValidatorStatus(val))
|
|
}
|
|
}
|
|
|
|
epoch := slots.ToEpoch(st.Slot())
|
|
valCount, err := validatorCountByStatus(st.Validators(), statusVals, epoch)
|
|
if err != nil {
|
|
errJson := &http2.DefaultErrorJson{
|
|
Message: fmt.Sprintf("could not get validator count: %v", err),
|
|
Code: http.StatusInternalServerError,
|
|
}
|
|
http2.WriteError(w, errJson)
|
|
return
|
|
}
|
|
|
|
valCountResponse := &ValidatorCountResponse{
|
|
ExecutionOptimistic: strconv.FormatBool(isOptimistic),
|
|
Finalized: strconv.FormatBool(isFinalized),
|
|
Data: valCount,
|
|
}
|
|
|
|
http2.WriteJson(w, valCountResponse)
|
|
}
|
|
|
|
// validatorCountByStatus returns a slice of validator count for each status in the given epoch.
|
|
func validatorCountByStatus(validators []*eth.Validator, statuses []validator.ValidatorStatus, epoch primitives.Epoch) ([]*ValidatorCount, error) {
|
|
countByStatus := make(map[validator.ValidatorStatus]uint64)
|
|
for _, val := range validators {
|
|
readOnlyVal, err := statenative.NewValidator(val)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not convert validator: %v", err)
|
|
}
|
|
valStatus, err := helpers.ValidatorStatus(readOnlyVal, epoch)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not get validator status: %v", err)
|
|
}
|
|
valSubStatus, err := helpers.ValidatorSubStatus(readOnlyVal, epoch)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not get validator sub status: %v", err)
|
|
}
|
|
|
|
for _, status := range statuses {
|
|
if valStatus == status || valSubStatus == status {
|
|
countByStatus[status]++
|
|
}
|
|
}
|
|
}
|
|
|
|
var resp []*ValidatorCount
|
|
for status, count := range countByStatus {
|
|
resp = append(resp, &ValidatorCount{
|
|
Status: status.String(),
|
|
Count: strconv.FormatUint(count, 10),
|
|
})
|
|
}
|
|
|
|
// Sort the response slice according to status strings for deterministic ordering of validator count response.
|
|
sort.Slice(resp, func(i, j int) bool {
|
|
return resp[i].Status < resp[j].Status
|
|
})
|
|
|
|
return resp, nil
|
|
}
|