Skip to content

Commit

Permalink
core/validatorapi: handle HTTP methods (#2866)
Browse files Browse the repository at this point in the history
Instead of handling all HTTP methods for all validator API endpoints, only support a subset of them for each.

Selected the appropriate HTTP method by matching them from the Beacon Node API spec: https://ethereum.github.io/beacon-APIs/#/.

category: bug
ticket: none
  • Loading branch information
gsora committed Feb 8, 2024
1 parent 422b507 commit fd70377
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 6 deletions.
32 changes: 31 additions & 1 deletion core/validatorapi/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,142 +86,172 @@ func NewRouter(ctx context.Context, h Handler, eth2Cl eth2wrap.Client) (*mux.Rou
Name string
Path string
Handler handlerFunc
Methods []string
}{
{
Name: "attester_duties",
Path: "/eth/v1/validator/duties/attester/{epoch}",
Handler: attesterDuties(h),
Methods: []string{http.MethodPost},
},
{
Name: "proposer_duties",
Path: "/eth/v1/validator/duties/proposer/{epoch}",
Handler: proposerDuties(h),
Methods: []string{http.MethodGet},
},
{
Name: "sync_committee_duties",
Path: "/eth/v1/validator/duties/sync/{epoch}",
Handler: syncCommitteeDuties(h),
Methods: []string{http.MethodPost},
},
{
Name: "attestation_data",
Path: "/eth/v1/validator/attestation_data",
Handler: attestationData(h),
Methods: []string{http.MethodGet},
},
{
Name: "submit_attestations",
Path: "/eth/v1/beacon/pool/attestations",
Handler: submitAttestations(h),
Methods: []string{http.MethodPost},
},
{
Name: "get_validators",
Path: "/eth/v1/beacon/states/{state_id}/validators",
Handler: getValidators(h),
Methods: []string{http.MethodPost, http.MethodGet},
},
{
Name: "get_validator",
Path: "/eth/v1/beacon/states/{state_id}/validators/{validator_id}",
Handler: getValidator(h),
Methods: []string{http.MethodGet},
},
{
Name: "propose_block",
Path: "/eth/v2/validator/blocks/{slot}",
Handler: proposeBlock(h),
Methods: []string{http.MethodGet},
},
{
Name: "submit_proposal_v1",
Path: "/eth/v1/beacon/blocks",
Handler: submitProposal(h),
Methods: []string{http.MethodPost},
},
{
Name: "submit_proposal_v2",
Path: "/eth/v2/beacon/blocks",
Handler: submitProposal(h),
Methods: []string{http.MethodPost},
},
{
Name: "propose_blinded_block",
Path: "/eth/v1/validator/blinded_blocks/{slot}",
Handler: proposeBlindedBlock(h),
Methods: []string{http.MethodGet},
},
{
Name: "submit_blinded_block_v1",
Path: "/eth/v1/beacon/blinded_blocks",
Handler: submitBlindedBlock(h),
Methods: []string{http.MethodPost},
},
{
Name: "submit_blinded_block_v2",
Path: "/eth/v2/beacon/blinded_blocks",
Handler: submitBlindedBlock(h),
Methods: []string{http.MethodPost},
},
{
Name: "submit_validator_registration",
Path: "/eth/v1/validator/register_validator",
Handler: submitValidatorRegistrations(h),
Methods: []string{http.MethodPost},
},
{
Name: "submit_voluntary_exit",
Path: "/eth/v1/beacon/pool/voluntary_exits",
Handler: submitExit(h),
Methods: []string{http.MethodPost},
},
{
Name: "teku_proposer_config",
Path: "/teku_proposer_config",
Handler: proposerConfig(h),
Methods: []string{http.MethodGet},
},
{
Name: "proposer_config",
Path: "/proposer_config",
Handler: proposerConfig(h),
Methods: []string{http.MethodGet},
},
{
Name: "aggregate_beacon_committee_selections",
Path: "/eth/v1/validator/beacon_committee_selections",
Handler: aggregateBeaconCommitteeSelections(h),
Methods: []string{http.MethodPost},
},
{
Name: "aggregate_attestation",
Path: "/eth/v1/validator/aggregate_attestation",
Handler: aggregateAttestation(h),
Methods: []string{http.MethodGet},
},
{
Name: "submit_aggregate_and_proofs",
Path: "/eth/v1/validator/aggregate_and_proofs",
Handler: submitAggregateAttestations(h),
Methods: []string{http.MethodPost},
},
{
Name: "submit_sync_committee_messages",
Path: "/eth/v1/beacon/pool/sync_committees",
Handler: submitSyncCommitteeMessages(h),
Methods: []string{http.MethodPost},
},
{
Name: "sync_committee_contribution",
Path: "/eth/v1/validator/sync_committee_contribution",
Handler: syncCommitteeContribution(h),
Methods: []string{http.MethodGet},
},
{
Name: "submit_contribution_and_proofs",
Path: "/eth/v1/validator/contribution_and_proofs",
Handler: submitContributionAndProofs(h),
Methods: []string{http.MethodPost},
},
{
Name: "submit_proposal_preparations",
Path: "/eth/v1/validator/prepare_beacon_proposer",
Handler: submitProposalPreparations(),
Methods: []string{http.MethodPost},
},
{
Name: "aggregate_sync_committee_selections",
Path: "/eth/v1/validator/sync_committee_selections",
Handler: aggregateSyncCommitteeSelections(h),
Methods: []string{http.MethodPost},
},
{
Name: "node_version",
Path: "/eth/v1/node/version",
Handler: nodeVersion(h),
Methods: []string{http.MethodGet},
},
}

r := mux.NewRouter()
for _, e := range endpoints {
r.Handle(e.Path, wrap(e.Name, e.Handler))
handler := r.Handle(e.Path, wrap(e.Name, e.Handler))
if len(e.Methods) != 0 {
handler.Methods(e.Methods...)
}
}

// Everything else is proxied
Expand Down
61 changes: 56 additions & 5 deletions core/validatorapi/router_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func TestRawRouter(t *testing.T) {
handler := testHandler{}

callback := func(ctx context.Context, baseURL string) {
res, err := http.Get(baseURL + "/eth/v1/validator/duties/attester/not_a_number")
res, err := http.Post(baseURL+"/eth/v1/validator/duties/attester/not_a_number", "application/json", bytes.NewReader([]byte("{}")))
require.NoError(t, err)

var errRes errorResponse
Expand All @@ -140,7 +140,7 @@ func TestRawRouter(t *testing.T) {
handler := testHandler{}

callback := func(ctx context.Context, baseURL string) {
res, err := http.Post(baseURL+"/eth/v2/validator/blocks/123", "", nil)
res, err := http.Get(baseURL + "/eth/v2/validator/blocks/123")
require.NoError(t, err)

var errRes errorResponse
Expand All @@ -159,7 +159,7 @@ func TestRawRouter(t *testing.T) {
handler := testHandler{}

callback := func(ctx context.Context, baseURL string) {
res, err := http.Post(baseURL+"/eth/v2/validator/blocks/123?randao_reveal=0x0000", "", nil)
res, err := http.Get(baseURL + "/eth/v2/validator/blocks/123?randao_reveal=0x0000")
require.NoError(t, err)

var errRes errorResponse
Expand All @@ -185,7 +185,7 @@ func TestRawRouter(t *testing.T) {

callback := func(ctx context.Context, baseURL string) {
randao := testutil.RandomEth2Signature().String()
res, err := http.Post(baseURL+"/eth/v2/validator/blocks/123?randao_reveal="+randao, "", nil)
res, err := http.Get(baseURL + "/eth/v2/validator/blocks/123?randao_reveal=" + randao)
require.NoError(t, err)

var okResp struct{ Data json.RawMessage }
Expand All @@ -201,7 +201,7 @@ func TestRawRouter(t *testing.T) {
handler := testHandler{}

callback := func(ctx context.Context, baseURL string) {
res, err := http.Get(baseURL + "/eth/v1/validator/duties/attester/1")
res, err := http.Post(baseURL+"/eth/v1/validator/duties/attester/1", "application/json", bytes.NewReader([]byte("")))
require.NoError(t, err)

var errRes errorResponse
Expand Down Expand Up @@ -573,6 +573,57 @@ func TestRouter(t *testing.T) {
"dependent_root": dependentRoot,
}

t.Run("wrong http method", func(t *testing.T) {
ctx := context.Background()

h := testHandler{}

proxy := httptest.NewServer(h.newBeaconHandler(t))
defer proxy.Close()

r, err := NewRouter(ctx, h, testBeaconAddr{addr: proxy.URL})
require.NoError(t, err)

server := httptest.NewServer(r)
defer server.Close()

endpointURL := fmt.Sprintf("%s/eth/v1/node/version", server.URL)

// node_version is a GET-only endpoint, we expect it to fail
resp, err := http.Post(
endpointURL,
"application/json",
bytes.NewReader([]byte("{}")),
)

require.NoError(t, err)

require.Equal(
t,
http.StatusNotFound,
resp.StatusCode,
)

// use the right http method and expect a response, and status code 200
resp, err = http.Get(endpointURL)
require.NoError(t, err)

require.Equal(
t,
http.StatusOK,
resp.StatusCode,
)

data, err := io.ReadAll(resp.Body)
require.NoError(t, err)

defer func() {
_ = resp.Body.Close()
}()

require.NotEmpty(t, data)
})

t.Run("attesterduty", func(t *testing.T) {
handler := testHandler{
AttesterDutiesFunc: func(ctx context.Context, opts *eth2api.AttesterDutiesOpts) (*eth2api.Response[[]*eth2v1.AttesterDuty], error) {
Expand Down

0 comments on commit fd70377

Please sign in to comment.