Skip to content

Commit

Permalink
Create graffiti management (Consensys#8216)
Browse files Browse the repository at this point in the history
  • Loading branch information
courtneyeh authored Apr 23, 2024
1 parent 533fcc8 commit a3683f7
Show file tree
Hide file tree
Showing 25 changed files with 1,459 additions and 98 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ the [releases page](https://github.com/Consensys/teku/releases).
- Updated the bootnodes for Chiado and Gnosis networks.
- Added hidden option `--Xp2p-dumps-to-file-enabled` to enable saving p2p dumps to file.
- Appends consensus layer (CL) and execution layer (EL) clients' information to the validator graffiti. Check [documentation](https://docs.teku.consensys.io/development/reference/cli#validators-graffiti-client-append-format) for available configuration options.
- Added support for [Graffiti management](https://ethereum.github.io/keymanager-APIs/?urls.primaryName=dev#/Graffiti) in the Key Manager API.

### Bug Fixes
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,8 @@ private void initialise() {
externalSignerHttpClientFactory, validatorConfig.getValidatorExternalSignerUrl()),
asyncRunner,
metricsSystem,
dataDirLayout);
dataDirLayout,
(publicKey) -> Optional.empty());

validatorLoader.loadValidators();
final Map<BLSPublicKey, Validator> ownedValidators =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import tech.pegasys.teku.spec.datastructures.state.CommitteeAssignment;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.validator.api.ValidatorApiChannel;
import tech.pegasys.teku.validator.api.noop.NoOpGraffitiManager;
import tech.pegasys.teku.validator.beaconnode.GenesisDataProvider;
import tech.pegasys.teku.validator.client.KeyManager;
import tech.pegasys.teku.validator.client.NoOpKeyManager;
Expand Down Expand Up @@ -165,7 +166,8 @@ public int generateSwaggerDocs(
dataDirLayout,
new SystemTimeProvider(),
Optional.empty(),
new DoppelgangerDetectionAlert());
new DoppelgangerDetectionAlert(),
new NoOpGraffitiManager());

if (api.getRestApiDocs().isPresent()) {
final String docs = api.getRestApiDocs().get();
Expand Down
2 changes: 2 additions & 0 deletions validator/api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dependencies {
implementation project(':infrastructure:events')
implementation project(':infrastructure:exceptions')
implementation project(':infrastructure:http')
implementation project(':infrastructure:serviceutils')
implementation project(':ethereum:execution-types')
implementation project(':ethereum:json-types')
implementation project(':ethereum:spec')
Expand All @@ -13,6 +14,7 @@ dependencies {

testImplementation testFixtures(project(':infrastructure:bls'))
testImplementation testFixtures(project(':infrastructure:logging'))
testImplementation testFixtures(project(':infrastructure:serviceutils'))
testImplementation testFixtures(project(':ethereum:spec'))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Consensys Software Inc., 2024
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.validator.api;

public class GraffitiManagementException extends RuntimeException {

public GraffitiManagementException(final String message) {
super(message);
}

public GraffitiManagementException(final String message, final Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright Consensys Software Inc., 2024
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.validator.api;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.service.serviceutils.layout.DataDirLayout;

public class GraffitiManager {
private static final Logger LOG = LogManager.getLogger();
static final String GRAFFITI_DIR = "graffiti";
private final Path directory;

public GraffitiManager(final DataDirLayout dataDirLayout) {
this(dataDirLayout.getValidatorDataDirectory().resolve(GRAFFITI_DIR));
}

public GraffitiManager(final Path directory) {
this.directory = directory;
if (!directory.toFile().exists() && !directory.toFile().mkdirs()) {
throw new IllegalStateException("Unable to create directory for graffiti management.");
}
}

public synchronized void setGraffiti(final BLSPublicKey publicKey, final String graffiti)
throws GraffitiManagementException {
final String strippedGraffiti = graffiti.strip();
final int graffitiSize = strippedGraffiti.getBytes(StandardCharsets.UTF_8).length;
if (graffitiSize > 32) {
throw new IllegalArgumentException(
String.format(
"'%s' converts to %s bytes. Input must be 32 bytes or less.",
strippedGraffiti, graffitiSize));
}

try {
final Path file = directory.resolve(resolveFileName(publicKey));
Files.writeString(file, strippedGraffiti);
} catch (IOException e) {
throw new GraffitiManagementException(
"Unable to update graffiti for validator " + publicKey, e);
}
}

public synchronized void deleteGraffiti(final BLSPublicKey publicKey)
throws GraffitiManagementException {
final Path file = directory.resolve(resolveFileName(publicKey));
if (!file.toFile().exists()) {
return;
}

try {
Files.delete(file);
} catch (IOException e) {
throw new GraffitiManagementException(
"Unable to delete graffiti for validator " + publicKey, e);
}
}

public synchronized Optional<Bytes32> getGraffiti(final BLSPublicKey publicKey)
throws GraffitiManagementException {
final Path filePath = directory.resolve(resolveFileName(publicKey));
if (!filePath.toFile().exists()) {
return Optional.empty();
}

try {
return Optional.of(GraffitiParser.loadFromFile(filePath));
} catch (GraffitiLoaderException | IllegalArgumentException e) {
LOG.error("Loading graffiti from graffiti storage failed.", e);
throw new GraffitiManagementException(
"Unable to retrieve stored graffiti for validator " + publicKey, e);
}
}

private String resolveFileName(final BLSPublicKey publicKey) {
return publicKey.toSSZBytes().toUnprefixedHexString() + ".txt";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright Consensys Software Inc., 2024
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.validator.api;

import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes32;

public class UpdatableGraffitiProvider implements GraffitiProvider {
private final Supplier<Optional<Bytes32>> storageProvider;
private final GraffitiProvider defaultProvider;

public UpdatableGraffitiProvider(
final Supplier<Optional<Bytes32>> storageProvider, final GraffitiProvider defaultProvider) {
this.storageProvider = storageProvider;
this.defaultProvider = defaultProvider;
}

@Override
public Optional<Bytes32> get() {
return getFromStorage().or(defaultProvider::get);
}

private Optional<Bytes32> getFromStorage() {
try {
return storageProvider.get();
} catch (Exception e) {
return Optional.empty();
}
}

/**
* @return graffiti without checking for thrown Exceptions.
*/
public Optional<Bytes32> getUnsafe() {
return storageProvider.get().or(defaultProvider::get);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Consensys Software Inc., 2024
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.validator.api.noop;

import java.nio.file.Path;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.validator.api.GraffitiManager;

public class NoOpGraffitiManager extends GraffitiManager {
public NoOpGraffitiManager() {
super(Path.of("."));
}

@Override
public void setGraffiti(final BLSPublicKey publicKey, final String graffiti) {}

@Override
public void deleteGraffiti(final BLSPublicKey publicKey) {}

@Override
public Optional<Bytes32> getGraffiti(final BLSPublicKey publicKey) {
return Optional.empty();
}
}
Loading

0 comments on commit a3683f7

Please sign in to comment.