Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GetBlockAttestationV2 #8434

Merged
merged 6 commits into from
Jul 15, 2024
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Added a state pruner that can limit the number of finalized states stored when running an archive node.

- Updated bootnodes for Sepolia network.
- Implemented [GetBlockAttestationV2](https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlockAttestationsV2) (adding support for Electra attestations)

- Updated a number of parameters to reduce issues when using `p2p-subscribe-all-subnets-enabled`. If you have adjusted queue sizes manually when using all-subnets, please refer to details below. Manual settings will still override these defaults.
- When `p2p-subscribe-all-subnets-enabled`, `p2p-peer-lower-bound` now defaults to 60 (previously 64), and `p2p-peer-upper-bound` now defaults to 80 (previously 100).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"operationId" : "getBlockAttestations",
"summary" : "Get block attestations",
"description" : "Retrieves attestations included in requested block.",
"deprecated": true,
"parameters" : [ {
"name" : "block_id",
"required" : true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"get" : {
"tags" : [ "Beacon" ],
"operationId" : "getBlockAttestationsV2",
"summary" : "Get block attestations",
"description" : "Retrieves attestations included in requested block.",
"parameters" : [ {
"name" : "block_id",
"required" : true,
"in" : "path",
"schema" : {
"type" : "string",
"description" : "Block identifier. Can be one of: \"head\" (canonical head in node's view), \"genesis\", \"finalized\", <slot>, <hex encoded blockRoot with 0x prefix>.",
"example" : "head"
}
} ],
"responses" : {
"200" : {
"description" : "Request successful",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/GetBlockAttestationsResponseV2"
}
}
}
},
"404" : {
"description" : "Not found",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"400" : {
"description" : "The request could not be processed, check the response for more information.",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"500" : {
"description" : "Internal server error",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"title" : "GetBlockAttestationsResponseV2",
"type" : "object",
"required" : [ "execution_optimistic", "finalized", "version", "data" ],
"properties" : {
"execution_optimistic" : {
"type" : "boolean"
},
"finalized" : {
"type" : "boolean"
},
"version" : {
"type" : "string",
"enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ]
},
"data" : {
"type" : "object",
"oneOf" : [ {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/AttestationElectra"
}
}, {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/AttestationPhase0"
}
} ]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostSyncDuties;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostValidatorLiveness;
import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.GetBlock;
import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.GetBlockAttestationsV2;
import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.PostBlindedBlockV2;
import tech.pegasys.teku.beaconrestapi.handlers.v2.beacon.PostBlockV2;
import tech.pegasys.teku.beaconrestapi.handlers.v2.debug.GetChainHeadsV2;
Expand Down Expand Up @@ -231,6 +232,7 @@ private static RestApi create(
.endpoint(new GetBlockRoot(dataProvider))
.endpoint(new GetFinalizedBlockRoot(dataProvider))
.endpoint(new GetBlockAttestations(dataProvider, spec))
.endpoint(new GetBlockAttestationsV2(dataProvider, schemaCache))
.endpoint(new GetAttestations(dataProvider, spec))
.endpoint(new PostAttestation(dataProvider, schemaCache))
.endpoint(new GetAttesterSlashings(dataProvider, spec))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public GetBlockAttestations(final ChainDataProvider chainDataProvider, final Spe
.pathParam(PARAMETER_BLOCK_ID)
.response(SC_OK, "Request successful", getResponseType(spec))
.withNotFoundResponse()
.deprecated(true)
.build());
this.chainDataProvider = chainDataProvider;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright Consensys Software Inc., 2022
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.beaconrestapi.handlers.v2.beacon;

import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.PARAMETER_BLOCK_ID;
import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.MILESTONE_TYPE;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EXECUTION_OPTIMISTIC;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.FINALIZED;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_BEACON;
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BOOLEAN_TYPE;
import static tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition.listOf;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import tech.pegasys.teku.api.ChainDataProvider;
import tech.pegasys.teku.api.DataProvider;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition;
import tech.pegasys.teku.infrastructure.json.types.SerializableOneOfTypeDefinition;
import tech.pegasys.teku.infrastructure.json.types.SerializableOneOfTypeDefinitionBuilder;
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
import tech.pegasys.teku.infrastructure.restapi.endpoints.AsyncApiResponse;
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionCache;

public class GetBlockAttestationsV2 extends RestApiEndpoint {

public static final String ROUTE = "/eth/v2/beacon/blocks/{block_id}/attestations";
private final ChainDataProvider chainDataProvider;

public GetBlockAttestationsV2(
final DataProvider dataProvider, final SchemaDefinitionCache schemaDefinitionCache) {
this(dataProvider.getChainDataProvider(), schemaDefinitionCache);
}

public GetBlockAttestationsV2(
final ChainDataProvider chainDataProvider,
final SchemaDefinitionCache schemaDefinitionCache) {
super(
EndpointMetadata.get(ROUTE)
.operationId("getBlockAttestationsV2")
.summary("Get block attestations")
.description("Retrieves attestations included in requested block.")
.tags(TAG_BEACON)
.pathParam(PARAMETER_BLOCK_ID)
.response(SC_OK, "Request successful", getResponseType(schemaDefinitionCache))
.withNotFoundResponse()
.build());
this.chainDataProvider = chainDataProvider;
}

@Override
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
final SafeFuture<Optional<ObjectAndMetaData<List<Attestation>>>> future =
chainDataProvider.getBlockAttestations(request.getPathParameter(PARAMETER_BLOCK_ID));

request.respondAsync(
future.thenApply(
maybeObjectAndMetaData ->
maybeObjectAndMetaData
.map(AsyncApiResponse::respondOk)
.orElseGet(AsyncApiResponse::respondNotFound)));
}

@SuppressWarnings("unchecked")
private static SerializableTypeDefinition<ObjectAndMetaData<List<Attestation>>> getResponseType(
final SchemaDefinitionCache schemaDefinitionCache) {
final DeserializableTypeDefinition<Attestation> electraAttestationTypeDef =
(DeserializableTypeDefinition<Attestation>)
schemaDefinitionCache
.getSchemaDefinition(SpecMilestone.ELECTRA)
.getAttestationSchema()
.getJsonTypeDefinition();

final DeserializableTypeDefinition<Attestation> phase0AttestationTypeDef =
(DeserializableTypeDefinition<Attestation>)
schemaDefinitionCache
.getSchemaDefinition(SpecMilestone.PHASE0)
.getAttestationSchema()
.getJsonTypeDefinition();

final SerializableOneOfTypeDefinition<List<Attestation>> oneOfTypeDefinition =
new SerializableOneOfTypeDefinitionBuilder<List<Attestation>>()
.withType(electraAttestationsPredicate(), listOf(electraAttestationTypeDef))
.withType(phase0AttestationsPredicate(), listOf(phase0AttestationTypeDef))
.build();

return SerializableTypeDefinition.<ObjectAndMetaData<List<Attestation>>>object()
.name("GetBlockAttestationsResponseV2")
.withField(EXECUTION_OPTIMISTIC, BOOLEAN_TYPE, ObjectAndMetaData::isExecutionOptimistic)
.withField(FINALIZED, BOOLEAN_TYPE, ObjectAndMetaData::isFinalized)
.withField("version", MILESTONE_TYPE, ObjectAndMetaData::getMilestone)
.withField("data", oneOfTypeDefinition, ObjectAndMetaData::getData)
.build();
}

private static Predicate<List<Attestation>> phase0AttestationsPredicate() {
// Before Electra attestations do not require committee bits
return attestations -> attestations.isEmpty() || !attestations.get(0).requiresCommitteeBits();
}

private static Predicate<List<Attestation>> electraAttestationsPredicate() {
// Only once we are in Electra attestations will have committee bits
return attestations -> !attestations.isEmpty() && attestations.get(0).requiresCommitteeBits();
}
}
Loading