mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 13:28:01 -05:00
Update Beacon API evaluator (#15304)
* Update Beacon API evaluator * cleanup and changelog * changelog message fix
This commit is contained in:
3
changelog/radek_api-evaluator-electra.md
Normal file
3
changelog/radek_api-evaluator-electra.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Updated e2e Beacon API evaluator to support more endpoints, including the ones introduced in Electra.
|
||||
@@ -28,26 +28,34 @@ var getRequests = map[string]endpoint{
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
})),
|
||||
// we want to test comma-separated query params
|
||||
"/beacon/states/{param1}/validators?id=0,1": newMetadata[structs.GetValidatorsResponse](
|
||||
"/beacon/states/{param1}/validators": newMetadata[structs.GetValidatorsResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withQueryParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"id=0,1"}
|
||||
})),
|
||||
"/beacon/states/{param1}/validators/{param2}": newMetadata[structs.GetValidatorResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head", "0"}
|
||||
})),
|
||||
"/beacon/states/{param1}/validator_balances?id=0,1": newMetadata[structs.GetValidatorBalancesResponse](
|
||||
"/beacon/states/{param1}/validator_balances": newMetadata[structs.GetValidatorBalancesResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withQueryParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"id=0,1"}
|
||||
})),
|
||||
"/beacon/states/{param1}/committees?index=0": newMetadata[structs.GetCommitteesResponse](
|
||||
"/beacon/states/{param1}/committees": newMetadata[structs.GetCommitteesResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withQueryParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"index=0"}
|
||||
})),
|
||||
"/beacon/states/{param1}/sync_committees": newMetadata[structs.GetSyncCommitteeResponse](
|
||||
v1PathTemplate,
|
||||
@@ -60,6 +68,27 @@ var getRequests = map[string]endpoint{
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
})),
|
||||
"/beacon/states/{param1}/pending_consolidations": newMetadata[structs.GetPendingConsolidationsResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.BeaconConfig().ElectraForkEpoch),
|
||||
withSsz(),
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
})),
|
||||
"/beacon/states/{param1}/pending_deposits": newMetadata[structs.GetPendingDepositsResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.BeaconConfig().ElectraForkEpoch),
|
||||
withSsz(),
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
})),
|
||||
"/beacon/states/{param1}/pending_partial_withdrawals": newMetadata[structs.GetPendingPartialWithdrawalsResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.BeaconConfig().ElectraForkEpoch),
|
||||
withSsz(),
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
})),
|
||||
"/beacon/headers": newMetadata[structs.GetBlockHeadersResponse](v1PathTemplate),
|
||||
"/beacon/headers/{param1}": newMetadata[structs.GetBlockHeaderResponse](
|
||||
v1PathTemplate,
|
||||
@@ -93,6 +122,10 @@ var getRequests = map[string]endpoint{
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
})),
|
||||
"/beacon/rewards/block/{param1}": newMetadata[structs.BlockRewardsResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.BeaconConfig().AltairForkEpoch),
|
||||
withParams(func(_ primitives.Epoch) []string { return []string{"head"} })),
|
||||
"/beacon/blinded_blocks/{param1}": newMetadata[structs.GetBlockV2Response](
|
||||
v1PathTemplate,
|
||||
withSsz(),
|
||||
@@ -125,11 +158,11 @@ var getRequests = map[string]endpoint{
|
||||
withCustomEval(func(p interface{}, lh interface{}) error {
|
||||
pResp, ok := p.(*structs.GetForkScheduleResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.GetForkScheduleResponse{}, p)
|
||||
return fmt.Errorf(msgWrongJSON, &structs.GetForkScheduleResponse{}, p)
|
||||
}
|
||||
lhResp, ok := lh.(*structs.GetForkScheduleResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.GetForkScheduleResponse{}, lh)
|
||||
return fmt.Errorf(msgWrongJSON, &structs.GetForkScheduleResponse{}, lh)
|
||||
}
|
||||
// remove all forks with far-future epoch
|
||||
for i := len(pResp.Data) - 1; i >= 0; i-- {
|
||||
@@ -175,7 +208,7 @@ var getRequests = map[string]endpoint{
|
||||
withCustomEval(func(p interface{}, _ interface{}) error {
|
||||
pResp, ok := p.(*structs.GetVersionResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.ListAttestationsResponse{}, p)
|
||||
return fmt.Errorf(msgWrongJSON, &structs.ListAttestationsResponse{}, p)
|
||||
}
|
||||
if pResp.Data == nil {
|
||||
return errEmptyPrysmData
|
||||
@@ -194,11 +227,11 @@ var getRequests = map[string]endpoint{
|
||||
withCustomEval(func(p interface{}, lh interface{}) error {
|
||||
pResp, ok := p.(*structs.GetProposerDutiesResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.GetProposerDutiesResponse{}, p)
|
||||
return fmt.Errorf(msgWrongJSON, &structs.GetProposerDutiesResponse{}, p)
|
||||
}
|
||||
lhResp, ok := lh.(*structs.GetProposerDutiesResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.GetProposerDutiesResponse{}, lh)
|
||||
return fmt.Errorf(msgWrongJSON, &structs.GetProposerDutiesResponse{}, lh)
|
||||
}
|
||||
if pResp.Data == nil {
|
||||
return errEmptyPrysmData
|
||||
@@ -212,57 +245,86 @@ var getRequests = map[string]endpoint{
|
||||
}
|
||||
return compareJSON(pResp, lhResp)
|
||||
})),
|
||||
"/validator/blocks/{param1}?randao_reveal=0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505": newMetadata[structs.ProduceBlockV3Response](
|
||||
"/validator/blocks/{param1}": newMetadata[structs.ProduceBlockV3Response](
|
||||
v3PathTemplate,
|
||||
withSanityCheckOnly(),
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{strconv.FormatUint(uint64(currentEpoch)*uint64(params.BeaconConfig().SlotsPerEpoch)+uint64(params.BeaconConfig().SlotsPerEpoch)/2+1, 10)}
|
||||
}),
|
||||
withQueryParams(func(_ primitives.Epoch) []string {
|
||||
return []string{
|
||||
"randao_reveal=0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505",
|
||||
}
|
||||
})),
|
||||
}
|
||||
|
||||
var postRequests = map[string]endpoint{
|
||||
"/beacon/states/{param1}/validators": newMetadata[structs.GetValidatorsResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withPOSTObj(func() interface{} {
|
||||
return struct {
|
||||
Ids []string `json:"ids"`
|
||||
Statuses []string `json:"statuses"`
|
||||
}{Ids: []string{"0", "1"}, Statuses: nil}
|
||||
}())),
|
||||
"/beacon/states/{param1}/validator_balances": newMetadata[structs.GetValidatorBalancesResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withPOSTObj(func() []string {
|
||||
return []string{"0", "1"}
|
||||
}())),
|
||||
"/validator/duties/attester/{param1}": newMetadata[structs.GetAttesterDutiesResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{fmt.Sprintf("%v", currentEpoch)}
|
||||
}),
|
||||
withPOSTObj(func() []string {
|
||||
validatorIndices := make([]string, 64)
|
||||
for i := range validatorIndices {
|
||||
validatorIndices[i] = fmt.Sprintf("%d", i)
|
||||
}
|
||||
return validatorIndices
|
||||
}())),
|
||||
"/validator/duties/sync/{param1}": newMetadata[structs.GetSyncCommitteeDutiesResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.AltairE2EForkEpoch),
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{fmt.Sprintf("%v", currentEpoch)}
|
||||
}),
|
||||
withPOSTObj(func() []string {
|
||||
validatorIndices := make([]string, 64)
|
||||
for i := range validatorIndices {
|
||||
validatorIndices[i] = fmt.Sprintf("%d", i)
|
||||
}
|
||||
return validatorIndices
|
||||
}())),
|
||||
}
|
||||
var (
|
||||
postRequests = map[string]endpoint{
|
||||
"/beacon/states/{param1}/validators": newMetadata[structs.GetValidatorsResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withPOSTObj(func() interface{} {
|
||||
return struct {
|
||||
Ids []string `json:"ids"`
|
||||
Statuses []string `json:"statuses"`
|
||||
}{Ids: []string{"0", "1"}, Statuses: nil}
|
||||
}())),
|
||||
"/beacon/states/{param1}/validator_balances": newMetadata[structs.GetValidatorBalancesResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(_ primitives.Epoch) []string {
|
||||
return []string{"head"}
|
||||
}),
|
||||
withPOSTObj(func() []string {
|
||||
return []string{"0", "1"}
|
||||
}())),
|
||||
"/beacon/states/{param1}/validator_identities": newMetadata[structs.GetValidatorIdentitiesResponse](
|
||||
v1PathTemplate,
|
||||
withSanityCheckOnly(), // LH doesn't support the endpoint
|
||||
withSsz(),
|
||||
withParams(func(_ primitives.Epoch) []string { return []string{"head"} }),
|
||||
withPOSTObj([]string{"0", "1"})),
|
||||
"/beacon/rewards/sync_committee/{param1}": newMetadata[structs.SyncCommitteeRewardsResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.BeaconConfig().AltairForkEpoch),
|
||||
withParams(func(_ primitives.Epoch) []string { return []string{"head"} })),
|
||||
"/beacon/rewards/attestations/{param1}": newMetadata[structs.AttestationRewardsResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.BeaconConfig().AltairForkEpoch),
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{fmt.Sprintf("%v", currentEpoch-2)}
|
||||
})),
|
||||
"/validator/duties/attester/{param1}": newMetadata[structs.GetAttesterDutiesResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{fmt.Sprintf("%v", currentEpoch)}
|
||||
}),
|
||||
withPOSTObj(func() []string {
|
||||
validatorIndices := make([]string, 64)
|
||||
for i := range validatorIndices {
|
||||
validatorIndices[i] = fmt.Sprintf("%d", i)
|
||||
}
|
||||
return validatorIndices
|
||||
}())),
|
||||
"/validator/duties/sync/{param1}": newMetadata[structs.GetSyncCommitteeDutiesResponse](
|
||||
v1PathTemplate,
|
||||
withStart(params.AltairE2EForkEpoch),
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{fmt.Sprintf("%v", currentEpoch)}
|
||||
}),
|
||||
withPOSTObj(func() []string {
|
||||
validatorIndices := make([]string, 64)
|
||||
for i := range validatorIndices {
|
||||
validatorIndices[i] = fmt.Sprintf("%d", i)
|
||||
}
|
||||
return validatorIndices
|
||||
}())),
|
||||
"/validator/liveness/{param1}": newMetadata[structs.GetLivenessResponse](
|
||||
v1PathTemplate,
|
||||
withParams(func(currentEpoch primitives.Epoch) []string {
|
||||
return []string{fmt.Sprintf("%v", currentEpoch)}
|
||||
}),
|
||||
withPOSTObj([]string{"0", "1"})),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -18,23 +18,26 @@ type endpoint interface {
|
||||
setPOSTObj(obj interface{})
|
||||
getPResp() interface{} // retrieves the Prysm JSON response
|
||||
getLHResp() interface{} // retrieves the Lighthouse JSON response
|
||||
getParams(epoch primitives.Epoch) []string
|
||||
setParams(f func(primitives.Epoch) []string)
|
||||
getParams(currentEpoch primitives.Epoch) []string
|
||||
setParams(f func(currentEpoch primitives.Epoch) []string)
|
||||
getQueryParams(currentEpoch primitives.Epoch) []string
|
||||
setQueryParams(f func(currentEpoch primitives.Epoch) []string)
|
||||
getCustomEval() func(interface{}, interface{}) error
|
||||
setCustomEval(f func(interface{}, interface{}) error)
|
||||
}
|
||||
|
||||
type apiEndpoint[Resp any] struct {
|
||||
basePath string
|
||||
sanity bool
|
||||
ssz bool
|
||||
start primitives.Epoch
|
||||
postObj interface{}
|
||||
pResp *Resp // Prysm JSON response
|
||||
lhResp *Resp // Lighthouse JSON response
|
||||
sszResp []byte // Prysm SSZ response
|
||||
params func(currentEpoch primitives.Epoch) []string
|
||||
customEval func(interface{}, interface{}) error
|
||||
basePath string
|
||||
sanity bool
|
||||
ssz bool
|
||||
start primitives.Epoch
|
||||
postObj interface{}
|
||||
pResp *Resp // Prysm JSON response
|
||||
lhResp *Resp // Lighthouse JSON response
|
||||
sszResp []byte // Prysm SSZ response
|
||||
params func(currentEpoch primitives.Epoch) []string
|
||||
queryParams func(currentEpoch primitives.Epoch) []string
|
||||
customEval func(interface{}, interface{}) error
|
||||
}
|
||||
|
||||
func (e *apiEndpoint[Resp]) getBasePath() string {
|
||||
@@ -89,17 +92,28 @@ func (e *apiEndpoint[Resp]) getLHResp() interface{} {
|
||||
return e.lhResp
|
||||
}
|
||||
|
||||
func (e *apiEndpoint[Resp]) getParams(epoch primitives.Epoch) []string {
|
||||
func (e *apiEndpoint[Resp]) getParams(currentEpoch primitives.Epoch) []string {
|
||||
if e.params == nil {
|
||||
return nil
|
||||
}
|
||||
return e.params(epoch)
|
||||
return e.params(currentEpoch)
|
||||
}
|
||||
|
||||
func (e *apiEndpoint[Resp]) setParams(f func(currentEpoch primitives.Epoch) []string) {
|
||||
e.params = f
|
||||
}
|
||||
|
||||
func (e *apiEndpoint[Resp]) getQueryParams(currentEpoch primitives.Epoch) []string {
|
||||
if e.queryParams == nil {
|
||||
return nil
|
||||
}
|
||||
return e.queryParams(currentEpoch)
|
||||
}
|
||||
|
||||
func (e *apiEndpoint[Resp]) setQueryParams(f func(currentEpoch primitives.Epoch) []string) {
|
||||
e.queryParams = f
|
||||
}
|
||||
|
||||
func (e *apiEndpoint[Resp]) getCustomEval() func(interface{}, interface{}) error {
|
||||
return e.customEval
|
||||
}
|
||||
@@ -157,6 +171,13 @@ func withParams(f func(currentEpoch primitives.Epoch) []string) endpointOpt {
|
||||
}
|
||||
}
|
||||
|
||||
// We specify query parameters.
|
||||
func withQueryParams(f func(currentEpoch primitives.Epoch) []string) endpointOpt {
|
||||
return func(e endpoint) {
|
||||
e.setQueryParams(f)
|
||||
}
|
||||
}
|
||||
|
||||
// We perform custom evaluation on responses.
|
||||
func withCustomEval(f func(interface{}, interface{}) error) endpointOpt {
|
||||
return func(e endpoint) {
|
||||
|
||||
@@ -19,13 +19,13 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
msgWrongJson = "JSON response has wrong structure, expected %T, got %T"
|
||||
msgWrongJSON = "JSON response has wrong structure, expected %T, got %T"
|
||||
msgRequestFailed = "%s request failed with response code %d with response body %s"
|
||||
msgUnknownNode = "unknown node type %s"
|
||||
msgSSZUnmarshalFailed = "failed to unmarshal SSZ"
|
||||
)
|
||||
|
||||
func doJSONGetRequest(template, requestPath string, beaconNodeIdx int, resp interface{}, bnType ...string) error {
|
||||
func doJSONGETRequest(template, requestPath string, beaconNodeIdx int, resp interface{}, bnType ...string) error {
|
||||
if len(bnType) == 0 {
|
||||
bnType = []string{"Prysm"}
|
||||
}
|
||||
@@ -41,32 +41,34 @@ func doJSONGetRequest(template, requestPath string, beaconNodeIdx int, resp inte
|
||||
}
|
||||
|
||||
basePath := fmt.Sprintf(template, port+beaconNodeIdx)
|
||||
httpResp, err := http.Get(
|
||||
basePath + requestPath,
|
||||
)
|
||||
httpResp, err := http.Get(basePath + requestPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
|
||||
var body interface{}
|
||||
if httpResp.StatusCode != http.StatusOK {
|
||||
if httpResp.Header.Get("Content-Type") == api.JsonMediaType {
|
||||
if err = json.NewDecoder(httpResp.Body).Decode(&body); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to decode response body")
|
||||
}
|
||||
} else {
|
||||
defer closeBody(httpResp.Body)
|
||||
body, err = io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to read response body")
|
||||
}
|
||||
}
|
||||
return fmt.Errorf(msgRequestFailed, bnType[0], httpResp.StatusCode, body)
|
||||
}
|
||||
return json.NewDecoder(httpResp.Body).Decode(&resp)
|
||||
|
||||
if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
|
||||
return errors.Wrap(err, "failed to decode response body")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doSSZGetRequest(template, requestPath string, beaconNodeIdx int, bnType ...string) ([]byte, error) {
|
||||
func doSSZGETRequest(template, requestPath string, beaconNodeIdx int, bnType ...string) ([]byte, error) {
|
||||
if len(bnType) == 0 {
|
||||
bnType = []string{"Prysm"}
|
||||
}
|
||||
@@ -85,30 +87,30 @@ func doSSZGetRequest(template, requestPath string, beaconNodeIdx int, bnType ...
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, basePath+requestPath, http.NoBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to build request")
|
||||
}
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var body interface{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to decode response body")
|
||||
}
|
||||
return nil, fmt.Errorf(msgRequestFailed, bnType[0], resp.StatusCode, body)
|
||||
}
|
||||
defer closeBody(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to read response body")
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func doJSONPostRequest(template, requestPath string, beaconNodeIdx int, postObj, resp interface{}, bnType ...string) error {
|
||||
func doJSONPOSTRequest(template, requestPath string, beaconNodeIdx int, postObj, resp interface{}, bnType ...string) error {
|
||||
if len(bnType) == 0 {
|
||||
bnType = []string{"Prysm"}
|
||||
}
|
||||
@@ -126,7 +128,7 @@ func doJSONPostRequest(template, requestPath string, beaconNodeIdx int, postObj,
|
||||
basePath := fmt.Sprintf(template, port+beaconNodeIdx)
|
||||
b, err := json.Marshal(postObj)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to marshal POST object")
|
||||
}
|
||||
httpResp, err := http.Post(
|
||||
basePath+requestPath,
|
||||
@@ -134,25 +136,76 @@ func doJSONPostRequest(template, requestPath string, beaconNodeIdx int, postObj,
|
||||
bytes.NewBuffer(b),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "request failed")
|
||||
}
|
||||
|
||||
var body interface{}
|
||||
if httpResp.StatusCode != http.StatusOK {
|
||||
if httpResp.Header.Get("Content-Type") == api.JsonMediaType {
|
||||
if err = json.NewDecoder(httpResp.Body).Decode(&body); err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to decode response body")
|
||||
}
|
||||
} else {
|
||||
defer closeBody(httpResp.Body)
|
||||
body, err = io.ReadAll(httpResp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
return errors.Wrap(err, "failed to read response body")
|
||||
}
|
||||
}
|
||||
return fmt.Errorf(msgRequestFailed, bnType[0], httpResp.StatusCode, body)
|
||||
}
|
||||
return json.NewDecoder(httpResp.Body).Decode(&resp)
|
||||
|
||||
if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
|
||||
return errors.Wrap(err, "failed to decode response body")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func doSSZPOSTRequest(template, requestPath string, beaconNodeIdx int, postObj interface{}, bnType ...string) ([]byte, error) {
|
||||
if len(bnType) == 0 {
|
||||
bnType = []string{"Prysm"}
|
||||
}
|
||||
|
||||
var port int
|
||||
switch bnType[0] {
|
||||
case "Prysm":
|
||||
port = params.TestParams.Ports.PrysmBeaconNodeHTTPPort
|
||||
case "Lighthouse":
|
||||
port = params.TestParams.Ports.LighthouseBeaconNodeHTTPPort
|
||||
default:
|
||||
return nil, fmt.Errorf(msgUnknownNode, bnType[0])
|
||||
}
|
||||
|
||||
basePath := fmt.Sprintf(template, port+beaconNodeIdx)
|
||||
b, err := json.Marshal(postObj)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal POST object")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, basePath+requestPath, bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to build request")
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "request failed")
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var body interface{}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode response body")
|
||||
}
|
||||
return nil, fmt.Errorf(msgRequestFailed, bnType[0], resp.StatusCode, body)
|
||||
}
|
||||
defer closeBody(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read response body")
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func closeBody(body io.Closer) {
|
||||
|
||||
@@ -48,7 +48,7 @@ func verify(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
|
||||
|
||||
func run(nodeIdx int) error {
|
||||
genesisResp := &structs.GetGenesisResponse{}
|
||||
if err := doJSONGetRequest(v1PathTemplate, "/beacon/genesis", nodeIdx, genesisResp); err != nil {
|
||||
if err := doJSONGETRequest(v1PathTemplate, "/beacon/genesis", nodeIdx, genesisResp); err != nil {
|
||||
return errors.Wrap(err, "error getting genesis data")
|
||||
}
|
||||
genesisTime, err := strconv.ParseInt(genesisResp.Data.GenesisTime, 10, 64)
|
||||
@@ -61,21 +61,19 @@ func run(nodeIdx int) error {
|
||||
if currentEpoch < m.getStart() {
|
||||
continue
|
||||
}
|
||||
apiPath := path
|
||||
if m.getParams(currentEpoch) != nil {
|
||||
apiPath = pathFromParams(path, m.getParams(currentEpoch))
|
||||
}
|
||||
|
||||
apiPath := pathFromParams(path, m.getParams(currentEpoch), m.getQueryParams(currentEpoch))
|
||||
|
||||
if m.sanityCheckOnlyEnabled() {
|
||||
resp := m.getPResp()
|
||||
if err = doJSONGetRequest(m.getBasePath(), apiPath, nodeIdx, resp); err != nil {
|
||||
if err = doJSONGETRequest(m.getBasePath(), apiPath, nodeIdx, resp); err != nil {
|
||||
return errors.Wrapf(err, "issue during Prysm JSON GET request for path %s", apiPath)
|
||||
}
|
||||
if resp == nil {
|
||||
return fmt.Errorf("nil response from Prysm JSON GET request for path %s", apiPath)
|
||||
}
|
||||
if m.sszEnabled() {
|
||||
sszResp, err := doSSZGetRequest(m.getBasePath(), apiPath, nodeIdx)
|
||||
sszResp, err := doSSZGETRequest(m.getBasePath(), apiPath, nodeIdx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "issue during Prysm SSZ GET request for path %s", apiPath)
|
||||
}
|
||||
@@ -101,12 +99,37 @@ func run(nodeIdx int) error {
|
||||
if currentEpoch < m.getStart() {
|
||||
continue
|
||||
}
|
||||
apiPath := path
|
||||
if m.getParams(currentEpoch) != nil {
|
||||
apiPath = pathFromParams(path, m.getParams(currentEpoch))
|
||||
}
|
||||
if err = comparePOSTJSON(nodeIdx, m.getBasePath(), apiPath, m.getPOSTObj(), m.getPResp(), m.getLHResp(), m.getCustomEval()); err != nil {
|
||||
return err
|
||||
|
||||
apiPath := pathFromParams(path, m.getParams(currentEpoch), m.getQueryParams(currentEpoch))
|
||||
|
||||
if m.sanityCheckOnlyEnabled() {
|
||||
resp := m.getPResp()
|
||||
if err = doJSONPOSTRequest(m.getBasePath(), apiPath, nodeIdx, m.getPOSTObj(), resp); err != nil {
|
||||
return errors.Wrapf(err, "issue during Prysm JSON POST request for path %s", apiPath)
|
||||
}
|
||||
if resp == nil {
|
||||
return fmt.Errorf("nil response from Prysm JSON POST request for path %s", apiPath)
|
||||
}
|
||||
if m.sszEnabled() {
|
||||
sszResp, err := doSSZPOSTRequest(m.getBasePath(), apiPath, nodeIdx, m.getPOSTObj())
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "issue during Prysm SSZ POST request for path %s", apiPath)
|
||||
}
|
||||
if sszResp == nil {
|
||||
return fmt.Errorf("nil response from Prysm SSZ POST request for path %s", apiPath)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = comparePOSTJSON(nodeIdx, m.getBasePath(), apiPath, m.getPOSTObj(), m.getPResp(), m.getLHResp(), m.getCustomEval()); err != nil {
|
||||
return err
|
||||
}
|
||||
if m.sszEnabled() {
|
||||
b, err := comparePOSTSSZ(nodeIdx, m.getBasePath(), apiPath, m.getPOSTObj())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.setSszResp(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +202,12 @@ func postEvaluation(nodeIdx int, requests map[string]endpoint, epoch primitives.
|
||||
blockHeaderData := requests["/beacon/headers/{param1}"]
|
||||
header, ok := blockHeaderData.getPResp().(*structs.GetBlockHeaderResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.GetBlockHeaderResponse{}, blockHeaderData.getPResp())
|
||||
return fmt.Errorf(msgWrongJSON, &structs.GetBlockHeaderResponse{}, blockHeaderData.getPResp())
|
||||
}
|
||||
dutiesData := requests["/validator/duties/proposer/{param1}"]
|
||||
duties, ok := dutiesData.getPResp().(*structs.GetProposerDutiesResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJson, &structs.GetProposerDutiesResponse{}, dutiesData.getPResp())
|
||||
return fmt.Errorf(msgWrongJSON, &structs.GetProposerDutiesResponse{}, dutiesData.getPResp())
|
||||
}
|
||||
if header.Data.Root != duties.DependentRoot {
|
||||
return fmt.Errorf("header root %s does not match duties root %s ", header.Data.Root, duties.DependentRoot)
|
||||
@@ -200,14 +223,32 @@ func postEvaluation(nodeIdx int, requests map[string]endpoint, epoch primitives.
|
||||
return fmt.Errorf("health check response's status code is %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
syncingData := requests["/node/syncing"]
|
||||
sync, ok := syncingData.getPResp().(*structs.SyncStatusResponse)
|
||||
if !ok {
|
||||
return fmt.Errorf(msgWrongJSON, &structs.SyncStatusResponse{}, syncingData.getPResp())
|
||||
}
|
||||
headSlot := sync.Data.HeadSlot
|
||||
|
||||
// get attestation data (it needs the current slot)
|
||||
if err = compareGETJSON(
|
||||
nodeIdx,
|
||||
v1PathTemplate,
|
||||
fmt.Sprintf("/validator/attestation_data?slot=%s&committee_index=0", headSlot),
|
||||
&structs.AttestationData{},
|
||||
&structs.AttestationData{},
|
||||
nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func compareGETJSON(nodeIdx int, base, path string, pResp, lhResp interface{}, customEval func(interface{}, interface{}) error) error {
|
||||
if err := doJSONGetRequest(base, path, nodeIdx, pResp); err != nil {
|
||||
if err := doJSONGETRequest(base, path, nodeIdx, pResp); err != nil {
|
||||
return errors.Wrapf(err, "issue during Prysm JSON GET request for path %s", path)
|
||||
}
|
||||
if err := doJSONGetRequest(base, path, nodeIdx, lhResp, "Lighthouse"); err != nil {
|
||||
if err := doJSONGETRequest(base, path, nodeIdx, lhResp, "Lighthouse"); err != nil {
|
||||
return errors.Wrapf(err, "issue during Lighthouse JSON GET request for path %s", path)
|
||||
}
|
||||
if pResp == nil {
|
||||
@@ -224,10 +265,10 @@ func compareGETJSON(nodeIdx int, base, path string, pResp, lhResp interface{}, c
|
||||
}
|
||||
|
||||
func comparePOSTJSON(nodeIdx int, base, path string, postObj, pResp, lhResp interface{}, customEval func(interface{}, interface{}) error) error {
|
||||
if err := doJSONPostRequest(base, path, nodeIdx, postObj, pResp); err != nil {
|
||||
if err := doJSONPOSTRequest(base, path, nodeIdx, postObj, pResp); err != nil {
|
||||
return errors.Wrapf(err, "issue during Prysm JSON POST request for path %s", path)
|
||||
}
|
||||
if err := doJSONPostRequest(base, path, nodeIdx, postObj, lhResp, "Lighthouse"); err != nil {
|
||||
if err := doJSONPOSTRequest(base, path, nodeIdx, postObj, lhResp, "Lighthouse"); err != nil {
|
||||
return errors.Wrapf(err, "issue during Lighthouse JSON POST request for path %s", path)
|
||||
}
|
||||
if pResp == nil {
|
||||
@@ -244,11 +285,11 @@ func comparePOSTJSON(nodeIdx int, base, path string, postObj, pResp, lhResp inte
|
||||
}
|
||||
|
||||
func compareGETSSZ(nodeIdx int, base, path string) ([]byte, error) {
|
||||
pResp, err := doSSZGetRequest(base, path, nodeIdx)
|
||||
pResp, err := doSSZGETRequest(base, path, nodeIdx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "issue during Prysm SSZ GET request for path %s", path)
|
||||
}
|
||||
lhResp, err := doSSZGetRequest(base, path, nodeIdx, "Lighthouse")
|
||||
lhResp, err := doSSZGETRequest(base, path, nodeIdx, "Lighthouse")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "issue during Lighthouse SSZ GET request for path %s", path)
|
||||
}
|
||||
@@ -258,6 +299,21 @@ func compareGETSSZ(nodeIdx int, base, path string) ([]byte, error) {
|
||||
return pResp, nil
|
||||
}
|
||||
|
||||
func comparePOSTSSZ(nodeIdx int, base, path string, postObj interface{}) ([]byte, error) {
|
||||
pResp, err := doSSZPOSTRequest(base, path, nodeIdx, postObj)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "issue during Prysm SSZ POST request for path %s", path)
|
||||
}
|
||||
lhResp, err := doSSZPOSTRequest(base, path, nodeIdx, postObj, "Lighthouse")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "issue during Lighthouse SSZ POST request for path %s", path)
|
||||
}
|
||||
if !bytes.Equal(pResp, lhResp) {
|
||||
return nil, errors.New("Prysm SSZ response does not match Lighthouse SSZ response")
|
||||
}
|
||||
return pResp, nil
|
||||
}
|
||||
|
||||
func compareJSON(pResp, lhResp interface{}) error {
|
||||
if !reflect.DeepEqual(pResp, lhResp) {
|
||||
p, err := json.Marshal(pResp)
|
||||
@@ -273,10 +329,17 @@ func compareJSON(pResp, lhResp interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathFromParams(path string, params []string) string {
|
||||
func pathFromParams(path string, params []string, queryParams []string) string {
|
||||
apiPath := path
|
||||
for i := range params {
|
||||
apiPath = strings.Replace(apiPath, fmt.Sprintf("{param%d}", i+1), params[i], 1)
|
||||
}
|
||||
for i := range queryParams {
|
||||
if i == 0 {
|
||||
apiPath = apiPath + "?" + queryParams[i]
|
||||
} else {
|
||||
apiPath = apiPath + "&" + queryParams[i]
|
||||
}
|
||||
}
|
||||
return apiPath
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user