Skip to content

Commit 40d756e

Browse files
committed
Add set and delete graffiti
1 parent 90b3875 commit 40d756e

File tree

9 files changed

+222
-50
lines changed

9 files changed

+222
-50
lines changed

teku/src/main/java/tech/pegasys/teku/cli/subcommand/debug/DebugToolsCommand.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import tech.pegasys.teku.spec.SpecFactory;
4242
import tech.pegasys.teku.spec.datastructures.state.CommitteeAssignment;
4343
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
44+
import tech.pegasys.teku.validator.api.GraffitiManager;
4445
import tech.pegasys.teku.validator.api.ValidatorApiChannel;
4546
import tech.pegasys.teku.validator.beaconnode.GenesisDataProvider;
4647
import tech.pegasys.teku.validator.client.KeyManager;
@@ -165,7 +166,8 @@ public int generateSwaggerDocs(
165166
dataDirLayout,
166167
new SystemTimeProvider(),
167168
Optional.empty(),
168-
new DoppelgangerDetectionAlert());
169+
new DoppelgangerDetectionAlert(),
170+
new GraffitiManager(dataDirLayout));
169171

170172
if (api.getRestApiDocs().isPresent()) {
171173
final String docs = api.getRestApiDocs().get();

validator/client/src/main/java/tech/pegasys/teku/validator/client/ValidatorClientService.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,8 @@ public static ValidatorClientService create(
267267
services.getEventChannels().getPublisher(ValidatorTimingChannel.class)),
268268
services.getDataDirLayout(),
269269
services.getTimeProvider(),
270-
validatorClientService.maybeDoppelgangerDetector);
270+
validatorClientService.maybeDoppelgangerDetector,
271+
graffitiManager.orElseThrow());
271272
} else {
272273
LOG.info("validator-api-enabled is false, not starting rest api.");
273274
}
@@ -343,7 +344,8 @@ private void initializeValidatorRestApi(
343344
final OwnedKeyManager keyManager,
344345
final DataDirLayout dataDirLayout,
345346
final TimeProvider timeProvider,
346-
final Optional<DoppelgangerDetector> maybeDoppelgangerDetector) {
347+
final Optional<DoppelgangerDetector> maybeDoppelgangerDetector,
348+
final GraffitiManager graffitiManager) {
347349
final RestApi validatorRestApi =
348350
ValidatorRestApi.create(
349351
spec,
@@ -355,7 +357,8 @@ private void initializeValidatorRestApi(
355357
dataDirLayout,
356358
timeProvider,
357359
maybeDoppelgangerDetector,
358-
new DoppelgangerDetectionAlert());
360+
new DoppelgangerDetectionAlert(),
361+
graffitiManager);
359362
maybeValidatorRestApi = Optional.of(validatorRestApi);
360363
}
361364

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import tech.pegasys.teku.infrastructure.version.VersionProvider;
3030
import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout;
3131
import tech.pegasys.teku.spec.Spec;
32+
import tech.pegasys.teku.validator.api.GraffitiManager;
3233
import tech.pegasys.teku.validator.api.ValidatorApiChannel;
3334
import tech.pegasys.teku.validator.beaconnode.GenesisDataProvider;
3435
import tech.pegasys.teku.validator.client.KeyManager;
@@ -72,7 +73,8 @@ public static RestApi create(
7273
final DataDirLayout dataDirLayout,
7374
final TimeProvider timeProvider,
7475
final Optional<DoppelgangerDetector> maybeDoppelgangerDetector,
75-
final SlashingRiskAction doppelgangerDetectionAction) {
76+
final SlashingRiskAction doppelgangerDetectionAction,
77+
final GraffitiManager graffitiManager) {
7678
final VoluntaryExitDataProvider voluntaryExitDataProvider =
7779
new VoluntaryExitDataProvider(
7880
spec, keyManager, validatorApiChannel, genesisDataProvider, timeProvider);
@@ -133,8 +135,8 @@ public static RestApi create(
133135
.endpoint(new DeleteGasLimit(proposerConfigManager))
134136
.endpoint(new PostVoluntaryExit(voluntaryExitDataProvider))
135137
.endpoint(new GetGraffiti())
136-
.endpoint(new SetGraffiti())
137-
.endpoint(new DeleteGraffiti())
138+
.endpoint(new SetGraffiti(keyManager, graffitiManager))
139+
.endpoint(new DeleteGraffiti(keyManager, graffitiManager))
138140
.sslCertificate(config.getRestApiKeystoreFile(), config.getRestApiKeystorePasswordFile())
139141
.passwordFilePath(validatorApiBearerFile)
140142
.build();

validator/client/src/main/java/tech/pegasys/teku/validator/client/restapi/apis/DeleteGraffiti.java

+26-4
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,27 @@
1313

1414
package tech.pegasys.teku.validator.client.restapi.apis;
1515

16+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
17+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
1618
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
1719
import static tech.pegasys.teku.validator.client.restapi.ValidatorRestApi.TAG_GRAFFITI;
1820
import static tech.pegasys.teku.validator.client.restapi.ValidatorTypes.PARAM_PUBKEY_TYPE;
1921

2022
import com.fasterxml.jackson.core.JsonProcessingException;
21-
import org.apache.commons.lang3.NotImplementedException;
23+
import java.util.Optional;
24+
import tech.pegasys.teku.bls.BLSPublicKey;
2225
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
2326
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
2427
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
28+
import tech.pegasys.teku.validator.api.GraffitiManager;
29+
import tech.pegasys.teku.validator.client.KeyManager;
30+
import tech.pegasys.teku.validator.client.Validator;
2531

2632
public class DeleteGraffiti extends RestApiEndpoint {
33+
private final KeyManager keyManager;
34+
private final GraffitiManager graffitiManager;
2735

28-
public DeleteGraffiti() {
36+
public DeleteGraffiti(final KeyManager keyManager, final GraffitiManager graffitiManager) {
2937
super(
3038
EndpointMetadata.delete(GetGraffiti.ROUTE)
3139
.operationId("deleteGraffiti")
@@ -39,12 +47,26 @@ public DeleteGraffiti() {
3947
"Successfully removed the graffiti, or there was no graffiti set for the requested public key.")
4048
.withAuthenticationResponses()
4149
.withNotFoundResponse()
42-
.withNotImplementedResponse()
4350
.build());
51+
this.keyManager = keyManager;
52+
this.graffitiManager = graffitiManager;
4453
}
4554

4655
@Override
4756
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
48-
throw new NotImplementedException("Not Implemented");
57+
final BLSPublicKey publicKey = request.getPathParameter(PARAM_PUBKEY_TYPE);
58+
59+
final Optional<Validator> maybeValidator = keyManager.getValidatorByPublicKey(publicKey);
60+
if (maybeValidator.isEmpty()) {
61+
request.respondError(SC_NOT_FOUND, "Validator not found");
62+
return;
63+
}
64+
65+
final Optional<String> error = graffitiManager.deleteGraffiti(publicKey);
66+
if (error.isPresent()) {
67+
request.respondError(SC_INTERNAL_SERVER_ERROR, error.get());
68+
} else {
69+
request.respondWithCode(SC_NO_CONTENT);
70+
}
4971
}
5072
}

validator/client/src/main/java/tech/pegasys/teku/validator/client/restapi/apis/SetGraffiti.java

+27-4
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,28 @@
1313

1414
package tech.pegasys.teku.validator.client.restapi.apis;
1515

16+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
17+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
1618
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
1719
import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.STRING_TYPE;
1820
import static tech.pegasys.teku.validator.client.restapi.ValidatorRestApi.TAG_GRAFFITI;
1921
import static tech.pegasys.teku.validator.client.restapi.ValidatorTypes.PARAM_PUBKEY_TYPE;
2022

2123
import com.fasterxml.jackson.core.JsonProcessingException;
22-
import org.apache.commons.lang3.NotImplementedException;
24+
import java.util.Optional;
25+
import tech.pegasys.teku.bls.BLSPublicKey;
2326
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
2427
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
2528
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
29+
import tech.pegasys.teku.validator.api.GraffitiManager;
30+
import tech.pegasys.teku.validator.client.KeyManager;
31+
import tech.pegasys.teku.validator.client.Validator;
2632

2733
public class SetGraffiti extends RestApiEndpoint {
34+
private final KeyManager keyManager;
35+
private final GraffitiManager graffitiManager;
2836

29-
public SetGraffiti() {
37+
public SetGraffiti(final KeyManager keyManager, final GraffitiManager graffitiManager) {
3038
super(
3139
EndpointMetadata.post(GetGraffiti.ROUTE)
3240
.operationId("setGraffiti")
@@ -39,12 +47,27 @@ public SetGraffiti() {
3947
.response(SC_NO_CONTENT, "Successfully updated graffiti.")
4048
.withAuthenticationResponses()
4149
.withNotFoundResponse()
42-
.withNotImplementedResponse()
4350
.build());
51+
this.keyManager = keyManager;
52+
this.graffitiManager = graffitiManager;
4453
}
4554

4655
@Override
4756
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
48-
throw new NotImplementedException("Not Implemented");
57+
final BLSPublicKey publicKey = request.getPathParameter(PARAM_PUBKEY_TYPE);
58+
final String graffiti = request.getRequestBody();
59+
60+
final Optional<Validator> maybeValidator = keyManager.getValidatorByPublicKey(publicKey);
61+
if (maybeValidator.isEmpty()) {
62+
request.respondError(SC_NOT_FOUND, "Validator not found");
63+
return;
64+
}
65+
66+
final Optional<String> error = graffitiManager.setGraffiti(publicKey, graffiti);
67+
if (error.isPresent()) {
68+
request.respondError(SC_INTERNAL_SERVER_ERROR, error.get());
69+
} else {
70+
request.respondWithCode(SC_NO_CONTENT);
71+
}
4972
}
5073
}

validator/client/src/test/java/tech/pegasys/teku/validator/client/restapi/ValidatorOpenApiTest.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout;
3232
import tech.pegasys.teku.spec.Spec;
3333
import tech.pegasys.teku.spec.SpecFactory;
34+
import tech.pegasys.teku.validator.api.GraffitiManager;
3435
import tech.pegasys.teku.validator.api.ValidatorApiChannel;
3536
import tech.pegasys.teku.validator.beaconnode.GenesisDataProvider;
3637
import tech.pegasys.teku.validator.client.OwnedKeyManager;
@@ -69,7 +70,8 @@ void setup() throws IOException {
6970
dataDirLayout,
7071
new SystemTimeProvider(),
7172
Optional.empty(),
72-
doppelgangerDetectionAction);
73+
doppelgangerDetectionAction,
74+
new GraffitiManager(dataDirLayout));
7375
final Optional<String> maybeJson = restApi.getRestApiDocs();
7476
assertThat(maybeJson).isPresent();
7577
jsonNode = util.parseSwagger(maybeJson.orElseThrow());

validator/client/src/test/java/tech/pegasys/teku/validator/client/restapi/apis/DeleteGraffitiTest.java

+64-7
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,82 @@
1313

1414
package tech.pegasys.teku.validator.client.restapi.apis;
1515

16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.mockito.ArgumentMatchers.any;
18+
import static org.mockito.Mockito.mock;
19+
import static org.mockito.Mockito.when;
1620
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_BAD_REQUEST;
1721
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_FORBIDDEN;
1822
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR;
19-
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_IMPLEMENTED;
23+
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
2024
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT;
2125
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_UNAUTHORIZED;
2226
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataEmptyResponse;
2327
import static tech.pegasys.teku.infrastructure.restapi.MetadataTestUtil.verifyMetadataErrorResponse;
28+
import static tech.pegasys.teku.spec.generator.signatures.NoOpLocalSigner.NO_OP_SIGNER;
2429

2530
import com.fasterxml.jackson.core.JsonProcessingException;
31+
import java.io.IOException;
32+
import java.util.Optional;
2633
import org.junit.jupiter.api.Test;
34+
import tech.pegasys.teku.bls.BLSPublicKey;
35+
import tech.pegasys.teku.infrastructure.http.HttpErrorResponse;
36+
import tech.pegasys.teku.infrastructure.restapi.StubRestApiRequest;
37+
import tech.pegasys.teku.spec.TestSpecFactory;
38+
import tech.pegasys.teku.spec.util.DataStructureUtil;
39+
import tech.pegasys.teku.validator.api.GraffitiManager;
40+
import tech.pegasys.teku.validator.client.OwnedKeyManager;
41+
import tech.pegasys.teku.validator.client.Validator;
2742

2843
class DeleteGraffitiTest {
29-
private final DeleteGraffiti handler = new DeleteGraffiti();
44+
private final DataStructureUtil dataStructureUtil =
45+
new DataStructureUtil(TestSpecFactory.createDefault());
46+
47+
private final OwnedKeyManager keyManager = mock(OwnedKeyManager.class);
48+
private final GraffitiManager graffitiManager = mock(GraffitiManager.class);
49+
private final DeleteGraffiti handler = new DeleteGraffiti(keyManager, graffitiManager);
50+
51+
private final BLSPublicKey publicKey = dataStructureUtil.randomPublicKey();
52+
53+
private final StubRestApiRequest request =
54+
StubRestApiRequest.builder()
55+
.metadata(handler.getMetadata())
56+
.pathParameter("pubkey", publicKey.toHexString())
57+
.build();
58+
59+
@Test
60+
void shouldSuccessfullyDeleteGraffiti() throws JsonProcessingException {
61+
final Validator validator = new Validator(publicKey, NO_OP_SIGNER, Optional::empty);
62+
when(keyManager.getValidatorByPublicKey(any())).thenReturn(Optional.of(validator));
63+
when(graffitiManager.deleteGraffiti(any())).thenReturn(Optional.empty());
64+
65+
handler.handleRequest(request);
66+
67+
assertThat(request.getResponseCode()).isEqualTo(SC_NO_CONTENT);
68+
assertThat(request.getResponseBody()).isNull();
69+
}
70+
71+
@Test
72+
void shouldReturnErrorWhenIssueDeleting() throws JsonProcessingException {
73+
final Validator validator = new Validator(publicKey, NO_OP_SIGNER, Optional::empty);
74+
when(keyManager.getValidatorByPublicKey(any())).thenReturn(Optional.of(validator));
75+
when(graffitiManager.deleteGraffiti(any())).thenReturn(Optional.of("Error deleting graffiti"));
76+
77+
handler.handleRequest(request);
78+
79+
assertThat(request.getResponseCode()).isEqualTo(SC_INTERNAL_SERVER_ERROR);
80+
assertThat(request.getResponseBody())
81+
.isEqualTo(new HttpErrorResponse(SC_INTERNAL_SERVER_ERROR, "Error deleting graffiti"));
82+
}
83+
84+
@Test
85+
void shouldRespondNotFoundWhenNoValidator() throws IOException {
86+
when(keyManager.getValidatorByPublicKey(any())).thenReturn(Optional.empty());
87+
88+
handler.handleRequest(request);
89+
90+
assertThat(request.getResponseCode()).isEqualTo(SC_NOT_FOUND);
91+
}
3092

3193
@Test
3294
void metadata_shouldHandle204() {
@@ -52,9 +114,4 @@ void metadata_shouldHandle403() throws JsonProcessingException {
52114
void metadata_shouldHandle500() throws JsonProcessingException {
53115
verifyMetadataErrorResponse(handler, SC_INTERNAL_SERVER_ERROR);
54116
}
55-
56-
@Test
57-
void metadata_shouldHandle501() throws JsonProcessingException {
58-
verifyMetadataErrorResponse(handler, SC_NOT_IMPLEMENTED);
59-
}
60117
}

0 commit comments

Comments
 (0)