Skip to content

Commit 9693c3e

Browse files
committed
Handle API get and validator get graffiti differently
1 parent 00536fb commit 9693c3e

File tree

10 files changed

+114
-41
lines changed

10 files changed

+114
-41
lines changed

validator/api/src/main/java/tech/pegasys/teku/validator/api/GraffitiManagementException.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@
1313

1414
package tech.pegasys.teku.validator.api;
1515

16-
import java.io.IOException;
17-
18-
public class GraffitiManagementException extends IOException {
16+
public class GraffitiManagementException extends Exception {
1917

2018
public GraffitiManagementException(final String message) {
2119
super(message);

validator/api/src/main/java/tech/pegasys/teku/validator/api/GraffitiManager.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ public void deleteGraffiti(final BLSPublicKey publicKey) throws GraffitiManageme
7474
}
7575
}
7676

77-
public Optional<Bytes32> getGraffiti(final BLSPublicKey publicKey) {
77+
public Optional<Bytes32> getGraffiti(final BLSPublicKey publicKey)
78+
throws GraffitiManagementException {
7879
final Path filePath = directory.resolve(resolveFileName(publicKey));
7980
if (!filePath.toFile().exists()) {
8081
return Optional.empty();
@@ -83,8 +84,9 @@ public Optional<Bytes32> getGraffiti(final BLSPublicKey publicKey) {
8384
try {
8485
return Optional.of(GraffitiParser.loadFromFile(filePath));
8586
} catch (GraffitiLoaderException | IllegalArgumentException e) {
86-
LOG.error("Unable to read graffiti from storage.", e);
87-
return Optional.empty();
87+
LOG.error("Loading graffiti from graffiti storage failed.", e);
88+
throw new GraffitiManagementException(
89+
"Unable to retrieve stored graffiti for validator " + publicKey, e);
8890
}
8991
}
9092

validator/api/src/main/java/tech/pegasys/teku/validator/api/UpdatableGraffitiProvider.java

+16-3
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,34 @@
1414
package tech.pegasys.teku.validator.api;
1515

1616
import java.util.Optional;
17-
import java.util.function.Supplier;
1817
import org.apache.tuweni.bytes.Bytes32;
18+
import tech.pegasys.teku.infrastructure.async.ExceptionThrowingSupplier;
1919

2020
public class UpdatableGraffitiProvider implements GraffitiProvider {
21-
private final Supplier<Optional<Bytes32>> storageProvider;
21+
private final ExceptionThrowingSupplier<Optional<Bytes32>> storageProvider;
2222
private final GraffitiProvider defaultProvider;
2323

2424
public UpdatableGraffitiProvider(
25-
final Supplier<Optional<Bytes32>> storageProvider, final GraffitiProvider defaultProvider) {
25+
final ExceptionThrowingSupplier<Optional<Bytes32>> storageProvider,
26+
final GraffitiProvider defaultProvider) {
2627
this.storageProvider = storageProvider;
2728
this.defaultProvider = defaultProvider;
2829
}
2930

3031
@Override
3132
public Optional<Bytes32> get() {
33+
return getFromStorage().or(defaultProvider::get);
34+
}
35+
36+
private Optional<Bytes32> getFromStorage() {
37+
try {
38+
return storageProvider.get();
39+
} catch (Throwable e) {
40+
return Optional.empty();
41+
}
42+
}
43+
44+
public Optional<Bytes32> getGraffitiWithThrowable() throws Throwable {
3245
return storageProvider.get().or(defaultProvider::get);
3346
}
3447
}

validator/api/src/test/java/tech/pegasys/teku/validator/api/GraffitiManagerTest.java

+34-11
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void shouldThrowExceptionWhenUnableToCreateManagementDirectory(@TempDir final Pa
5656

5757
@Test
5858
void setGraffiti_shouldSetGraffitiWhenFileNotExist(@TempDir final Path tempDir)
59-
throws IOException {
59+
throws GraffitiManagementException {
6060
dataDirLayout = new SimpleDataDirLayout(tempDir);
6161
manager = new GraffitiManager(dataDirLayout);
6262
assertThat(getGraffitiManagementDir().toFile().exists()).isTrue();
@@ -66,7 +66,8 @@ void setGraffiti_shouldSetGraffitiWhenFileNotExist(@TempDir final Path tempDir)
6666
}
6767

6868
@Test
69-
void setGraffiti_shouldSetGraffitiWhenFileExist(@TempDir final Path tempDir) throws IOException {
69+
void setGraffiti_shouldSetGraffitiWhenFileExist(@TempDir final Path tempDir)
70+
throws IOException, GraffitiManagementException {
7071
dataDirLayout = new SimpleDataDirLayout(tempDir);
7172
manager = new GraffitiManager(dataDirLayout);
7273
assertThat(getGraffitiManagementDir().resolve(getFileName(publicKey)).toFile().createNewFile())
@@ -117,7 +118,7 @@ void deleteGraffiti_shouldSucceedWhenNoGraffitiToDelete(@TempDir final Path temp
117118

118119
@Test
119120
void deleteGraffiti_shouldDeleteGraffitiWhenFileExist(@TempDir final Path tempDir)
120-
throws IOException {
121+
throws IOException, GraffitiManagementException {
121122
dataDirLayout = new SimpleDataDirLayout(tempDir);
122123
manager = new GraffitiManager(dataDirLayout);
123124
assertThat(getGraffitiManagementDir().resolve(getFileName(publicKey)).toFile().createNewFile())
@@ -145,7 +146,7 @@ void deleteGraffiti_shouldReturnErrorMessageWhenUnableToDeleteFile(@TempDir fina
145146

146147
@Test
147148
void shouldSetAndDeleteGraffitiWhenManagementPreexisting(@TempDir final Path tempDir)
148-
throws IOException {
149+
throws GraffitiManagementException {
149150
dataDirLayout = new SimpleDataDirLayout(tempDir);
150151
final Path managementDir = getGraffitiManagementDir();
151152
assertThat(managementDir.toFile().mkdirs()).isTrue();
@@ -174,7 +175,8 @@ private void checkNoGraffitiFile(final BLSPublicKey publicKey) {
174175
}
175176

176177
@Test
177-
void getGraffiti_shouldGetGraffitiFromStorage(@TempDir final Path tempDir) throws IOException {
178+
void getGraffiti_shouldGetGraffitiFromStorage(@TempDir final Path tempDir)
179+
throws IOException, GraffitiManagementException {
178180
dataDirLayout = new SimpleDataDirLayout(tempDir);
179181
manager = new GraffitiManager(dataDirLayout);
180182
final Path filePath = getGraffitiManagementDir().resolve(getFileName(publicKey));
@@ -185,41 +187,62 @@ void getGraffiti_shouldGetGraffitiFromStorage(@TempDir final Path tempDir) throw
185187
}
186188

187189
@Test
188-
void getGraffiti_shouldReturnEmptyWhenFileNotExist(@TempDir final Path tempDir) {
190+
void getGraffiti_shouldReturnEmptyWhenFileNotExist(@TempDir final Path tempDir)
191+
throws GraffitiManagementException {
189192
dataDirLayout = new SimpleDataDirLayout(tempDir);
190193
manager = new GraffitiManager(dataDirLayout);
191194

192195
assertThat(manager.getGraffiti(publicKey)).isEmpty();
193196
}
194197

195198
@Test
196-
void getGraffiti_shouldReturnEmptyWhenFileTooBig(@TempDir final Path tempDir) throws IOException {
199+
void getGraffiti_shouldThrowExceptionWhenGraffitiOver32Bytes(@TempDir final Path tempDir)
200+
throws IOException {
197201
dataDirLayout = new SimpleDataDirLayout(tempDir);
198202
manager = new GraffitiManager(dataDirLayout);
199203

200204
final String invalidGraffiti = "This graffiti is a bit too long!!";
201205
final Path filePath = getGraffitiManagementDir().resolve(getFileName(publicKey));
202206
Files.writeString(filePath, invalidGraffiti);
203207

204-
assertThat(manager.getGraffiti(publicKey)).isEmpty();
208+
assertThatThrownBy(() -> manager.getGraffiti(publicKey))
209+
.isInstanceOf(GraffitiManagementException.class)
210+
.hasMessage("Unable to retrieve stored graffiti for validator " + publicKey);
211+
}
212+
213+
@Test
214+
void getGraffiti_shouldThrowExceptionWhenFileOver40Bytes(@TempDir final Path tempDir)
215+
throws IOException {
216+
dataDirLayout = new SimpleDataDirLayout(tempDir);
217+
manager = new GraffitiManager(dataDirLayout);
218+
219+
final String invalidGraffiti = "This graffiti is a bit too long to get from file!!";
220+
final Path filePath = getGraffitiManagementDir().resolve(getFileName(publicKey));
221+
Files.writeString(filePath, invalidGraffiti);
222+
223+
assertThatThrownBy(() -> manager.getGraffiti(publicKey))
224+
.isInstanceOf(GraffitiManagementException.class)
225+
.hasMessage("Unable to retrieve stored graffiti for validator " + publicKey);
205226
}
206227

207228
@Test
208229
@DisabledOnOs(OS.WINDOWS) // Can't set permissions on Windows
209-
void getGraffiti_shouldReturnEmptyWhenNotReadableFile(@TempDir final Path tempDir)
230+
void getGraffiti_shouldThrowExceptionWhenNotReadableFile(@TempDir final Path tempDir)
210231
throws IOException {
211232
dataDirLayout = new SimpleDataDirLayout(tempDir);
212233
manager = new GraffitiManager(dataDirLayout);
213234
final Path filePath = getGraffitiManagementDir().resolve(getFileName(publicKey));
214235
Files.writeString(filePath, graffiti);
215236
assertThat(filePath.toFile().setReadable(false)).isTrue();
216237

217-
assertThat(manager.getGraffiti(publicKey)).isEmpty();
238+
assertThatThrownBy(() -> manager.getGraffiti(publicKey))
239+
.isInstanceOf(GraffitiManagementException.class)
240+
.hasMessage("Unable to retrieve stored graffiti for validator " + publicKey);
218241
}
219242

220243
@Test
221244
void getGraffiti_shouldReturnEmptyBytesWhenFileEmpty(@TempDir final Path tempDir)
222-
throws IOException {
245+
throws IOException, GraffitiManagementException {
223246
dataDirLayout = new SimpleDataDirLayout(tempDir);
224247
manager = new GraffitiManager(dataDirLayout);
225248
final Path filePath = getGraffitiManagementDir().resolve(getFileName(publicKey));

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

-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

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

16-
import com.google.common.annotations.VisibleForTesting;
1716
import java.util.Optional;
1817
import org.apache.tuweni.bytes.Bytes32;
1918
import tech.pegasys.teku.bls.BLSPublicKey;
@@ -58,7 +57,6 @@ public boolean isReadOnly() {
5857
return readOnly;
5958
}
6059

61-
@VisibleForTesting
6260
public GraffitiProvider getGraffitiProvider() {
6361
return graffitiProvider;
6462
}

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

+12-7
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
import java.util.List;
2424
import java.util.Optional;
2525
import java.util.Random;
26-
import java.util.function.Function;
2726
import java.util.function.Supplier;
2827
import org.apache.logging.log4j.LogManager;
2928
import org.apache.logging.log4j.Logger;
3029
import org.apache.tuweni.bytes.Bytes32;
3130
import org.hyperledger.besu.plugin.services.MetricsSystem;
3231
import tech.pegasys.teku.bls.BLSPublicKey;
3332
import tech.pegasys.teku.infrastructure.async.AsyncRunner;
33+
import tech.pegasys.teku.infrastructure.async.ExceptionThrowingFunction;
3434
import tech.pegasys.teku.infrastructure.async.SafeFuture;
3535
import tech.pegasys.teku.infrastructure.events.EventChannels;
3636
import tech.pegasys.teku.infrastructure.exceptions.ExceptionUtil;
@@ -163,12 +163,17 @@ public static ValidatorClientService create(
163163
validatorApiConfig.isRestApiEnabled()
164164
? new GraffitiManager(services.getDataDirLayout())
165165
: null);
166+
final ExceptionThrowingFunction<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider =
167+
(publicKey) -> {
168+
if (graffitiManager.isPresent()) {
169+
final GraffitiManager manager = graffitiManager.get();
170+
return manager.getGraffiti(publicKey);
171+
}
172+
return Optional.empty();
173+
};
174+
166175
final ValidatorLoader validatorLoader =
167-
createValidatorLoader(
168-
services,
169-
config,
170-
asyncRunner,
171-
(publicKey) -> graffitiManager.flatMap(manager -> manager.getGraffiti(publicKey)));
176+
createValidatorLoader(services, config, asyncRunner, updatableGraffitiProvider);
172177
final ValidatorStatusProvider validatorStatusProvider =
173178
new OwnedValidatorStatusProvider(
174179
services.getMetricsSystem(),
@@ -409,7 +414,7 @@ private static ValidatorLoader createValidatorLoader(
409414
final ServiceConfig services,
410415
final ValidatorClientConfiguration config,
411416
final AsyncRunner asyncRunner,
412-
final Function<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider) {
417+
final ExceptionThrowingFunction<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider) {
413418
final Path slashingProtectionPath = getSlashingProtectionPath(services.getDataDirLayout());
414419
final SlashingProtector slashingProtector =
415420
config.getValidatorConfig().isLocalSlashingProtectionSynchronizedModeEnabled()

validator/client/src/main/java/tech/pegasys/teku/validator/client/loader/MultithreadedValidatorLoader.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
import java.util.concurrent.Executors;
2525
import java.util.concurrent.Future;
2626
import java.util.concurrent.atomic.AtomicInteger;
27-
import java.util.function.Function;
2827
import org.apache.tuweni.bytes.Bytes32;
2928
import tech.pegasys.teku.bls.BLSPublicKey;
29+
import tech.pegasys.teku.infrastructure.async.ExceptionThrowingFunction;
3030
import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout;
3131
import tech.pegasys.teku.spec.signatures.DeletableSigner;
3232
import tech.pegasys.teku.validator.api.GraffitiProvider;
@@ -49,7 +49,7 @@ public static void loadValidators(
4949
final OwnedValidators ownedValidators,
5050
final Map<BLSPublicKey, ValidatorProvider> providers,
5151
final GraffitiProvider defaultGraffitiProvider,
52-
final Function<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider,
52+
final ExceptionThrowingFunction<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider,
5353
final Optional<DataDirLayout> maybeDataDirLayout) {
5454
final int totalValidatorCount = providers.size();
5555
STATUS_LOG.loadingValidators(totalValidatorCount);

validator/client/src/main/java/tech/pegasys/teku/validator/client/loader/ValidatorLoader.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.List;
2121
import java.util.Map;
2222
import java.util.Optional;
23-
import java.util.function.Function;
2423
import java.util.function.Supplier;
2524
import org.apache.logging.log4j.LogManager;
2625
import org.apache.logging.log4j.Logger;
@@ -31,6 +30,7 @@
3130
import tech.pegasys.teku.bls.keystore.model.KeyStoreData;
3231
import tech.pegasys.teku.data.SlashingProtectionImporter;
3332
import tech.pegasys.teku.infrastructure.async.AsyncRunner;
33+
import tech.pegasys.teku.infrastructure.async.ExceptionThrowingFunction;
3434
import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout;
3535
import tech.pegasys.teku.spec.Spec;
3636
import tech.pegasys.teku.spec.signatures.DeletableSigner;
@@ -55,7 +55,8 @@ public class ValidatorLoader {
5555
private final Optional<ValidatorSource> mutableExternalValidatorSource;
5656
private final OwnedValidators ownedValidators = new OwnedValidators();
5757
private final GraffitiProvider defaultGraffitiProvider;
58-
private final Function<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider;
58+
private final ExceptionThrowingFunction<BLSPublicKey, Optional<Bytes32>>
59+
updatableGraffitiProvider;
5960
private final Optional<DataDirLayout> maybeDataDirLayout;
6061
private final SlashingProtectionLogger slashingProtectionLogger;
6162

@@ -64,7 +65,7 @@ private ValidatorLoader(
6465
final Optional<ValidatorSource> mutableLocalValidatorSource,
6566
final Optional<ValidatorSource> mutableExternalValidatorSource,
6667
final GraffitiProvider defaultGraffitiProvider,
67-
final Function<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider,
68+
final ExceptionThrowingFunction<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider,
6869
final Optional<DataDirLayout> maybeDataDirLayout,
6970
final SlashingProtectionLogger slashingProtectionLogger) {
7071
this.validatorSources = validatorSources;
@@ -241,7 +242,7 @@ public static ValidatorLoader create(
241242
final AsyncRunner asyncRunner,
242243
final MetricsSystem metricsSystem,
243244
final Optional<DataDirLayout> maybeMutableDir,
244-
final Function<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider) {
245+
final ExceptionThrowingFunction<BLSPublicKey, Optional<Bytes32>> updatableGraffitiProvider) {
245246
final ValidatorSourceFactory validatorSources =
246247
new ValidatorSourceFactory(
247248
spec,

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
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_INTERNAL_SERVER_ERROR;
1718
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND;
1819
import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK;
1920
import static tech.pegasys.teku.validator.client.restapi.ValidatorRestApi.TAG_GRAFFITI;
@@ -33,6 +34,7 @@
3334
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
3435
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
3536
import tech.pegasys.teku.validator.api.Bytes32Parser;
37+
import tech.pegasys.teku.validator.api.UpdatableGraffitiProvider;
3638
import tech.pegasys.teku.validator.client.KeyManager;
3739
import tech.pegasys.teku.validator.client.Validator;
3840

@@ -88,7 +90,13 @@ public void handleRequest(final RestApiRequest request) throws JsonProcessingExc
8890
return;
8991
}
9092

91-
request.respondOk(new GraffitiResponse(publicKey, maybeValidator.get().getGraffiti()));
93+
try {
94+
final UpdatableGraffitiProvider provider =
95+
(UpdatableGraffitiProvider) maybeValidator.get().getGraffitiProvider();
96+
request.respondOk(new GraffitiResponse(publicKey, provider.getGraffitiWithThrowable()));
97+
} catch (Throwable e) {
98+
request.respondError(SC_INTERNAL_SERVER_ERROR, e.getMessage());
99+
}
92100
}
93101

94102
private static String processGraffitiString(final Bytes32 graffiti) {

0 commit comments

Comments
 (0)