mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-08 18:51:19 +00:00
f09fe4f038
* Reorganize Prysm custom endpoints * add deprecated paths * review
493 lines
12 KiB
Go
493 lines
12 KiB
Go
package beacon
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
neturl "net/url"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/api/server/structs"
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
|
|
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/lookup"
|
|
"github.com/prysmaticlabs/prysm/v5/network/httputil"
|
|
|
|
"github.com/gorilla/mux"
|
|
chainMock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
|
|
"github.com/prysmaticlabs/prysm/v5/beacon-chain/rpc/testutil"
|
|
"github.com/prysmaticlabs/prysm/v5/config/params"
|
|
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
|
|
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/require"
|
|
"github.com/prysmaticlabs/prysm/v5/testing/util"
|
|
)
|
|
|
|
func TestGetValidatorCountInvalidRequest(t *testing.T) {
|
|
st, _ := util.DeterministicGenesisState(t, 10)
|
|
stateIdCheckerStateFunc := func(_ context.Context, stateId []byte) (state.BeaconState, error) {
|
|
stateIdString := strings.ToLower(string(stateId))
|
|
switch stateIdString {
|
|
case "head", "genesis", "finalized", "justified":
|
|
return st, nil
|
|
default:
|
|
if len(stateId) == 32 {
|
|
return nil, nil
|
|
} else {
|
|
_, parseErr := strconv.ParseUint(stateIdString, 10, 64)
|
|
if parseErr != nil {
|
|
// ID format does not match any valid options.
|
|
e := lookup.NewStateIdParseError(parseErr)
|
|
return nil, &e
|
|
}
|
|
return st, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
stater lookup.Stater
|
|
status string
|
|
stateID string
|
|
expectedErrorMessage string
|
|
statusCode int
|
|
}{
|
|
{
|
|
name: "invalid status",
|
|
stater: &testutil.MockStater{
|
|
BeaconState: st,
|
|
},
|
|
status: "helloworld",
|
|
stateID: "head",
|
|
expectedErrorMessage: "invalid status query parameter",
|
|
statusCode: http.StatusBadRequest,
|
|
},
|
|
{
|
|
name: "invalid state ID",
|
|
stater: &testutil.MockStater{StateProviderFunc: stateIdCheckerStateFunc},
|
|
stateID: "helloworld",
|
|
expectedErrorMessage: "Invalid state ID",
|
|
statusCode: http.StatusBadRequest,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
chainService := &chainMock.ChainService{Optimistic: false, FinalizedRoots: make(map[[32]byte]bool)}
|
|
|
|
server := &Server{
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
Stater: test.stater,
|
|
}
|
|
|
|
testRouter := mux.NewRouter()
|
|
testRouter.HandleFunc("/eth/v1/beacon/states/{state_id}/validator_count", server.GetValidatorCount)
|
|
s := httptest.NewServer(testRouter)
|
|
defer s.Close()
|
|
|
|
queryParams := neturl.Values{}
|
|
queryParams.Add("status", test.status)
|
|
resp, err := http.Get(s.URL + fmt.Sprintf("/eth/v1/beacon/states/%s/validator_count?%s",
|
|
test.stateID, queryParams.Encode()))
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var errJson httputil.DefaultJsonError
|
|
err = json.Unmarshal(body, &errJson)
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.statusCode, errJson.Code)
|
|
require.StringContains(t, test.expectedErrorMessage, errJson.Message)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetValidatorCount(t *testing.T) {
|
|
st, _ := util.DeterministicGenesisState(t, 10)
|
|
farFutureEpoch := params.BeaconConfig().FarFutureEpoch
|
|
validators := []*eth.Validator{
|
|
// Pending initialized.
|
|
{
|
|
ActivationEpoch: farFutureEpoch,
|
|
ActivationEligibilityEpoch: farFutureEpoch,
|
|
ExitEpoch: farFutureEpoch,
|
|
WithdrawableEpoch: farFutureEpoch,
|
|
},
|
|
// Pending queued.
|
|
{
|
|
ActivationEpoch: 10,
|
|
ActivationEligibilityEpoch: 4,
|
|
ExitEpoch: farFutureEpoch,
|
|
WithdrawableEpoch: farFutureEpoch,
|
|
},
|
|
// Active ongoing.
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: farFutureEpoch,
|
|
},
|
|
// Active slashed.
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: 30,
|
|
Slashed: true,
|
|
WithdrawableEpoch: 50,
|
|
},
|
|
// Active exiting.
|
|
{
|
|
ActivationEpoch: 0,
|
|
ExitEpoch: 30,
|
|
Slashed: false,
|
|
WithdrawableEpoch: 50,
|
|
},
|
|
// Exit slashed (at epoch 35).
|
|
{
|
|
ActivationEpoch: 3,
|
|
ExitEpoch: 30,
|
|
WithdrawableEpoch: 50,
|
|
Slashed: true,
|
|
},
|
|
// Exit unslashed (at epoch 35).
|
|
{
|
|
ActivationEpoch: 3,
|
|
ExitEpoch: 30,
|
|
WithdrawableEpoch: 50,
|
|
Slashed: false,
|
|
},
|
|
// Withdrawable (at epoch 45).
|
|
{
|
|
ActivationEpoch: 3,
|
|
ExitEpoch: 30,
|
|
WithdrawableEpoch: 40,
|
|
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
|
|
Slashed: false,
|
|
},
|
|
// Withdrawal done (at epoch 45).
|
|
{
|
|
ActivationEpoch: 3,
|
|
ExitEpoch: 30,
|
|
WithdrawableEpoch: 40,
|
|
EffectiveBalance: 0,
|
|
Slashed: false,
|
|
},
|
|
}
|
|
for _, validator := range validators {
|
|
require.NoError(t, st.AppendValidator(validator))
|
|
require.NoError(t, st.AppendBalance(params.BeaconConfig().MaxEffectiveBalance))
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
stateID string
|
|
statuses []string
|
|
currentEpoch int
|
|
expectedResponse structs.GetValidatorCountResponse
|
|
}{
|
|
{
|
|
name: "Head count active validators",
|
|
stateID: "head",
|
|
statuses: []string{"active"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "active",
|
|
Count: "13",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count active ongoing validators",
|
|
stateID: "head",
|
|
statuses: []string{"active_ongoing"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "active_ongoing",
|
|
Count: "11",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count active exiting validators",
|
|
stateID: "head",
|
|
statuses: []string{"active_exiting"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "active_exiting",
|
|
Count: "1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count active slashed validators",
|
|
stateID: "head",
|
|
statuses: []string{"active_slashed"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "active_slashed",
|
|
Count: "1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count pending validators",
|
|
stateID: "head",
|
|
statuses: []string{"pending"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "pending",
|
|
Count: "6",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count pending initialized validators",
|
|
stateID: "head",
|
|
statuses: []string{"pending_initialized"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "pending_initialized",
|
|
Count: "1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count pending queued validators",
|
|
stateID: "head",
|
|
statuses: []string{"pending_queued"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "pending_queued",
|
|
Count: "5",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count exited validators",
|
|
stateID: "head",
|
|
statuses: []string{"exited"},
|
|
currentEpoch: 35,
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "exited",
|
|
Count: "6",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count exited slashed validators",
|
|
stateID: "head",
|
|
statuses: []string{"exited_slashed"},
|
|
currentEpoch: 35,
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "exited_slashed",
|
|
Count: "2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count exited unslashed validators",
|
|
stateID: "head",
|
|
statuses: []string{"exited_unslashed"},
|
|
currentEpoch: 35,
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "exited_unslashed",
|
|
Count: "4",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count withdrawal validators",
|
|
stateID: "head",
|
|
statuses: []string{"withdrawal"},
|
|
currentEpoch: 45,
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "withdrawal",
|
|
Count: "2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count withdrawal possible validators",
|
|
stateID: "head",
|
|
statuses: []string{"withdrawal_possible"},
|
|
currentEpoch: 45,
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "withdrawal_possible",
|
|
Count: "1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count withdrawal done validators",
|
|
stateID: "head",
|
|
statuses: []string{"withdrawal_done"},
|
|
currentEpoch: 45,
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "withdrawal_done",
|
|
Count: "1",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count active and pending validators",
|
|
stateID: "head",
|
|
statuses: []string{"active", "pending"},
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "active",
|
|
Count: "13",
|
|
},
|
|
{
|
|
Status: "pending",
|
|
Count: "6",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Head count of ALL validators",
|
|
stateID: "head",
|
|
expectedResponse: structs.GetValidatorCountResponse{
|
|
ExecutionOptimistic: "false",
|
|
Finalized: "true",
|
|
Data: []*structs.ValidatorCount{
|
|
{
|
|
Status: "active",
|
|
Count: "13",
|
|
},
|
|
{
|
|
Status: "active_exiting",
|
|
Count: "1",
|
|
},
|
|
{
|
|
Status: "active_ongoing",
|
|
Count: "11",
|
|
},
|
|
{
|
|
Status: "active_slashed",
|
|
Count: "1",
|
|
},
|
|
{
|
|
Status: "pending",
|
|
Count: "6",
|
|
},
|
|
{
|
|
Status: "pending_initialized",
|
|
Count: "1",
|
|
},
|
|
{
|
|
Status: "pending_queued",
|
|
Count: "5",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
chainService := &chainMock.ChainService{Optimistic: false, FinalizedRoots: make(map[[32]byte]bool)}
|
|
blockRoot, err := st.LatestBlockHeader().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
chainService.FinalizedRoots[blockRoot] = true
|
|
require.NoError(t, st.SetSlot(params.BeaconConfig().SlotsPerEpoch*primitives.Slot(test.currentEpoch)))
|
|
|
|
server := &Server{
|
|
OptimisticModeFetcher: chainService,
|
|
FinalizationFetcher: chainService,
|
|
Stater: &testutil.MockStater{
|
|
BeaconState: st,
|
|
},
|
|
}
|
|
|
|
testRouter := mux.NewRouter()
|
|
testRouter.HandleFunc("/eth/v1/beacon/states/{state_id}/validator_count", server.GetValidatorCount)
|
|
s := httptest.NewServer(testRouter)
|
|
defer s.Close()
|
|
|
|
queryParams := neturl.Values{}
|
|
for _, status := range test.statuses {
|
|
queryParams.Add("status", status)
|
|
}
|
|
resp, err := http.Get(s.URL + fmt.Sprintf("/eth/v1/beacon/states/%s/validator_count?%s",
|
|
test.stateID, queryParams.Encode()))
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var count structs.GetValidatorCountResponse
|
|
err = json.Unmarshal(body, &count)
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, test.expectedResponse, count)
|
|
})
|
|
}
|
|
}
|