Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 53d456c

Browse files
authoredFeb 28, 2024··
Use JsonTypeDef for PostStateValidators request (Consensys#7985)
1 parent 11720af commit 53d456c

File tree

9 files changed

+185
-120
lines changed

9 files changed

+185
-120
lines changed
 

‎validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/apiclient/OkHttpValidatorRestApiClientTest.java

-45
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import static org.junit.jupiter.api.Assertions.fail;
2121
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
2222
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
23-
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_METHOD_NOT_ALLOWED;
2423
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
2524
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
2625
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
@@ -39,8 +38,6 @@
3938
import org.junit.jupiter.api.AfterEach;
4039
import org.junit.jupiter.api.BeforeEach;
4140
import org.junit.jupiter.api.Test;
42-
import org.junit.jupiter.params.ParameterizedTest;
43-
import org.junit.jupiter.params.provider.ValueSource;
4441
import tech.pegasys.teku.api.response.v1.beacon.GetGenesisResponse;
4542
import tech.pegasys.teku.api.response.v1.beacon.GetStateValidatorsResponse;
4643
import tech.pegasys.teku.api.response.v1.beacon.PostDataFailure;
@@ -151,48 +148,6 @@ public void getValidators_WhenSuccess_ReturnsResponse() {
151148
assertThat(result.get()).usingRecursiveComparison().isEqualTo(expected);
152149
}
153150

154-
@Test
155-
void postValidators_MakesExpectedRequest() throws Exception {
156-
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_NO_CONTENT));
157-
158-
apiClient.postValidators(List.of("1", "0x1234"));
159-
160-
final RecordedRequest request = mockWebServer.takeRequest();
161-
assertThat(request.getMethod()).isEqualTo("POST");
162-
assertThat(request.getPath()).contains(ValidatorApiMethod.GET_VALIDATORS.getPath(emptyMap()));
163-
assertThat(request.getBody().readUtf8()).isEqualTo("{\"ids\":[\"1\",\"0x1234\"]}");
164-
}
165-
166-
@Test
167-
public void postValidators_WhenNoContent_ReturnsEmpty() {
168-
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_NO_CONTENT));
169-
170-
assertThat(apiClient.postValidators(List.of("1"))).isEmpty();
171-
}
172-
173-
@ParameterizedTest
174-
@ValueSource(ints = {SC_BAD_REQUEST, SC_NOT_FOUND, SC_METHOD_NOT_ALLOWED})
175-
public void postValidators_WhenNotExisting_ThrowsException(final int responseCode) {
176-
mockWebServer.enqueue(new MockResponse().setResponseCode(responseCode));
177-
178-
assertThatThrownBy(() -> apiClient.postValidators(List.of("1")))
179-
.isInstanceOf(PostStateValidatorsNotExistingException.class);
180-
}
181-
182-
@Test
183-
public void postValidators_WhenSuccess_ReturnsResponse() {
184-
final List<ValidatorResponse> expected =
185-
List.of(schemaObjects.validatorResponse(), schemaObjects.validatorResponse());
186-
final GetStateValidatorsResponse response = new GetStateValidatorsResponse(false, expected);
187-
188-
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(asJson(response)));
189-
190-
Optional<List<ValidatorResponse>> result = apiClient.postValidators(List.of("1", "2"));
191-
192-
assertThat(result).isPresent();
193-
assertThat(result.get()).usingRecursiveComparison().isEqualTo(expected);
194-
}
195-
196151
@Test
197152
public void sendVoluntaryExit_makesExpectedRequest() throws Exception {
198153
final SignedVoluntaryExit exit = schemaObjects.signedVoluntaryExit();

‎validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java

+91-4
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,22 @@
1313

1414
package tech.pegasys.teku.validator.remote.typedef;
1515

16+
import static java.util.Collections.emptyMap;
1617
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1719
import static org.assertj.core.api.Assumptions.assumeThat;
20+
import static tech.pegasys.teku.ethereum.json.types.beacon.StateValidatorDataBuilder.STATE_VALIDATORS_RESPONSE_TYPE;
21+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
22+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_METHOD_NOT_ALLOWED;
23+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
24+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
25+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
26+
import static tech.pegasys.teku.infrastructure.json.JsonUtil.serialize;
27+
import static tech.pegasys.teku.spec.config.SpecConfig.FAR_FUTURE_EPOCH;
1828

1929
import com.fasterxml.jackson.core.JsonProcessingException;
2030
import com.fasterxml.jackson.databind.ObjectMapper;
31+
import java.util.List;
2132
import java.util.Optional;
2233
import okhttp3.mockwebserver.MockResponse;
2334
import okhttp3.mockwebserver.RecordedRequest;
@@ -26,7 +37,8 @@
2637
import org.junit.jupiter.api.BeforeEach;
2738
import org.junit.jupiter.api.TestTemplate;
2839
import tech.pegasys.teku.api.exceptions.RemoteServiceNotAvailableException;
29-
import tech.pegasys.teku.infrastructure.json.JsonUtil;
40+
import tech.pegasys.teku.api.response.v1.beacon.ValidatorStatus;
41+
import tech.pegasys.teku.ethereum.json.types.beacon.StateValidatorData;
3042
import tech.pegasys.teku.infrastructure.ssz.SszDataAssert;
3143
import tech.pegasys.teku.infrastructure.ssz.SszList;
3244
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
@@ -37,10 +49,14 @@
3749
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
3850
import tech.pegasys.teku.spec.datastructures.builder.SignedValidatorRegistration;
3951
import tech.pegasys.teku.spec.datastructures.metadata.BlockContainerAndMetaData;
52+
import tech.pegasys.teku.spec.datastructures.metadata.ObjectAndMetaData;
53+
import tech.pegasys.teku.spec.datastructures.state.Validator;
4054
import tech.pegasys.teku.spec.networks.Eth2Network;
4155
import tech.pegasys.teku.spec.schemas.ApiSchemas;
4256
import tech.pegasys.teku.validator.api.SendSignedBlockResult;
4357
import tech.pegasys.teku.validator.api.required.SyncingStatus;
58+
import tech.pegasys.teku.validator.remote.apiclient.PostStateValidatorsNotExistingException;
59+
import tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod;
4460
import tech.pegasys.teku.validator.remote.typedef.handlers.RegisterValidatorsRequest;
4561

4662
@TestSpecContext(allMilestones = true, network = Eth2Network.MINIMAL)
@@ -136,7 +152,7 @@ void publishesBlindedBlockJsonEncoded() throws InterruptedException, JsonProcess
136152
final RecordedRequest recordedRequest = mockWebServer.takeRequest();
137153

138154
final String expectedRequest =
139-
JsonUtil.serialize(
155+
serialize(
140156
signedBeaconBlock,
141157
spec.atSlot(UInt64.ONE)
142158
.getSchemaDefinitions()
@@ -193,7 +209,7 @@ void registerValidators_makesJsonRequest() throws InterruptedException, JsonProc
193209
dataStructureUtil.randomSignedValidatorRegistrations(5);
194210

195211
final String expectedRequest =
196-
JsonUtil.serialize(
212+
serialize(
197213
validatorRegistrations,
198214
ApiSchemas.SIGNED_VALIDATOR_REGISTRATIONS_SCHEMA.getJsonTypeDefinition());
199215

@@ -323,6 +339,77 @@ void blockV3ShouldFallbacksToBlockV2WhenNotFound()
323339
assertThat(secondRequest.getPath()).startsWith("/eth/v1/validator/blinded_blocks");
324340
}
325341

342+
@TestTemplate
343+
void postValidators_MakesExpectedRequest() throws Exception {
344+
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_NO_CONTENT));
345+
346+
okHttpValidatorTypeDefClient.postStateValidators(List.of("1", "0x1234"));
347+
348+
final RecordedRequest request = mockWebServer.takeRequest();
349+
assertThat(request.getMethod()).isEqualTo("POST");
350+
351+
assertThat(request.getPath()).contains(ValidatorApiMethod.GET_VALIDATORS.getPath(emptyMap()));
352+
assertThat(request.getBody().readUtf8()).isEqualTo("{\"ids\":[\"1\",\"0x1234\"]}");
353+
}
354+
355+
@TestTemplate
356+
public void postValidators_WhenNoContent_ReturnsEmpty() {
357+
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_NO_CONTENT));
358+
359+
assertThat(okHttpValidatorTypeDefClient.postStateValidators(List.of("1"))).isEmpty();
360+
}
361+
362+
@TestTemplate
363+
public void postValidators_WhenNotExisting_ThrowsException() {
364+
final List<Integer> responseCodes =
365+
List.of(SC_BAD_REQUEST, SC_NOT_FOUND, SC_METHOD_NOT_ALLOWED);
366+
for (int code : responseCodes) {
367+
checkThrowsExceptionForCode(code);
368+
}
369+
}
370+
371+
private void checkThrowsExceptionForCode(final int responseCode) {
372+
mockWebServer.enqueue(new MockResponse().setResponseCode(responseCode));
373+
assertThatThrownBy(() -> okHttpValidatorTypeDefClient.postStateValidators(List.of("1")))
374+
.isInstanceOf(PostStateValidatorsNotExistingException.class);
375+
}
376+
377+
@TestTemplate
378+
public void postValidators_WhenSuccess_ReturnsResponse() throws JsonProcessingException {
379+
final List<StateValidatorData> expected =
380+
List.of(generateStateValidatorData(), generateStateValidatorData());
381+
final ObjectAndMetaData<List<StateValidatorData>> response =
382+
new ObjectAndMetaData<>(expected, specMilestone, false, true, false);
383+
384+
final String body = serialize(response, STATE_VALIDATORS_RESPONSE_TYPE);
385+
mockWebServer.enqueue(new MockResponse().setResponseCode(SC_OK).setBody(body));
386+
387+
Optional<List<StateValidatorData>> result =
388+
okHttpValidatorTypeDefClient.postStateValidators(List.of("1", "2"));
389+
390+
assertThat(result).isPresent();
391+
assertThat(result.get()).isEqualTo(expected);
392+
}
393+
394+
private StateValidatorData generateStateValidatorData() {
395+
final long index = dataStructureUtil.randomLong();
396+
final Validator validator =
397+
new Validator(
398+
dataStructureUtil.randomPublicKey(),
399+
dataStructureUtil.randomBytes32(),
400+
dataStructureUtil.randomUInt64(),
401+
false,
402+
UInt64.ZERO,
403+
UInt64.ZERO,
404+
FAR_FUTURE_EPOCH,
405+
FAR_FUTURE_EPOCH);
406+
return new StateValidatorData(
407+
UInt64.valueOf(index),
408+
dataStructureUtil.randomUInt64(),
409+
ValidatorStatus.active_ongoing,
410+
validator);
411+
}
412+
326413
private void verifyRegisterValidatorsPostRequest(
327414
final RecordedRequest recordedRequest, final String expectedContentType) {
328415
assertThat(recordedRequest.getPath()).isEqualTo("/eth/v1/validator/register_validator");
@@ -341,7 +428,7 @@ private void assertJsonEquals(final String actual, final String expected) {
341428

342429
private String serializeBlockContainer(final BlockContainer blockContainer)
343430
throws JsonProcessingException {
344-
return JsonUtil.serialize(
431+
return serialize(
345432
blockContainer,
346433
blockContainer.isBlinded()
347434
? schemaDefinitions.getBlindedBlockContainerSchema().getJsonTypeDefinition()

‎validator/remote/src/main/java/tech/pegasys/teku/validator/remote/RemoteValidatorApiHandler.java

+9-16
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.apache.tuweni.bytes.Bytes32;
3939
import tech.pegasys.teku.api.migrated.ValidatorLivenessAtEpoch;
4040
import tech.pegasys.teku.api.response.v1.beacon.PostDataFailureResponse;
41-
import tech.pegasys.teku.api.response.v1.beacon.ValidatorResponse;
4241
import tech.pegasys.teku.api.response.v1.beacon.ValidatorStatus;
4342
import tech.pegasys.teku.api.response.v1.validator.PostSyncDutiesResponse;
4443
import tech.pegasys.teku.api.response.v1.validator.PostValidatorLivenessResponse;
@@ -136,29 +135,24 @@ public SafeFuture<Map<BLSPublicKey, Integer>> getValidatorIndices(
136135
}
137136
return sendRequest(
138137
() ->
139-
makeValidatorRequest(
140-
publicKeys, StateValidatorData::getIntegerIndex, ValidatorResponse::getIndex)
138+
makeValidatorRequest(publicKeys, StateValidatorData::getIntegerIndex)
141139
.orElse(emptyMap()));
142140
}
143141

144142
@Override
145143
public SafeFuture<Optional<Map<BLSPublicKey, ValidatorStatus>>> getValidatorStatuses(
146144
final Collection<BLSPublicKey> publicKeys) {
147-
return sendRequest(
148-
() ->
149-
makeValidatorRequest(
150-
publicKeys, StateValidatorData::getStatus, ValidatorResponse::getStatus));
145+
return sendRequest(() -> makeValidatorRequest(publicKeys, StateValidatorData::getStatus));
151146
}
152147

153148
private <T> Optional<Map<BLSPublicKey, T>> makeValidatorRequest(
154149
final Collection<BLSPublicKey> publicKeys,
155-
final Function<StateValidatorData, T> valueExtractor,
156-
final Function<ValidatorResponse, T> validatorResponseExtractor) {
150+
final Function<StateValidatorData, T> valueExtractor) {
157151
if (usePostValidatorsEndpoint.get()) {
158152
try {
159-
return apiClient
160-
.postValidators(convertPublicKeysToValidatorIds(publicKeys))
161-
.map(responses -> convertToValidatorMap(responses, validatorResponseExtractor));
153+
return typeDefClient
154+
.postStateValidators(convertPublicKeysToValidatorIds(publicKeys))
155+
.map(responses -> convertToValidatorMap(responses, valueExtractor));
162156
} catch (final PostStateValidatorsNotExistingException __) {
163157
LOG.debug(
164158
"POST method is not available for getting validators from state. Will use GET instead.");
@@ -175,10 +169,9 @@ private List<String> convertPublicKeysToValidatorIds(final Collection<BLSPublicK
175169
}
176170

177171
private <T> Map<BLSPublicKey, T> convertToValidatorMap(
178-
final List<ValidatorResponse> validatorResponses,
179-
final Function<ValidatorResponse, T> valueExtractor) {
180-
return validatorResponses.stream()
181-
.collect(toMap(ValidatorResponse::getPublicKey, valueExtractor));
172+
final List<StateValidatorData> validatorData,
173+
final Function<StateValidatorData, T> valueExtractor) {
174+
return validatorData.stream().collect(toMap(StateValidatorData::getPublicKey, valueExtractor));
182175
}
183176

184177
private <T> Optional<Map<BLSPublicKey, T>> makeBatchedValidatorRequest(

‎validator/remote/src/main/java/tech/pegasys/teku/validator/remote/apiclient/OkHttpValidatorRestApiClient.java

-26
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
package tech.pegasys.teku.validator.remote.apiclient;
1515

1616
import static java.util.Collections.emptyMap;
17-
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
18-
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_METHOD_NOT_ALLOWED;
1917
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
2018
import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.GET_AGGREGATE;
2119
import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.GET_ATTESTATION_DUTIES;
@@ -56,7 +54,6 @@
5654
import org.apache.logging.log4j.LogManager;
5755
import org.apache.logging.log4j.Logger;
5856
import org.apache.tuweni.bytes.Bytes32;
59-
import tech.pegasys.teku.api.request.v1.beacon.PostStateValidatorsRequest;
6057
import tech.pegasys.teku.api.request.v1.validator.BeaconCommitteeSubscriptionRequest;
6158
import tech.pegasys.teku.api.response.v1.beacon.GetBlockHeaderResponse;
6259
import tech.pegasys.teku.api.response.v1.beacon.GetGenesisResponse;
@@ -131,29 +128,6 @@ public Optional<List<ValidatorResponse>> getValidators(final List<String> valida
131128
.map(response -> response.data);
132129
}
133130

134-
/**
135-
* <a
136-
* href="https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/postStateValidators">POST
137-
* Get validators from state</a>
138-
*/
139-
@Override
140-
public Optional<List<ValidatorResponse>> postValidators(final List<String> validatorIds) {
141-
final PostStateValidatorsRequest requestBody = new PostStateValidatorsRequest(validatorIds);
142-
return post(
143-
GET_VALIDATORS,
144-
EMPTY_MAP,
145-
requestBody,
146-
createHandler(GetStateValidatorsResponse.class)
147-
.withHandler(
148-
(request, response) -> {
149-
throw new PostStateValidatorsNotExistingException();
150-
},
151-
SC_BAD_REQUEST,
152-
SC_NOT_FOUND,
153-
SC_METHOD_NOT_ALLOWED))
154-
.map(response -> response.data);
155-
}
156-
157131
@Override
158132
public Optional<PostAttesterDutiesResponse> getAttestationDuties(
159133
final UInt64 epoch, final Collection<Integer> validatorIndices) {

‎validator/remote/src/main/java/tech/pegasys/teku/validator/remote/apiclient/ValidatorRestApiClient.java

-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ public interface ValidatorRestApiClient {
4343

4444
Optional<List<ValidatorResponse>> getValidators(List<String> validatorIds);
4545

46-
Optional<List<ValidatorResponse>> postValidators(List<String> validatorIds);
47-
4846
Optional<PostAttesterDutiesResponse> getAttestationDuties(
4947
final UInt64 epoch, final Collection<Integer> validatorIndices);
5048

‎validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClient.java

+9
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import tech.pegasys.teku.validator.remote.typedef.handlers.GetProposerDutiesRequest;
4444
import tech.pegasys.teku.validator.remote.typedef.handlers.GetStateValidatorsRequest;
4545
import tech.pegasys.teku.validator.remote.typedef.handlers.GetSyncingStatusRequest;
46+
import tech.pegasys.teku.validator.remote.typedef.handlers.PostStateValidatorsRequest;
4647
import tech.pegasys.teku.validator.remote.typedef.handlers.ProduceBlockRequest;
4748
import tech.pegasys.teku.validator.remote.typedef.handlers.RegisterValidatorsRequest;
4849
import tech.pegasys.teku.validator.remote.typedef.handlers.SendSignedBlockRequest;
@@ -58,6 +59,7 @@ public class OkHttpValidatorTypeDefClient extends OkHttpValidatorMinimalTypeDefC
5859
private final GetGenesisRequest getGenesisRequest;
5960
private final GetProposerDutiesRequest getProposerDutiesRequest;
6061
private final GetStateValidatorsRequest getStateValidatorsRequest;
62+
private final PostStateValidatorsRequest postStateValidatorsRequest;
6163
private final SendSignedBlockRequest sendSignedBlockRequest;
6264
private final RegisterValidatorsRequest registerValidatorsRequest;
6365
private final CreateAttestationDataRequest createAttestationDataRequest;
@@ -76,6 +78,7 @@ public OkHttpValidatorTypeDefClient(
7678
this.getGenesisRequest = new GetGenesisRequest(okHttpClient, baseEndpoint);
7779
this.getProposerDutiesRequest = new GetProposerDutiesRequest(baseEndpoint, okHttpClient);
7880
this.getStateValidatorsRequest = new GetStateValidatorsRequest(baseEndpoint, okHttpClient);
81+
this.postStateValidatorsRequest = new PostStateValidatorsRequest(baseEndpoint, okHttpClient);
7982
this.sendSignedBlockRequest =
8083
new SendSignedBlockRequest(spec, baseEndpoint, okHttpClient, preferSszBlockEncoding);
8184
this.registerValidatorsRequest =
@@ -110,6 +113,12 @@ public Optional<List<StateValidatorData>> getStateValidators(final List<String>
110113
.map(ObjectAndMetaData::getData);
111114
}
112115

116+
public Optional<List<StateValidatorData>> postStateValidators(final List<String> validatorIds) {
117+
return postStateValidatorsRequest
118+
.postStateValidators(validatorIds)
119+
.map(ObjectAndMetaData::getData);
120+
}
121+
113122
public SendSignedBlockResult sendSignedBlock(final SignedBlockContainer blockContainer) {
114123
return sendSignedBlockRequest.sendSignedBlock(blockContainer);
115124
}

‎validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/ResponseHandler.java

+8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ public ResponseHandler<TObject> withHandler(
7878
return this;
7979
}
8080

81+
public ResponseHandler<TObject> withHandler(
82+
final Handler<TObject> handler, final int... responseCodes) {
83+
for (final int responseCode : responseCodes) {
84+
handlers.put(responseCode, handler);
85+
}
86+
return this;
87+
}
88+
8189
private Optional<TObject> defaultOkHandler(final Request request, final Response response)
8290
throws IOException {
8391
final ResponseBody responseBody = response.body();
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.