Skip to content

Commit

Permalink
GetBlockAttestationV2 (#8434)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucassaldanha authored Jul 15, 2024
1 parent bae748c commit e4b7c9e
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 0 deletions.
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

0 comments on commit e4b7c9e

Please sign in to comment.