Skip to content

Commit

Permalink
Updated sendValidatorLiveness to use typedef client (#8498)
Browse files Browse the repository at this point in the history
  • Loading branch information
rolfyone authored Aug 5, 2024
1 parent 7455d50 commit 98a1cc4
Show file tree
Hide file tree
Showing 14 changed files with 258 additions and 191 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ private boolean validatorIsLive(
return validatorLivenessAtEpochs.stream()
.anyMatch(
validatorLivenessAtEpoch ->
validatorLivenessAtEpoch.getIndex().equals(validatorIndex)
validatorLivenessAtEpoch.index().equals(validatorIndex)
&& validatorLivenessAtEpoch.isLive());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,44 @@
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BOOLEAN_TYPE;
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.UINT64_TYPE;

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

public class ValidatorLivenessAtEpoch {
private final UInt64 index;
private final boolean isLive;
public record ValidatorLivenessAtEpoch(UInt64 index, boolean isLive) {

public ValidatorLivenessAtEpoch(final UInt64 index, final boolean isLive) {
this.index = index;
this.isLive = isLive;
public static DeserializableTypeDefinition<ValidatorLivenessAtEpoch> getJsonTypeDefinition() {
return DeserializableTypeDefinition.object(
ValidatorLivenessAtEpoch.class, ValidatorLivenessAtEpoch.Builder.class)
.initializer(Builder::new)
.finisher(Builder::build)
.withField("index", UINT64_TYPE, ValidatorLivenessAtEpoch::index, Builder::index)
.withField("is_live", BOOLEAN_TYPE, ValidatorLivenessAtEpoch::isLive, Builder::isLive)
.build();
}

public UInt64 getIndex() {
return index;
@Override
public String toString() {
return "ValidatorLivenessAtEpoch{" + "index=" + index + ", isLive=" + isLive + '}';
}

public boolean isLive() {
return isLive;
}
public static class Builder {
private UInt64 index;
private boolean isLive;

public static SerializableTypeDefinition<ValidatorLivenessAtEpoch> getJsonTypeDefinition() {
return SerializableTypeDefinition.object(ValidatorLivenessAtEpoch.class)
.withField("index", UINT64_TYPE, ValidatorLivenessAtEpoch::getIndex)
.withField("is_live", BOOLEAN_TYPE, ValidatorLivenessAtEpoch::isLive)
.build();
}
public Builder() {}

@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
public Builder isLive(final boolean isLive) {
this.isLive = isLive;
return this;
}
final ValidatorLivenessAtEpoch that = (ValidatorLivenessAtEpoch) o;
return isLive == that.isLive && Objects.equals(index, that.index);
}

@Override
public int hashCode() {
return Objects.hash(index, isLive);
}
public Builder index(final UInt64 index) {
this.index = index;
return this;
}

@Override
public String toString() {
return "ValidatorLivenessAtEpoch{" + "index=" + index + ", isLive=" + isLive + '}';
public ValidatorLivenessAtEpoch build() {
return new ValidatorLivenessAtEpoch(index, isLive);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ private void checkValidatorDoppelgangers(
doppelgangers.forEach(
doppelganger ->
detectedDoppelgangers.putIfAbsent(
doppelganger.getRight().getIndex(), doppelganger.getLeft()));
doppelganger.getRight().index(), doppelganger.getLeft()));
if (allKeysAreActive()) {
statusLog.doppelgangerDetectionEnd(
mapToAbbreviatedKeys(pubKeys).collect(Collectors.toSet()),
Expand All @@ -327,7 +327,7 @@ private void checkValidatorDoppelgangers(
private Map<UInt64, String> mapLivenessAtEpochToIndicesByPubKeyStrings(
final List<Pair<BLSPublicKey, ValidatorLivenessAtEpoch>> doppelgangers) {
return doppelgangers.stream()
.collect(Collectors.toMap(e -> e.getRight().getIndex(), e -> e.getLeft().toString()));
.collect(Collectors.toMap(e -> e.getRight().index(), e -> e.getLeft().toString()));
}

private Stream<String> mapToAbbreviatedKeys(final Set<BLSPublicKey> pubKeys) {
Expand All @@ -344,16 +344,15 @@ private List<Pair<BLSPublicKey, ValidatorLivenessAtEpoch>> filterLiveValidators(
.filter(
validatorLivenessAtEpoch ->
validatorPubKeysByIndices.containsValue(
validatorLivenessAtEpoch.getIndex())
validatorLivenessAtEpoch.index())
&& validatorLivenessAtEpoch.isLive())
.map(
validatorLivenessAtEpoch ->
Pair.of(
validatorPubKeysByIndices.entrySet().stream()
.filter(
e ->
e.getValue()
.equals(validatorLivenessAtEpoch.getIndex()))
e.getValue().equals(validatorLivenessAtEpoch.index()))
.findFirst()
.get()
.getKey(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import tech.pegasys.teku.spec.TestSpecContext;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.networks.Eth2Network;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionCache;
import tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod;
import tech.pegasys.teku.validator.remote.typedef.AbstractTypeDefRequestTestBase;

Expand All @@ -43,7 +44,8 @@ public class CreateAggregateAttestationRequestTest extends AbstractTypeDefReques
@BeforeEach
void setupRequest() {
createAggregateAttestationRequest =
new CreateAggregateAttestationRequest(mockWebServer.url("/"), okHttpClient, slot, spec);
new CreateAggregateAttestationRequest(
mockWebServer.url("/"), okHttpClient, new SchemaDefinitionCache(spec));
}

@TestTemplate
Expand All @@ -52,7 +54,7 @@ public void getAggregateAttestation_makesExpectedRequest() throws Exception {

mockWebServer.enqueue(new MockResponse().setResponseCode(SC_NO_CONTENT));

createAggregateAttestationRequest.createAggregate(attestationHashTreeRoot);
createAggregateAttestationRequest.createAggregate(slot, attestationHashTreeRoot);

final RecordedRequest request = mockWebServer.takeRequest();
assertThat(request.getMethod()).isEqualTo("GET");
Expand All @@ -76,7 +78,7 @@ public void shouldGetAggregateAttestation() {
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(mockResponse));

final Optional<Attestation> maybeAttestation =
createAggregateAttestationRequest.createAggregate(attestation.hashTreeRoot());
createAggregateAttestationRequest.createAggregate(slot, attestation.hashTreeRoot());

assertThat(maybeAttestation).isPresent();
assertThat(maybeAttestation.get()).isEqualTo(getAggregateAttestationResponse.getData());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class GetSyncingStatusRequestTest extends AbstractTypeDefRequestTestBase

@BeforeEach
void setupRequest() {
request = new GetSyncingStatusRequest(okHttpClient, mockWebServer.url("/"));
request = new GetSyncingStatusRequest(mockWebServer.url("/"), okHttpClient);
}

@TestTemplate
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.validator.remote.typedef.handlers;

import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.RecordedRequest;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;
import tech.pegasys.teku.api.migrated.ValidatorLivenessAtEpoch;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.TestSpecContext;
import tech.pegasys.teku.spec.networks.Eth2Network;
import tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod;
import tech.pegasys.teku.validator.remote.typedef.AbstractTypeDefRequestTestBase;

@TestSpecContext(milestone = SpecMilestone.PHASE0, network = Eth2Network.MINIMAL)
public class SendValidatorLivenessRequestTest extends AbstractTypeDefRequestTestBase {

private SendValidatorLivenessRequest sendValidatorLivenessRequest;

@BeforeEach
void setupRequest() {
sendValidatorLivenessRequest =
new SendValidatorLivenessRequest(mockWebServer.url("/"), okHttpClient);
}

@TestTemplate
public void sendValidatorLiveness_makesExpectedRequest() throws Exception {
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK));
sendValidatorLivenessRequest.submit(UInt64.ONE, List.of(UInt64.valueOf(1)));

final RecordedRequest request = mockWebServer.takeRequest();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getPath())
.contains(ValidatorApiMethod.SEND_VALIDATOR_LIVENESS.getPath(Map.of("epoch", "1")));
}

@TestTemplate
public void sendValidatorLiveness_readsResponse() throws Exception {
mockWebServer.enqueue(
new MockResponse()
.setResponseCode(SC_OK)
.setBody("{\"data\":[{\"index\":\"1\",\"is_live\":false}]}"));
final Optional<List<ValidatorLivenessAtEpoch>> result =
sendValidatorLivenessRequest.submit(UInt64.ONE, List.of(UInt64.valueOf(1)));

final RecordedRequest request = mockWebServer.takeRequest();
assertThat(request.getMethod()).isEqualTo("POST");
assertThat(request.getPath())
.contains(ValidatorApiMethod.SEND_VALIDATOR_LIVENESS.getPath(Map.of("epoch", "1")));

assertThat(result).isPresent();
assertThat(result.get()).containsExactly(new ValidatorLivenessAtEpoch(UInt64.ONE, false));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import tech.pegasys.teku.api.migrated.ValidatorLivenessAtEpoch;
import tech.pegasys.teku.api.response.v1.beacon.PostDataFailureResponse;
import tech.pegasys.teku.api.response.v1.beacon.ValidatorStatus;
import tech.pegasys.teku.api.response.v1.validator.PostValidatorLivenessResponse;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.bls.BLSSignature;
import tech.pegasys.teku.ethereum.json.types.beacon.StateValidatorData;
Expand Down Expand Up @@ -366,11 +365,7 @@ public SafeFuture<Void> registerValidators(
@Override
public SafeFuture<Optional<List<ValidatorLivenessAtEpoch>>> getValidatorsLiveness(
final List<UInt64> validatorIndices, final UInt64 epoch) {
return sendRequest(
() ->
apiClient
.sendValidatorsLiveness(epoch, validatorIndices)
.map(this::responseToValidatorsLivenessResult));
return sendRequest(() -> typeDefClient.sendValidatorsLiveness(epoch, validatorIndices));
}

@Override
Expand All @@ -385,16 +380,6 @@ public SafeFuture<Optional<List<SyncCommitteeSelectionProof>>> getSyncCommitteeS
return sendRequest(() -> typeDefClient.getSyncCommitteeSelectionProof(requests));
}

private List<ValidatorLivenessAtEpoch> responseToValidatorsLivenessResult(
final PostValidatorLivenessResponse response) {
return response.data.stream()
.map(
validatorLivenessAtEpoch ->
new ValidatorLivenessAtEpoch(
validatorLivenessAtEpoch.index, validatorLivenessAtEpoch.isLive))
.toList();
}

private SafeFuture<Void> sendRequest(final ExceptionThrowingRunnable requestExecutor) {
return sendRequest(
() -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@

package tech.pegasys.teku.validator.remote.apiclient;

import static java.util.Collections.emptyMap;
import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.SEND_SIGNED_AGGREGATE_AND_PROOF;
import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.SEND_SIGNED_ATTESTATION;
import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.SEND_SYNC_COMMITTEE_MESSAGES;
import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.SEND_VALIDATOR_LIVENESS;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -35,11 +34,9 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import tech.pegasys.teku.api.response.v1.beacon.PostDataFailureResponse;
import tech.pegasys.teku.api.response.v1.validator.PostValidatorLivenessResponse;
import tech.pegasys.teku.api.schema.Attestation;
import tech.pegasys.teku.api.schema.SignedAggregateAndProof;
import tech.pegasys.teku.api.schema.altair.SyncCommitteeMessage;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.provider.JsonProvider;

public class OkHttpValidatorRestApiClient implements ValidatorRestApiClient {
Expand All @@ -48,7 +45,6 @@ public class OkHttpValidatorRestApiClient implements ValidatorRestApiClient {

private static final MediaType APPLICATION_JSON =
MediaType.parse("application/json; charset=utf-8");
private static final Map<String, String> EMPTY_MAP = emptyMap();

private final JsonProvider jsonProvider = new JsonProvider();
private final OkHttpClient httpClient;
Expand All @@ -64,6 +60,7 @@ public Optional<PostDataFailureResponse> sendSignedAttestations(
final List<Attestation> attestations) {
return post(
SEND_SIGNED_ATTESTATION,
Collections.emptyMap(),
attestations,
ResponseHandler.createForEmptyOkAndContentInBadResponse(
jsonProvider, PostDataFailureResponse.class));
Expand All @@ -74,6 +71,7 @@ public Optional<PostDataFailureResponse> sendAggregateAndProofs(
final List<SignedAggregateAndProof> signedAggregateAndProof) {
return post(
SEND_SIGNED_AGGREGATE_AND_PROOF,
Collections.emptyMap(),
signedAggregateAndProof,
ResponseHandler.createForEmptyOkAndContentInBadResponse(
jsonProvider, PostDataFailureResponse.class));
Expand All @@ -84,31 +82,19 @@ public Optional<PostDataFailureResponse> sendSyncCommitteeMessages(
final List<SyncCommitteeMessage> syncCommitteeMessages) {
return post(
SEND_SYNC_COMMITTEE_MESSAGES,
Collections.emptyMap(),
syncCommitteeMessages,
ResponseHandler.createForEmptyOkAndContentInBadResponse(
jsonProvider, PostDataFailureResponse.class));
}

@Override
public Optional<PostValidatorLivenessResponse> sendValidatorsLiveness(
final UInt64 epoch, final List<UInt64> validatorsIndices) {
return post(
SEND_VALIDATOR_LIVENESS,
Map.of("epoch", epoch.toString()),
validatorsIndices,
createHandler(PostValidatorLivenessResponse.class));
}

private <T> ResponseHandler<T> createHandler(final Class<T> responseClass) {
return new ResponseHandler<>(jsonProvider, responseClass);
}

private <T> Optional<T> post(
final ValidatorApiMethod apiMethod,
final Map<String, String> urlParams,
final Object requestBodyObj,
final ResponseHandler<T> responseHandler) {
final HttpUrl.Builder httpUrlBuilder = urlBuilder(apiMethod, urlParams);
final HttpUrl.Builder httpUrlBuilder =
baseEndpoint.resolve(apiMethod.getPath(urlParams)).newBuilder();
final String requestBody;
try {
requestBody = jsonProvider.objectToJSON(requestBodyObj);
Expand All @@ -135,18 +121,6 @@ private Request.Builder requestBuilder() {
return builder;
}

private <T> Optional<T> post(
final ValidatorApiMethod apiMethod,
final Object requestBodyObj,
final ResponseHandler<T> responseHandler) {
return post(apiMethod, EMPTY_MAP, requestBodyObj, responseHandler);
}

private HttpUrl.Builder urlBuilder(
final ValidatorApiMethod apiMethod, final Map<String, String> urlParams) {
return baseEndpoint.resolve(apiMethod.getPath(urlParams)).newBuilder();
}

private <T> Optional<T> executeCall(
final Request request, final ResponseHandler<T> responseHandler) {
try (final Response response = httpClient.newCall(request).execute()) {
Expand Down
Loading

0 comments on commit 98a1cc4

Please sign in to comment.