Skip to content

Commit

Permalink
Add support for slashing interchange format tests
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov committed Apr 10, 2024
1 parent 2c2249a commit f482f88
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 6 deletions.
34 changes: 31 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -297,10 +297,13 @@ allprojects {

def refTestVersion = 'v1.4.0' // Arbitrary change to refresh cache number: 1
def blsRefTestVersion = 'v0.1.2'
def slashingProtectionRefTestVersion = 'v5.3.0'
def refTestBaseUrl = 'https://github.com/ethereum/consensus-spec-tests/releases/download'
def blsRefTestBaseUrl = 'https://github.com/ethereum/bls12-381-tests/releases/download'
def slashingProtectionRefTestBaseUrl = 'https://github.com/eth-clients/slashing-protection-interchange-tests/archive/refs/tags'
def refTestDownloadDir = "${buildDir}/refTests/${refTestVersion}"
def blsRefTestDownloadDir = "${buildDir}/blsRefTests/${blsRefTestVersion}"
def slashingProtectionRefTestDownloadDir = "${buildDir}/slashingProtectionRefTests/${slashingProtectionRefTestVersion}"
def refTestExpandDir = "${project.rootDir}/eth-reference-tests/src/referenceTest/resources/consensus-spec-tests/"

task downloadEthRefTests(type: Download) {
Expand All @@ -321,7 +324,15 @@ task downloadBlsRefTests(type: Download) {
overwrite false
}

task downloadRefTests(dependsOn: [downloadEthRefTests, downloadBlsRefTests])
task downloadSlashingProtectionRefTests(type: Download) {
src([
"${slashingProtectionRefTestBaseUrl}/${slashingProtectionRefTestVersion}.tar.gz"
])
dest "${slashingProtectionRefTestDownloadDir}/slashing-protection-interchange-tests.tar.gz"
overwrite false
}

task downloadRefTests(dependsOn: [downloadEthRefTests, downloadBlsRefTests, downloadSlashingProtectionRefTests])

task cleanRefTestsGeneral(type: Delete) {
delete "${refTestExpandDir}/tests/general"
Expand Down Expand Up @@ -359,8 +370,25 @@ task expandRefTestsBls(type: Copy, dependsOn: [cleanRefTestsBls, downloadBlsRefT
into "${refTestExpandDir}/tests/bls"
}

task expandRefTests(dependsOn: [expandRefTestsGeneral, expandRefTestsMainnet, expandRefTestsMinimal, expandRefTestsBls])
task cleanRefTests(dependsOn: [cleanRefTestsGeneral, cleanRefTestsMainnet, cleanRefTestsMinimal, cleanRefTestsBls])
task cleanRefTestsSlashingProtection(type: Delete) {
delete "${refTestExpandDir}/tests/slashing-protection-interchange"
}

task expandRefTestsSlashingProtection(type: Copy, dependsOn: [cleanRefTestsSlashingProtection, downloadSlashingProtectionRefTests]) {
into "${refTestExpandDir}/tests/slashing-protection-interchange"
from {
tarTree("${slashingProtectionRefTestDownloadDir}/slashing-protection-interchange-tests.tar.gz").matching {
include "**/tests/generated/*.json"
// flatten the directory structure
eachFile { FileCopyDetails fcp ->
fcp.path = fcp.name
}
}
}
}

task expandRefTests(dependsOn: [expandRefTestsGeneral, expandRefTestsMainnet, expandRefTestsMinimal, expandRefTestsBls, expandRefTestsSlashingProtection])
task cleanRefTests(dependsOn: [cleanRefTestsGeneral, cleanRefTestsMainnet, cleanRefTestsMinimal, cleanRefTestsBls, cleanRefTestsSlashingProtection])

task deploy() {}

Expand Down
3 changes: 3 additions & 0 deletions eth-reference-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ dependencies {
referenceTestImplementation project(':storage')
referenceTestImplementation testFixtures(project(':storage'))
referenceTestImplementation project(':infrastructure:async')
referenceTestImplementation project(':infrastructure:io')
referenceTestImplementation testFixtures(project(':infrastructure:async'))
referenceTestImplementation testFixtures(project(':infrastructure:metrics'))
referenceTestImplementation project(':infrastructure:time')
referenceTestImplementation project(':data:dataexchange')
referenceTestImplementation project(':data:serializer')

referenceTestImplementation 'org.hyperledger.besu:plugin-api'
referenceTestImplementation 'com.fasterxml.jackson.core:jackson-databind'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import tech.pegasys.teku.reference.phase0.rewards.RewardsTestExecutorPhase0;
import tech.pegasys.teku.reference.phase0.sanity.SanityTests;
import tech.pegasys.teku.reference.phase0.shuffling.ShufflingTestExecutor;
import tech.pegasys.teku.reference.phase0.slashing_protection_interchange.SlashingProtectionInterchangeTestExecutor;
import tech.pegasys.teku.reference.phase0.ssz_generic.SszGenericTests;
import tech.pegasys.teku.reference.phase0.ssz_static.SszTestExecutor;

Expand All @@ -48,6 +49,7 @@ public abstract class Eth2ReferenceTestCase {
.putAll(SszGenericTests.SSZ_GENERIC_TEST_TYPES)
.putAll(OperationsTestExecutor.OPERATIONS_TEST_TYPES)
.putAll(SanityTests.SANITY_TEST_TYPES)
.put("slashing-protection-interchange", new SlashingProtectionInterchangeTestExecutor())
.put("light_client/single_merkle_proof", TestExecutor.IGNORE_TESTS)
.put("light_client/sync", TestExecutor.IGNORE_TESTS)
.put("light_client/update_ranking", TestExecutor.IGNORE_TESTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package tech.pegasys.teku.reference;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
Expand All @@ -29,18 +31,21 @@
import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition;
import tech.pegasys.teku.infrastructure.ssz.SszData;
import tech.pegasys.teku.infrastructure.ssz.schema.SszSchema;
import tech.pegasys.teku.provider.JsonProvider;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;

public class TestDataUtils {

private static final YAMLFactory YAML_FACTORY;
private static final JsonProvider JSON_PROVIDER;

static {
final LoaderOptions loaderOptions = new LoaderOptions();
// Set the code point limit to 100MB - context:
// https://github.com/FasterXML/jackson-dataformats-text/tree/2.15/yaml#maximum-input-yaml-document-size-3-mb
loaderOptions.setCodePointLimit(1024 * 1024 * 100);
YAML_FACTORY = YAMLFactory.builder().loaderOptions(loaderOptions).build();
JSON_PROVIDER = new JsonProvider();
}

public static <T extends SszData> T loadSsz(
Expand Down Expand Up @@ -100,4 +105,15 @@ public static <T> T loadYaml(
return type.deserialize(in);
}
}

public static JsonNode loadJson(final TestDefinition testDefinition, final String fileName)
throws IOException {
final Path path = testDefinition.getTestDirectory().resolve(fileName);
return JSON_PROVIDER.getObjectMapper().readTree(Files.newInputStream(path));
}

public static <T> T jsonToObject(final String json, final Class<T> type)
throws JsonProcessingException {
return JSON_PROVIDER.jsonToObject(json, type);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* 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.reference.phase0.slashing_protection_interchange;

import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.databind.JsonNode;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
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.data.SlashingProtectionImporter;
import tech.pegasys.teku.ethtests.finder.TestDefinition;
import tech.pegasys.teku.infrastructure.io.SyncDataAccessor;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.reference.TestDataUtils;
import tech.pegasys.teku.reference.TestExecutor;
import tech.pegasys.teku.spec.signatures.LocalSlashingProtector;

public class SlashingProtectionInterchangeTestExecutor implements TestExecutor {

private static final Logger LOG = LogManager.getLogger();

//TODO: implement the logic
@Override
public void runTest(final TestDefinition testDefinition) throws Throwable {
final JsonNode testNode = TestDataUtils.loadJson(testDefinition, testDefinition.getTestName());

final String testName = testNode.get("name").asText();
final Bytes32 genesisValidatorsRoot =
Bytes32.fromHexString(testNode.get("genesis_validators_root").asText());

LOG.info("Running {}", testName);

final Path tempDir = Files.createTempDirectory(testName);

final Path slashProtectionPath = tempDir.resolve("slashprotection");

final Path slashProtectionImportFile = tempDir.resolve("import.yml");

Files.writeString(
slashProtectionImportFile,
testNode.get("steps").get(0).get("interchange").toString(),
StandardCharsets.UTF_8);

final BLSPublicKey pubkey =
BLSPublicKey.fromHexString(
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c");

Files.createDirectories(slashProtectionPath);
SlashingProtectionImporter importer = new SlashingProtectionImporter(slashProtectionPath);
importer.initialise(slashProtectionImportFile.toFile());
final Map<BLSPublicKey, String> errors = importer.updateLocalRecords((__) -> {});

assertThat(errors).isEmpty();

LocalSlashingProtector localSlashingProtector =
new LocalSlashingProtector(
SyncDataAccessor.create(slashProtectionPath), slashProtectionPath);
assertThat(
localSlashingProtector.maySignBlock(pubkey, genesisValidatorsRoot, UInt64.valueOf(10)))
.isCompletedWithValue(false);
assertThat(
localSlashingProtector.maySignBlock(pubkey, genesisValidatorsRoot, UInt64.valueOf(13)))
.isCompletedWithValue(false);
assertThat(
localSlashingProtector.maySignBlock(pubkey, genesisValidatorsRoot, UInt64.valueOf(14)))
.isCompletedWithValue(true);
assertThat(
localSlashingProtector.maySignAttestation(
pubkey, genesisValidatorsRoot, UInt64.valueOf(0), UInt64.valueOf(2)))
.isCompletedWithValue(false);
assertThat(
localSlashingProtector.maySignAttestation(
pubkey, genesisValidatorsRoot, UInt64.valueOf(1), UInt64.valueOf(3)))
.isCompletedWithValue(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public Stream<TestDefinition> findTests(final String fork, final String spec, fi
@MustBeClosed
private Stream<TestDefinition> findBlsTests(
final String spec, final Path testRoot, final Path testCategoryDir) throws IOException {
final String testType = "bls/" + testRoot.relativize(testCategoryDir).toString();
final String testType = "bls/" + testRoot.relativize(testCategoryDir);
return Files.list(testCategoryDir)
.filter(file -> file.toFile().getName().endsWith(".yaml"))
.map(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public static Stream<TestDefinition> findReferenceTests() throws IOException {
private static Stream<TestDefinition> findTestTypes(final Path specDirectory) throws IOException {
final String spec = specDirectory.getFileName().toString();
if (spec.equals("bls")) {
return new BlsRefTestFinder().findTests(TestFork.PHASE0, spec, specDirectory);
return new BlsRefTestFinder().findTests("", spec, specDirectory);
}
if (spec.equals("slashing-protection-interchange")) {
return new SlashingProtectionInterchangeTestFinder().findTests("", spec, specDirectory);
}
return SUPPORTED_FORKS.stream()
.flatMap(
Expand All @@ -60,7 +63,6 @@ private static Stream<TestDefinition> findTestTypes(final Path specDirectory) th
return Stream.of(
new BlsTestFinder(),
new KzgTestFinder(),
new BlsRefTestFinder(),
new SszTestFinder("ssz_generic"),
new SszTestFinder("ssz_static"),
new ShufflingTestFinder(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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.ethtests.finder;

import com.google.errorprone.annotations.MustBeClosed;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class SlashingProtectionInterchangeTestFinder implements TestFinder {

@Override
@MustBeClosed
public Stream<TestDefinition> findTests(final String fork, final String spec, final Path testRoot)
throws IOException {
if (!spec.equals("slashing-protection-interchange")) {
return Stream.empty();
}
return Files.list(testRoot)
.filter(file -> file.toFile().getName().endsWith(".json"))
.map(
testFile ->
new TestDefinition(
fork,
spec,
spec,
testFile.toFile().getName(),
testRoot.relativize(testFile.getParent())));
}
}

0 comments on commit f482f88

Please sign in to comment.