Skip to content

Commit

Permalink
Slashing protection pruning (#371)
Browse files Browse the repository at this point in the history
  • Loading branch information
jframe authored Mar 12, 2021
1 parent 37517e8 commit 172b1f3
Show file tree
Hide file tree
Showing 29 changed files with 1,167 additions and 72 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features Added
- Publish binaries to Cloudsmith
- Resolve Signers from Cloudsmith
- Slashing protection database pruning for Eth2

### Bugs Fixed
- Fixed build failure when checked out as a shallow clone. Shallow clones are still not recommended as the version number cannot be determined correctly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ public class SignerConfiguration {
private final boolean enableSlashing;
private final Optional<Path> slashingExportPath;
private final Optional<Path> slashingImportPath;
private final boolean enableSlashingPruning;
private final boolean swaggerUIEnabled;
private final boolean useConfigFile;
private final long slashingPruningEpochsToKeep;
private final long slashingPruningSlotsPerEpoch;
private final long slashingPruningInterval;

public SignerConfiguration(
final String hostname,
Expand All @@ -71,6 +75,10 @@ public SignerConfiguration(
final boolean enableSlashing,
final Optional<Path> slashingExportPath,
final Optional<Path> slashingImportPath,
final boolean enableSlashingPruning,
final long slashingPruningEpochsToKeep,
final long slashingPruningSlotsPerEpoch,
final long slashingPruningSchedule,
final boolean swaggerUIEnabled,
final boolean useConfigFile) {
this.hostname = hostname;
Expand All @@ -93,6 +101,10 @@ public SignerConfiguration(
this.enableSlashing = enableSlashing;
this.slashingExportPath = slashingExportPath;
this.slashingImportPath = slashingImportPath;
this.enableSlashingPruning = enableSlashingPruning;
this.slashingPruningEpochsToKeep = slashingPruningEpochsToKeep;
this.slashingPruningSlotsPerEpoch = slashingPruningSlotsPerEpoch;
this.slashingPruningInterval = slashingPruningSchedule;
this.swaggerUIEnabled = swaggerUIEnabled;
this.useConfigFile = useConfigFile;
}
Expand Down Expand Up @@ -185,6 +197,22 @@ public Optional<Path> getSlashingImportPath() {
return slashingImportPath;
}

public boolean isSlashingProtectionPruningEnabled() {
return enableSlashingPruning;
}

public long getSlashingProtectionPruningEpochsToKeep() {
return slashingPruningEpochsToKeep;
}

public long getSlashingProtectionPruningSlotsPerEpoch() {
return slashingPruningSlotsPerEpoch;
}

public long getSlashingProtectionPruningInterval() {
return slashingPruningInterval;
}

public boolean isSwaggerUIEnabled() {
return swaggerUIEnabled;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ public class SignerConfigurationBuilder {
private String slashingProtectionDbUrl;
private Path slashingExportPath;
private Path slashingImportPath;
private boolean enableSlashingPruning = false;
private boolean swaggerUIEnabled = false;
private boolean useConfigFile = false;
private long pruningEpochsToKeep = 1;
private long slashingPruningSlotsPerEpoch = 1;
private long slashingPruningInterval = 1;

public SignerConfigurationBuilder withLogLevel(final Level logLevel) {
this.logLevel = logLevel;
Expand Down Expand Up @@ -146,6 +150,29 @@ public SignerConfigurationBuilder withSlashingImportPath(final Path slashingImpo
return this;
}

public SignerConfigurationBuilder withSlashingPruningEnabled(final boolean enablePruning) {
this.enableSlashingPruning = enablePruning;
return this;
}

public SignerConfigurationBuilder withSlashingPruningEpochsToKeep(
final long pruningEpochsToKeep) {
this.pruningEpochsToKeep = pruningEpochsToKeep;
return this;
}

public SignerConfigurationBuilder withSlashingPruningSlotsPerEpoch(
final long slashingPruningSlotsPerEpoch) {
this.slashingPruningSlotsPerEpoch = slashingPruningSlotsPerEpoch;
return this;
}

public SignerConfigurationBuilder withSlashingPruningInterval(
final long slashingPruningInterval) {
this.slashingPruningInterval = slashingPruningInterval;
return this;
}

public SignerConfigurationBuilder withEnvironment(final Map<String, String> environment) {
this.web3SignerEnvironment = environment;
return this;
Expand Down Expand Up @@ -186,6 +213,10 @@ public SignerConfiguration build() {
enableSlashing,
Optional.ofNullable(slashingExportPath),
Optional.ofNullable(slashingImportPath),
enableSlashingPruning,
pruningEpochsToKeep,
slashingPruningSlotsPerEpoch,
slashingPruningInterval,
swaggerUIEnabled,
useConfigFile);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,29 @@ private String createEth2SlashingProtectionArgs() {
signerConfig.getSlashingProtectionDbPassword()));
}

if (signerConfig.isSlashingProtectionPruningEnabled()) {
yamlConfig.append(
String.format(
YAML_BOOLEAN_FMT,
"eth2.slashing-protection-pruning-enabled",
signerConfig.isSlashingProtectionPruningEnabled()));
yamlConfig.append(
String.format(
YAML_NUMERIC_FMT,
"eth2.slashing-protection-pruning-epochs-to-keep",
signerConfig.getSlashingProtectionPruningEpochsToKeep()));
yamlConfig.append(
String.format(
YAML_NUMERIC_FMT,
"eth2.sslashing-protection-pruning-slots-per-epoch",
signerConfig.getSlashingProtectionPruningSlotsPerEpoch()));
yamlConfig.append(
String.format(
YAML_NUMERIC_FMT,
"eth2.slashing-protection-pruning-schedule",
signerConfig.getSlashingProtectionPruningInterval()));
}

return yamlConfig.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@ private Collection<String> createEth2Args() {
params.add(signerConfig.getSlashingImportPath().get().toAbsolutePath().toString());
}

if (signerConfig.isSlashingProtectionPruningEnabled()) {
params.add("--slashing-protection-pruning-enabled");
params.add(Boolean.toString(signerConfig.isSlashingProtectionPruningEnabled()));
params.add("--slashing-protection-pruning-epochs-to-keep");
params.add(Long.toString(signerConfig.getSlashingProtectionPruningEpochsToKeep()));
params.add("--slashing-protection-pruning-slots-per-epoch");
params.add(Long.toString(signerConfig.getSlashingProtectionPruningSlotsPerEpoch()));
params.add("--slashing-protection-pruning-interval");
params.add(Long.toString(signerConfig.getSlashingProtectionPruningInterval()));
}

return params;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2020 ConsenSys AG.
*
* 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.web3signer.tests.slashing;

import static org.assertj.core.api.Assertions.assertThat;
import static tech.pegasys.web3signer.dsl.utils.Eth2RequestUtils.createAttestationRequest;

import tech.pegasys.teku.bls.BLSKeyPair;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.web3signer.core.signing.KeyType;
import tech.pegasys.web3signer.dsl.signer.Signer;
import tech.pegasys.web3signer.dsl.signer.SignerConfigurationBuilder;
import tech.pegasys.web3signer.dsl.utils.EmbeddedDatabaseUtils;
import tech.pegasys.web3signer.dsl.utils.Eth2RequestUtils;
import tech.pegasys.web3signer.dsl.utils.MetadataFileHelpers;
import tech.pegasys.web3signer.tests.AcceptanceTestBase;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;

import org.apache.tuweni.bytes.Bytes32;
import org.jdbi.v3.core.Jdbi;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

public class SlashingPruningAcceptanceTest extends AcceptanceTestBase {

private static final MetadataFileHelpers metadataFileHelpers = new MetadataFileHelpers();
public static final String DB_USERNAME = "postgres";
public static final String DB_PASSWORD = "postgres";

protected final BLSKeyPair keyPair = BLSKeyPair.random(0);

@Test
void slashingDataIsPruned(@TempDir Path testDirectory) throws IOException {
final String dbUrl = EmbeddedDatabaseUtils.createEmbeddedDatabase();
final Jdbi jdbi = Jdbi.create(dbUrl, DB_USERNAME, DB_PASSWORD);

final Path keyConfigFile = testDirectory.resolve("keyfile.yaml");
metadataFileHelpers.createUnencryptedYamlFileAt(
keyConfigFile, keyPair.getSecretKey().toBytes().toHexString(), KeyType.BLS);

final SignerConfigurationBuilder signerBuilder =
new SignerConfigurationBuilder()
.withMode("eth2")
.withSlashingEnabled(true)
.withSlashingProtectionDbUsername(DB_USERNAME)
.withSlashingProtectionDbPassword(DB_PASSWORD)
.withSlashingProtectionDbUrl(dbUrl)
.withKeyStoreDirectory(testDirectory);

final Signer dataCreatingSigner = new Signer(signerBuilder.build(), null);
dataCreatingSigner.start();
dataCreatingSigner.awaitStartupCompletion();

// populate slashing database with 2 block signings and 2 attestation signings
dataCreatingSigner.eth2Sign(
keyPair.getPublicKey().toString(), createAttestationRequest(1, 2, UInt64.ZERO));
dataCreatingSigner.eth2Sign(
keyPair.getPublicKey().toString(), createAttestationRequest(2, 3, UInt64.ZERO));
dataCreatingSigner.eth2Sign(
keyPair.getPublicKey().toString(),
Eth2RequestUtils.createBlockRequest(UInt64.valueOf(1), Bytes32.fromHexString("0x1111")));
dataCreatingSigner.eth2Sign(
keyPair.getPublicKey().toString(),
Eth2RequestUtils.createBlockRequest(UInt64.valueOf(2), Bytes32.fromHexString("0x1111")));
dataCreatingSigner.shutdown();

final List<Map<String, Object>> attestationsBeforePruning = getAttestations(jdbi);
final List<Map<String, Object>> blocksBeforePruning = getSignedBlocks(jdbi);
assertThat(attestationsBeforePruning).hasSize(2);
assertThat(blocksBeforePruning).hasSize(2);

// start signer with pruning enabled configured to only keep one block and attestation
signerBuilder
.withSlashingPruningEnabled(true)
.withSlashingPruningEpochsToKeep(1)
.withSlashingPruningSlotsPerEpoch(1)
.withSlashingPruningInterval(1);
final Signer pruningSigner = new Signer(signerBuilder.build(), null);
pruningSigner.start();
pruningSigner.awaitStartupCompletion();

final List<Map<String, Object>> attestations = getAttestations(jdbi);
final Map<String, Object> expectedHeadAttestation = attestationsBeforePruning.get(1);
assertThat(attestations).hasSize(1);
assertThat(attestations.get(0).get("validator_id"))
.isEqualTo(expectedHeadAttestation.get("validator_id"));
assertThat(attestations.get(0).get("source_epoch"))
.isEqualTo(expectedHeadAttestation.get("source_epoch"));
assertThat(attestations.get(0).get("target_epoch"))
.isEqualTo(expectedHeadAttestation.get("target_epoch"));
assertThat(attestations.get(0).get("signing_root"))
.isEqualTo(expectedHeadAttestation.get("signing_root"));

final List<Map<String, Object>> blocks = getSignedBlocks(jdbi);
final Map<String, Object> expectedHeadBlock = blocksBeforePruning.get(1);
assertThat(blocks).hasSize(1);
assertThat(blocks.get(0).get("validator_id")).isEqualTo(expectedHeadBlock.get("validator_id"));
assertThat(blocks.get(0).get("slot")).isEqualTo(expectedHeadBlock.get("slot"));
assertThat(blocks.get(0).get("signing_root")).isEqualTo(expectedHeadBlock.get("signing_root"));
}

private List<Map<String, Object>> getSignedBlocks(final Jdbi jdbi) {
return jdbi.withHandle(h -> h.select("SELECT * from signed_blocks").mapToMap().list());
}

private List<Map<String, Object>> getAttestations(final Jdbi jdbi) {
return jdbi.withHandle(h -> h.select("SELECT * from signed_attestations").mapToMap().list());
}
}
Loading

0 comments on commit 172b1f3

Please sign in to comment.