Skip to content

Commit

Permalink
GET graffiti keymanager api implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
courtneyeh committed Apr 15, 2024
1 parent 064ce08 commit dc47d80
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public static RestApi create(
.endpoint(new DeleteFeeRecipient(proposerConfigManager))
.endpoint(new DeleteGasLimit(proposerConfigManager))
.endpoint(new PostVoluntaryExit(voluntaryExitDataProvider))
.endpoint(new GetGraffiti())
.endpoint(new GetGraffiti(keyManager))
.sslCertificate(config.getRestApiKeystoreFile(), config.getRestApiKeystorePasswordFile())
.passwordFilePath(validatorApiBearerFile)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,29 @@
package tech.pegasys.teku.validator.client.restapi.apis;

import static tech.pegasys.teku.ethereum.json.types.SharedApiTypes.PUBKEY_API_TYPE;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.STRING_TYPE;
import static tech.pegasys.teku.validator.client.restapi.ValidatorRestApi.TAG_GRAFFITI;
import static tech.pegasys.teku.validator.client.restapi.ValidatorTypes.PARAM_PUBKEY_TYPE;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.bls.BLSPublicKey;
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;
import tech.pegasys.teku.validator.client.KeyManager;
import tech.pegasys.teku.validator.client.Validator;

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

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

public GetGraffiti() {
public GetGraffiti(final KeyManager keyManager) {
super(
EndpointMetadata.get(ROUTE)
.operationId("getGraffiti")
Expand All @@ -60,11 +65,25 @@ public GetGraffiti() {
.withNotFoundResponse()
.withNotImplementedResponse()
.build());
this.keyManager = keyManager;
}

@Override
public void handleRequest(RestApiRequest request) throws JsonProcessingException {
throw new NotImplementedException("Not implemented");
final BLSPublicKey publicKey = request.getPathParameter(PARAM_PUBKEY_TYPE);

final Optional<Validator> maybeValidator = keyManager.getValidatorByPublicKey(publicKey);
if (maybeValidator.isEmpty()) {
request.respondError(SC_NOT_FOUND, "Validator not found");
return;
}

String graffiti = maybeValidator.get().getGraffiti().map(this::processGraffitiBytes).orElse("");
request.respondOk(new GraffitiResponse(publicKey, graffiti));
}

private String processGraffitiBytes(final Bytes32 graffiti) {
return new String(graffiti.toArrayUnsafe(), StandardCharsets.UTF_8).strip().replace("\0", "");
}

static class GraffitiResponse {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,102 @@
package tech.pegasys.teku.validator.client.restapi.apis;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_FORBIDDEN;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
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.HttpStatusCodes.SC_UNAUTHORIZED;
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.getResponseStringFromMetadata;
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataErrorResponse;

import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.IOException;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.infrastructure.http.HttpErrorResponse;
import tech.pegasys.teku.infrastructure.restapi.StubRestApiRequest;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.signatures.Signer;
import tech.pegasys.teku.spec.util.DataStructureUtil;
import tech.pegasys.teku.validator.api.Bytes32Parser;
import tech.pegasys.teku.validator.client.OwnedKeyManager;
import tech.pegasys.teku.validator.client.Validator;

class GetGraffitiTest {
private final GetGraffiti handler = new GetGraffiti();
private final OwnedKeyManager keyManager = mock(OwnedKeyManager.class);
private final GetGraffiti handler = new GetGraffiti(keyManager);
private StubRestApiRequest request;

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

@Test
void shouldGetGraffiti() throws JsonProcessingException {
final BLSPublicKey publicKey = dataStructureUtil.randomPublicKey();
final String stringGraffiti = "Test graffiti";
final Bytes32 graffiti = Bytes32Parser.toBytes32(stringGraffiti);

request =
StubRestApiRequest.builder()
.metadata(handler.getMetadata())
.pathParameter("pubkey", publicKey.toHexString())
.build();

final Validator validator =
new Validator(publicKey, mock(Signer.class), () -> Optional.of(graffiti));
when(keyManager.getValidatorByPublicKey(eq(publicKey))).thenReturn(Optional.of(validator));

handler.handleRequest(request);

GetGraffiti.GraffitiResponse expectedResponse =
new GetGraffiti.GraffitiResponse(publicKey, stringGraffiti);
assertThat(request.getResponseCode()).isEqualTo(SC_OK);
assertThat(request.getResponseBody()).isEqualTo(expectedResponse);
}

@Test
void shouldGetEmptyGraffiti() throws JsonProcessingException {
final BLSPublicKey publicKey = dataStructureUtil.randomPublicKey();
request =
StubRestApiRequest.builder()
.metadata(handler.getMetadata())
.pathParameter("pubkey", publicKey.toHexString())
.build();

final Validator validator = new Validator(publicKey, mock(Signer.class), Optional::empty);
when(keyManager.getValidatorByPublicKey(eq(publicKey))).thenReturn(Optional.of(validator));

handler.handleRequest(request);

GetGraffiti.GraffitiResponse expectedResponse = new GetGraffiti.GraffitiResponse(publicKey, "");
assertThat(request.getResponseCode()).isEqualTo(SC_OK);
assertThat(request.getResponseBody()).isEqualTo(expectedResponse);
}

@Test
void shouldHandleValidatorNotFound() throws IOException {
request =
StubRestApiRequest.builder()
.metadata(handler.getMetadata())
.pathParameter("pubkey", dataStructureUtil.randomPublicKey().toHexString())
.build();

when(keyManager.getValidatorByPublicKey(any())).thenReturn(Optional.empty());

handler.handleRequest(request);
assertThat(request.getResponseCode()).isEqualTo(SC_NOT_FOUND);
assertThat(request.getResponseBody())
.isEqualTo(new HttpErrorResponse(SC_NOT_FOUND, "Validator not found"));
}

@Test
void metadata_shouldHandle200() throws JsonProcessingException {
final GetGraffiti.GraffitiResponse response =
Expand Down

0 comments on commit dc47d80

Please sign in to comment.