Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
"description" : "Requests that the beacon node produce a `PayloadAttestationData`.\n\nThis endpoint is used by PTC validators to obtain the data structure they need to attest to regarding payload presence and blob data availability for a specific slot.\n\nA 503 error must be returned if the beacon node is currently syncing.\n",
"parameters" : [ {
"name" : "slot",
"required" : true,
"in" : "path",
"in" : "query",
"schema" : {
"type" : "string",
"description" : "The slot for which payload attestation data should be created.",
Expand Down Expand Up @@ -53,16 +52,6 @@
}
}
},
"404" : {
"description" : "No canonical block has been seen for the requested slot.",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"406" : {
"description" : "Not acceptable",
"content" : {
Expand Down Expand Up @@ -94,7 +83,7 @@
}
},
"204" : {
"description" : "Data is unavailable because the chain has not yet reached genesis",
"description" : "No block has been seen for the requested slot. Used to signal validator to not cast any payload attestation.",
"content" : { }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@
import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.ETH_CONSENSUS_HEADER_TYPE;
import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.MILESTONE_TYPE;
import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.sszResponseType;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_VALIDATOR;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_VALIDATOR_REQUIRED;
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.HTTP_ERROR_RESPONSE_TYPE;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.Optional;
Expand All @@ -44,7 +43,7 @@
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsGloas;

public class GetPayloadAttestationData extends RestApiEndpoint {
public static final String ROUTE = "/eth/v1/validator/payload_attestation_data/{slot}";
public static final String ROUTE = "/eth/v1/validator/payload_attestation_data";

private final ValidatorDataProvider provider;
private final SchemaDefinitionCache schemaDefinitionCache;
Expand Down Expand Up @@ -76,29 +75,28 @@ public GetPayloadAttestationData(
A 503 error must be returned if the beacon node is currently syncing.
""")
.tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED)
.pathParam(SLOT_PARAM)
.queryParam(SLOT_PARAM)
.response(
SC_OK,
"Success response",
getResponseType(schemaDefinitionCache),
sszResponseType(),
ETH_CONSENSUS_HEADER_TYPE)
.withBadRequestResponse(Optional.of("Invalid request - the slot is invalid"))
.response(
SC_NOT_FOUND,
"No canonical block has been seen for the requested slot.",
HTTP_ERROR_RESPONSE_TYPE)
.withNotAcceptableResponse()
.withInternalErrorResponse()
.withChainDataResponses()
.response(
SC_NO_CONTENT,
"No block has been seen for the requested slot. Used to signal validator to not cast any payload attestation.")
.build());
this.provider = validatorDataProvider;
this.schemaDefinitionCache = schemaDefinitionCache;
}

@Override
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
final UInt64 slot = request.getPathParameter(SLOT_PARAM);
final UInt64 slot = request.getQueryParameter(SLOT_PARAM);
final SafeFuture<Optional<PayloadAttestationData>> future =
provider.createPayloadAttestationData(slot);

Expand All @@ -115,11 +113,7 @@ public void handleRequest(final RestApiRequest request) throws JsonProcessingExc
new ObjectAndMetaData<>(
payloadAttestationData, milestone, false, true, false));
})
.orElseGet(
() ->
AsyncApiResponse.respondWithError(
SC_NOT_FOUND,
"No canonical block found at slot=" + slot.toString()))));
.orElseGet(() -> AsyncApiResponse.respondWithCode(SC_NO_CONTENT))));
}

private static SerializableTypeDefinition<ObjectAndMetaData<PayloadAttestationData>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_ACCEPTABLE;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION;
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.getResponseStringFromMetadata;
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataEmptyResponse;
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataErrorResponse;
import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ONE;

Expand All @@ -32,7 +33,6 @@
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.beaconrestapi.AbstractMigratedBeaconHandlerTest;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.http.HttpErrorResponse;
import tech.pegasys.teku.infrastructure.http.RestApiConstants;
import tech.pegasys.teku.infrastructure.json.JsonUtil;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
Expand All @@ -53,13 +53,13 @@ void setUp() {
payloadAttestationDataResponse =
new ObjectAndMetaData<>(payloadAttestationData, SpecMilestone.GLOAS, false, true, false);
setHandler(new GetPayloadAttestationData(validatorDataProvider, schemaDefinitionCache));
request.setPathParameter(RestApiConstants.SLOT, ONE.toString());
request.setQueryParameter(RestApiConstants.SLOT, ONE.toString());
}

@Test
void metadata_shouldUsePayloadAttestationDataRoute() {
assertThat(handler.getMetadata().getPath())
.isEqualTo("/eth/v1/validator/payload_attestation_data/{slot}");
.isEqualTo("/eth/v1/validator/payload_attestation_data");
}

@Test
Expand All @@ -78,7 +78,7 @@ void shouldUseMilestoneFromRequestedSlot() throws Exception {
setSpec(TestSpecFactory.createMinimalWithHezeForkEpoch(ONE));
setHandler(new GetPayloadAttestationData(validatorDataProvider, schemaDefinitionCache));
final UInt64 hezeSlot = spec.computeStartSlotAtEpoch(ONE);
request.setPathParameter(RestApiConstants.SLOT, hezeSlot.toString());
request.setQueryParameter(RestApiConstants.SLOT, hezeSlot.toString());
final PayloadAttestationData hezePayloadAttestationData =
dataStructureUtil.randomPayloadAttestationData(hezeSlot);
when(validatorDataProvider.createPayloadAttestationData(hezeSlot))
Expand All @@ -95,15 +95,13 @@ void shouldUseMilestoneFromRequestedSlot() throws Exception {
}

@Test
void shouldReturnNotFoundWhenNoCanonicalBlockExistsAtSlot() throws Exception {
void shouldReturnNoContentWhenNoBlockSeenAtSlot() throws Exception {
when(validatorDataProvider.createPayloadAttestationData(ONE))
.thenReturn(SafeFuture.completedFuture(Optional.empty()));

handler.handleRequest(request);

assertThat(request.getResponseCode()).isEqualTo(SC_NOT_FOUND);
assertThat(request.getResponseBody())
.isEqualTo(new HttpErrorResponse(SC_NOT_FOUND, "No canonical block found at slot=1"));
assertThat(request.getResponseCode()).isEqualTo(SC_NO_CONTENT);
}

@Test
Expand All @@ -112,8 +110,8 @@ void metadata_shouldHandle400() throws JsonProcessingException {
}

@Test
void metadata_shouldHandle404() throws JsonProcessingException {
verifyMetadataErrorResponse(handler, SC_NOT_FOUND);
void metadata_shouldHandle204() throws JsonProcessingException {
verifyMetadataEmptyResponse(handler, SC_NO_CONTENT);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

package tech.pegasys.teku.validator.remote.typedef.handlers;

import static java.util.Collections.emptyMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
Expand All @@ -21,7 +22,6 @@
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;

import java.util.Map;
import java.util.Optional;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
Expand Down Expand Up @@ -57,12 +57,9 @@ public void makesExpectedRequest() throws Exception {
final RecordedRequest request = mockWebServer.takeRequest();

assertThat(request.getMethod()).isEqualTo("GET");
assertThat(request.getPath())
.isEqualTo(
"/"
+ ValidatorApiMethod.GET_PAYLOAD_ATTESTATION_DATA.getPath(
Map.of("slot", slot.toString())));
assertThat(request.getRequestUrl().queryParameter("slot")).isNull();
assertThat(request.getRequestUrl().encodedPath())
.isEqualTo("/" + ValidatorApiMethod.GET_PAYLOAD_ATTESTATION_DATA.getPath(emptyMap()));
assertThat(request.getRequestUrl().queryParameter("slot")).isEqualTo(slot.toString());
assertThat(request.getHeader("Accept"))
.isEqualTo("application/octet-stream;q=0.9, application/json;q=0.4");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public enum ValidatorApiMethod {
SEND_SIGNED_BLOCK_V2("eth/v2/beacon/blocks"),
SEND_SIGNED_BLINDED_BLOCK_V2("eth/v2/beacon/blinded_blocks"),
GET_ATTESTATION_DATA("eth/v1/validator/attestation_data"),
GET_PAYLOAD_ATTESTATION_DATA("eth/v1/validator/payload_attestation_data/:slot"),
GET_PAYLOAD_ATTESTATION_DATA("eth/v1/validator/payload_attestation_data"),
SEND_SIGNED_ATTESTATION("eth/v1/beacon/pool/attestations"),
SEND_SIGNED_ATTESTATION_V2("eth/v2/beacon/pool/attestations"),
SEND_SIGNED_VOLUNTARY_EXIT("eth/v1/beacon/pool/voluntary_exits"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,16 @@ public Optional<PayloadAttestationData> submit(final UInt64 slot) {
(request, response) ->
handlePayloadAttestationDataResult(
request, response, payloadAttestationDataSchema, jsonResponseHandler));
final Map<String, String> urlParams = Map.of("slot", slot.toString());
final Map<String, String> queryParams = Map.of("slot", slot.toString());
final Map<String, String> headers =
Map.of("Accept", "application/octet-stream;q=0.9, application/json;q=0.4");
return get(
GET_PAYLOAD_ATTESTATION_DATA, urlParams, emptyMap(), emptyMap(), headers, responseHandler);
GET_PAYLOAD_ATTESTATION_DATA,
emptyMap(),
queryParams,
emptyMap(),
headers,
responseHandler);
}

private Optional<PayloadAttestationData> handlePayloadAttestationDataResult(
Expand Down
Loading