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

Updated sendValidatorLiveness to use typedef client #8498

Merged
merged 1 commit into from
Aug 5, 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
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