Skip to content

Commit

Permalink
Implementing /eth/v1/validator/beacon_committee_selections (Consensys…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucassaldanha authored Feb 26, 2024
1 parent ee1e296 commit 917beea
Show file tree
Hide file tree
Showing 20 changed files with 575 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.bls.BLSSignature;
import tech.pegasys.teku.ethereum.json.types.beacon.StateValidatorData;
import tech.pegasys.teku.ethereum.json.types.validator.BeaconCommitteeSelectionProof;
import tech.pegasys.teku.ethereum.json.types.validator.ProposerDuties;
import tech.pegasys.teku.ethereum.json.types.validator.ProposerDuty;
import tech.pegasys.teku.ethereum.performance.trackers.BlockProductionPerformance;
Expand Down Expand Up @@ -833,4 +834,10 @@ private List<ProposerDuty> getProposalSlotsForEpoch(final BeaconState state, fin
}
return proposerSlots;
}

@Override
public SafeFuture<Optional<List<BeaconCommitteeSelectionProof>>> getBeaconCommitteeSelectionProof(
final List<BeaconCommitteeSelectionProof> requests) {
throw new UnsupportedOperationException("This method is not implemented by the Beacon Node");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1196,6 +1196,12 @@ public void checkValidatorsDoppelganger_ShouldReturnDoppelgangerDetectionResult(
assertThat(validatorIsLive(validatorLivenessAtEpochsResult, thirdIndex)).isTrue();
}

@Test
public void getBeaconCommitteeSelectionProofShouldNotBeImplementedByBeaconNode() {
assertThatThrownBy(() -> validatorApiHandler.getBeaconCommitteeSelectionProof(List.of()))
.isInstanceOf(UnsupportedOperationException.class);
}

private boolean validatorIsLive(
List<ValidatorLivenessAtEpoch> validatorLivenessAtEpochs, UInt64 validatorIndex) {
return validatorLivenessAtEpochs.stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"post" : {
"tags" : [ "Validator" ],
"operationId" : "submitBeaconCommitteeSelections",
"summary" : "Determine if a distributed validator has been selected to aggregate attestations",
"description" : "This endpoint should be used by a validator client running as part of a distributed validator cluster, and is implemented by a distributed validator middleware client. This endpoint is used to exchange partial selection proofs for combined/aggregated selection proofs to allow a validator client to correctly determine if any of its validators has been selected to perform an attestation aggregation duty in a slot. Validator clients running in a distributed validator cluster must query this endpoint at the start of an epoch for the current and lookahead (next) epochs for all validators that have attester duties in the current and lookahead epochs. Consensus clients need not support this endpoint and may return a 501.",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/BeaconCommitteeSelectionProof"
}
}
}
}
},
"responses" : {
"200" : {
"description" : "Returns the threshold aggregated beacon committee selection proofs.",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/PostBeaconCommitteeSelectionsResponse"
}
}
}
},
"400" : {
"description" : "Invalid request syntax.",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"500" : {
"description" : "Internal server error",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"501" : {
"description" : "Not implemented",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
},
"503" : {
"description" : "Beacon node is currently syncing and not serving requests.",
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/HttpErrorResponse"
}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"title" : "BeaconCommitteeSelectionProof",
"type" : "object",
"required" : [ "validator_index", "slot", "selection_proof" ],
"properties" : {
"validator_index" : {
"type" : "string",
"description" : "integer string",
"example" : "1",
"format" : "integer"
},
"slot" : {
"type" : "string",
"description" : "unsigned 64 bit integer",
"example" : "1",
"format" : "uint64"
},
"selection_proof" : {
"type" : "string"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"title" : "PostBeaconCommitteeSelectionsResponse",
"type" : "object",
"required" : [ "data" ],
"properties" : {
"data" : {
"type" : "array",
"items" : {
"$ref" : "#/components/schemas/BeaconCommitteeSelectionProof"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.GetSyncCommitteeContribution;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostAggregateAndProofs;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostAttesterDuties;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostBeaconCommitteeSelections;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostContributionAndProofs;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostPrepareBeaconProposer;
import tech.pegasys.teku.beaconrestapi.handlers.v1.validator.PostRegisterValidator;
Expand Down Expand Up @@ -275,6 +276,8 @@ private static RestApi create(
.endpoint(new PostContributionAndProofs(dataProvider, schemaCache))
.endpoint(new PostPrepareBeaconProposer(dataProvider))
.endpoint(new PostRegisterValidator(dataProvider))
// Obol DVT Methods
.endpoint(new PostBeaconCommitteeSelections())
// Config Handlers
.endpoint(
new GetDepositContract(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.v1.validator;

import static tech.pegasys.teku.ethereum.json.types.validator.BeaconCommitteeSelectionProof.BEACON_COMMITTEE_SELECTION_PROOF;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_IMPLEMENTED;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_VALIDATOR;
import static tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition.listOf;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import tech.pegasys.teku.ethereum.json.types.validator.BeaconCommitteeSelectionProof;
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;

public class PostBeaconCommitteeSelections extends RestApiEndpoint {

public static final String ROUTE = "/eth/v1/validator/beacon_committee_selections";

private static final SerializableTypeDefinition<List<BeaconCommitteeSelectionProof>>
RESPONSE_TYPE =
SerializableTypeDefinition.<List<BeaconCommitteeSelectionProof>>object()
.name("PostBeaconCommitteeSelectionsResponse")
.withField("data", listOf(BEACON_COMMITTEE_SELECTION_PROOF), Function.identity())
.build();

public PostBeaconCommitteeSelections() {
super(
EndpointMetadata.post(ROUTE)
.operationId("submitBeaconCommitteeSelections")
.summary(
"Determine if a distributed validator has been selected to aggregate attestations")
.description(
"This endpoint should be used by a validator client running as part of a distributed validator cluster, "
+ "and is implemented by a distributed validator middleware client. This endpoint is used to "
+ "exchange partial selection proofs for combined/aggregated selection proofs to allow a validator "
+ "client to correctly determine if any of its validators has been selected to perform an "
+ "attestation aggregation duty in a slot. Validator clients running in a distributed validator "
+ "cluster must query this endpoint at the start of an epoch for the current and lookahead (next) "
+ "epochs for all validators that have attester duties in the current and lookahead epochs. Consensus"
+ " clients need not support this endpoint and may return a 501.")
.tags(TAG_VALIDATOR)
.requestBodyType(listOf(BEACON_COMMITTEE_SELECTION_PROOF))
.response(
SC_OK,
"Returns the threshold aggregated beacon committee selection proofs.",
RESPONSE_TYPE)
.withBadRequestResponse(Optional.of("Invalid request syntax."))
.withInternalErrorResponse()
.withNotImplementedResponse()
.withServiceUnavailableResponse()
.build());
}

@Override
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
request.respondError(SC_NOT_IMPLEMENTED, "Method not implemented by the Beacon Node");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright Consensys Software Inc., 2024
*
* 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.ethereum.json.types.validator;

import java.util.Objects;
import tech.pegasys.teku.infrastructure.json.types.CoreTypes;
import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;

public class BeaconCommitteeSelectionProof {

public static final DeserializableTypeDefinition<BeaconCommitteeSelectionProof>
BEACON_COMMITTEE_SELECTION_PROOF =
DeserializableTypeDefinition.object(
BeaconCommitteeSelectionProof.class, BeaconCommitteeSelectionProof.Builder.class)
.name("BeaconCommitteeSelectionProof")
.initializer(BeaconCommitteeSelectionProof::builder)
.finisher(BeaconCommitteeSelectionProof.Builder::build)
.withField(
"validator_index",
CoreTypes.INTEGER_TYPE,
BeaconCommitteeSelectionProof::getValidatorIndex,
BeaconCommitteeSelectionProof.Builder::validatorIndex)
.withField(
"slot",
CoreTypes.UINT64_TYPE,
BeaconCommitteeSelectionProof::getSlot,
BeaconCommitteeSelectionProof.Builder::slot)
.withField(
"selection_proof",
CoreTypes.STRING_TYPE,
BeaconCommitteeSelectionProof::getSelectionProof,
BeaconCommitteeSelectionProof.Builder::selectionProof)
.build();

private final int validatorIndex;
private final UInt64 slot;
private final String selectionProof;

private BeaconCommitteeSelectionProof(
final int validatorIndex, final UInt64 slot, final String selectionProof) {
this.validatorIndex = validatorIndex;
this.slot = slot;
this.selectionProof = selectionProof;
}

public int getValidatorIndex() {
return validatorIndex;
}

public UInt64 getSlot() {
return slot;
}

public String getSelectionProof() {
return selectionProof;
}

public static BeaconCommitteeSelectionProof.Builder builder() {
return new BeaconCommitteeSelectionProof.Builder();
}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final BeaconCommitteeSelectionProof that = (BeaconCommitteeSelectionProof) o;
return validatorIndex == that.validatorIndex
&& Objects.equals(slot, that.slot)
&& Objects.equals(selectionProof, that.selectionProof);
}

@Override
public int hashCode() {
return Objects.hash(validatorIndex, slot, selectionProof);
}

public static class Builder {

private int validatorIndex;
private UInt64 slot;
private String selectionProof;

public Builder validatorIndex(final int validatorIndex) {
this.validatorIndex = validatorIndex;
return this;
}

public Builder slot(final UInt64 slot) {
this.slot = slot;
return this;
}

public Builder selectionProof(final String selectionProof) {
this.selectionProof = selectionProof;
return this;
}

public BeaconCommitteeSelectionProof build() {
return new BeaconCommitteeSelectionProof(validatorIndex, slot, selectionProof);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import tech.pegasys.teku.api.response.v1.beacon.ValidatorStatus;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.bls.BLSSignature;
import tech.pegasys.teku.ethereum.json.types.validator.BeaconCommitteeSelectionProof;
import tech.pegasys.teku.ethereum.json.types.validator.ProposerDuties;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.events.ChannelInterface;
Expand Down Expand Up @@ -174,6 +175,12 @@ public SafeFuture<Optional<List<ValidatorLivenessAtEpoch>>> getValidatorsLivenes
List<UInt64> validatorIndices, UInt64 epoch) {
return SafeFuture.completedFuture(Optional.empty());
}

@Override
public SafeFuture<Optional<List<BeaconCommitteeSelectionProof>>>
getBeaconCommitteeSelectionProof(final List<BeaconCommitteeSelectionProof> requests) {
return SafeFuture.completedFuture(Optional.of(requests));
}
};

int UNKNOWN_VALIDATOR_ID = -1;
Expand Down Expand Up @@ -250,4 +257,7 @@ SafeFuture<Void> prepareBeaconProposer(

SafeFuture<Optional<List<ValidatorLivenessAtEpoch>>> getValidatorsLiveness(
List<UInt64> validatorIndices, UInt64 epoch);

SafeFuture<Optional<List<BeaconCommitteeSelectionProof>>> getBeaconCommitteeSelectionProof(
List<BeaconCommitteeSelectionProof> requests);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ public class BeaconNodeRequestLabels {
public static final String PREPARE_BEACON_PROPOSERS_METHOD = "prepare_beacon_proposers";
public static final String REGISTER_VALIDATORS_METHOD = "register_validators";
public static final String GET_VALIDATORS_LIVENESS = "get_validators_liveness";
public static final String BEACON_COMMITTEE_SELECTIONS = "beacon_committee_selections";
}
Loading

0 comments on commit 917beea

Please sign in to comment.