Skip to content

Commit dc47d80

Browse files
committed
GET graffiti keymanager api implementation
1 parent 064ce08 commit dc47d80

File tree

3 files changed

+100
-5
lines changed

3 files changed

+100
-5
lines changed

Diff for: validator/client/src/main/java/tech/pegasys/teku/validator/client/restapi/ValidatorRestApi.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ public static RestApi create(
130130
.endpoint(new DeleteFeeRecipient(proposerConfigManager))
131131
.endpoint(new DeleteGasLimit(proposerConfigManager))
132132
.endpoint(new PostVoluntaryExit(voluntaryExitDataProvider))
133-
.endpoint(new GetGraffiti())
133+
.endpoint(new GetGraffiti(keyManager))
134134
.sslCertificate(config.getRestApiKeystoreFile(), config.getRestApiKeystorePasswordFile())
135135
.passwordFilePath(validatorApiBearerFile)
136136
.build();

Diff for: validator/client/src/main/java/tech/pegasys/teku/validator/client/restapi/apis/GetGraffiti.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,29 @@
1414
package tech.pegasys.teku.validator.client.restapi.apis;
1515

1616
import static tech.pegasys.teku.ethereum.json.types.SharedApiTypes.PUBKEY_API_TYPE;
17+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
1718
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
1819
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.STRING_TYPE;
1920
import static tech.pegasys.teku.validator.client.restapi.ValidatorRestApi.TAG_GRAFFITI;
2021
import static tech.pegasys.teku.validator.client.restapi.ValidatorTypes.PARAM_PUBKEY_TYPE;
2122

2223
import com.fasterxml.jackson.core.JsonProcessingException;
24+
import java.nio.charset.StandardCharsets;
2325
import java.util.Objects;
2426
import java.util.Optional;
2527
import java.util.function.Function;
26-
import org.apache.commons.lang3.NotImplementedException;
28+
import org.apache.tuweni.bytes.Bytes32;
2729
import tech.pegasys.teku.bls.BLSPublicKey;
2830
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
2931
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
3032
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
3133
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
34+
import tech.pegasys.teku.validator.client.KeyManager;
35+
import tech.pegasys.teku.validator.client.Validator;
3236

3337
public class GetGraffiti extends RestApiEndpoint {
3438
public static final String ROUTE = "/eth/v1/validator/{pubkey}/graffiti";
39+
private final KeyManager keyManager;
3540

3641
private static final SerializableTypeDefinition<GraffitiResponse> GRAFFITI_TYPE =
3742
SerializableTypeDefinition.object(GraffitiResponse.class)
@@ -45,7 +50,7 @@ public class GetGraffiti extends RestApiEndpoint {
4550
.withField("data", GRAFFITI_TYPE, Function.identity())
4651
.build();
4752

48-
public GetGraffiti() {
53+
public GetGraffiti(final KeyManager keyManager) {
4954
super(
5055
EndpointMetadata.get(ROUTE)
5156
.operationId("getGraffiti")
@@ -60,11 +65,25 @@ public GetGraffiti() {
6065
.withNotFoundResponse()
6166
.withNotImplementedResponse()
6267
.build());
68+
this.keyManager = keyManager;
6369
}
6470

6571
@Override
6672
public void handleRequest(RestApiRequest request) throws JsonProcessingException {
67-
throw new NotImplementedException("Not implemented");
73+
final BLSPublicKey publicKey = request.getPathParameter(PARAM_PUBKEY_TYPE);
74+
75+
final Optional<Validator> maybeValidator = keyManager.getValidatorByPublicKey(publicKey);
76+
if (maybeValidator.isEmpty()) {
77+
request.respondError(SC_NOT_FOUND, "Validator not found");
78+
return;
79+
}
80+
81+
String graffiti = maybeValidator.get().getGraffiti().map(this::processGraffitiBytes).orElse("");
82+
request.respondOk(new GraffitiResponse(publicKey, graffiti));
83+
}
84+
85+
private String processGraffitiBytes(final Bytes32 graffiti) {
86+
return new String(graffiti.toArrayUnsafe(), StandardCharsets.UTF_8).strip().replace("\0", "");
6887
}
6988

7089
static class GraffitiResponse {

Diff for: validator/client/src/test/java/tech/pegasys/teku/validator/client/restapi/apis/GetGraffitiTest.java

+77-1
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,102 @@
1414
package tech.pegasys.teku.validator.client.restapi.apis;
1515

1616
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.mockito.ArgumentMatchers.any;
18+
import static org.mockito.ArgumentMatchers.eq;
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.when;
1721
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
1822
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_FORBIDDEN;
1923
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
24+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
2025
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_IMPLEMENTED;
2126
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
2227
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_UNAUTHORIZED;
2328
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.getResponseStringFromMetadata;
2429
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataErrorResponse;
2530

2631
import com.fasterxml.jackson.core.JsonProcessingException;
32+
import java.io.IOException;
33+
import java.util.Optional;
34+
import org.apache.tuweni.bytes.Bytes32;
2735
import org.junit.jupiter.api.Test;
36+
import tech.pegasys.teku.bls.BLSPublicKey;
37+
import tech.pegasys.teku.infrastructure.http.HttpErrorResponse;
38+
import tech.pegasys.teku.infrastructure.restapi.StubRestApiRequest;
2839
import tech.pegasys.teku.spec.TestSpecFactory;
40+
import tech.pegasys.teku.spec.signatures.Signer;
2941
import tech.pegasys.teku.spec.util.DataStructureUtil;
42+
import tech.pegasys.teku.validator.api.Bytes32Parser;
43+
import tech.pegasys.teku.validator.client.OwnedKeyManager;
44+
import tech.pegasys.teku.validator.client.Validator;
3045

3146
class GetGraffitiTest {
32-
private final GetGraffiti handler = new GetGraffiti();
47+
private final OwnedKeyManager keyManager = mock(OwnedKeyManager.class);
48+
private final GetGraffiti handler = new GetGraffiti(keyManager);
49+
private StubRestApiRequest request;
3350

3451
private final DataStructureUtil dataStructureUtil =
3552
new DataStructureUtil(TestSpecFactory.createDefault());
3653

54+
@Test
55+
void shouldGetGraffiti() throws JsonProcessingException {
56+
final BLSPublicKey publicKey = dataStructureUtil.randomPublicKey();
57+
final String stringGraffiti = "Test graffiti";
58+
final Bytes32 graffiti = Bytes32Parser.toBytes32(stringGraffiti);
59+
60+
request =
61+
StubRestApiRequest.builder()
62+
.metadata(handler.getMetadata())
63+
.pathParameter("pubkey", publicKey.toHexString())
64+
.build();
65+
66+
final Validator validator =
67+
new Validator(publicKey, mock(Signer.class), () -> Optional.of(graffiti));
68+
when(keyManager.getValidatorByPublicKey(eq(publicKey))).thenReturn(Optional.of(validator));
69+
70+
handler.handleRequest(request);
71+
72+
GetGraffiti.GraffitiResponse expectedResponse =
73+
new GetGraffiti.GraffitiResponse(publicKey, stringGraffiti);
74+
assertThat(request.getResponseCode()).isEqualTo(SC_OK);
75+
assertThat(request.getResponseBody()).isEqualTo(expectedResponse);
76+
}
77+
78+
@Test
79+
void shouldGetEmptyGraffiti() throws JsonProcessingException {
80+
final BLSPublicKey publicKey = dataStructureUtil.randomPublicKey();
81+
request =
82+
StubRestApiRequest.builder()
83+
.metadata(handler.getMetadata())
84+
.pathParameter("pubkey", publicKey.toHexString())
85+
.build();
86+
87+
final Validator validator = new Validator(publicKey, mock(Signer.class), Optional::empty);
88+
when(keyManager.getValidatorByPublicKey(eq(publicKey))).thenReturn(Optional.of(validator));
89+
90+
handler.handleRequest(request);
91+
92+
GetGraffiti.GraffitiResponse expectedResponse = new GetGraffiti.GraffitiResponse(publicKey, "");
93+
assertThat(request.getResponseCode()).isEqualTo(SC_OK);
94+
assertThat(request.getResponseBody()).isEqualTo(expectedResponse);
95+
}
96+
97+
@Test
98+
void shouldHandleValidatorNotFound() throws IOException {
99+
request =
100+
StubRestApiRequest.builder()
101+
.metadata(handler.getMetadata())
102+
.pathParameter("pubkey", dataStructureUtil.randomPublicKey().toHexString())
103+
.build();
104+
105+
when(keyManager.getValidatorByPublicKey(any())).thenReturn(Optional.empty());
106+
107+
handler.handleRequest(request);
108+
assertThat(request.getResponseCode()).isEqualTo(SC_NOT_FOUND);
109+
assertThat(request.getResponseBody())
110+
.isEqualTo(new HttpErrorResponse(SC_NOT_FOUND, "Validator not found"));
111+
}
112+
37113
@Test
38114
void metadata_shouldHandle200() throws JsonProcessingException {
39115
final GetGraffiti.GraffitiResponse response =

0 commit comments

Comments
 (0)