Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate fork choice compliance test suite #8419

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.safeJoin;
import static tech.pegasys.teku.infrastructure.time.TimeUtilities.secondsToMillis;
import static tech.pegasys.teku.reference.BlsSetting.IGNORED;
import static tech.pegasys.teku.reference.TestDataUtils.loadYaml;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Expand All @@ -42,6 +48,7 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.kzg.KZG;
import tech.pegasys.teku.kzg.KZGProof;
import tech.pegasys.teku.reference.BlsSetting;
import tech.pegasys.teku.reference.KzgRetriever;
import tech.pegasys.teku.reference.TestDataUtils;
import tech.pegasys.teku.reference.TestExecutor;
Expand All @@ -53,12 +60,15 @@
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot;
import tech.pegasys.teku.spec.datastructures.execution.PowBlock;
import tech.pegasys.teku.spec.datastructures.forkchoice.ProtoNodeData;
import tech.pegasys.teku.spec.datastructures.forkchoice.ReadOnlyForkChoiceStrategy;
import tech.pegasys.teku.spec.datastructures.forkchoice.VoteUpdater;
import tech.pegasys.teku.spec.datastructures.operations.Attestation;
import tech.pegasys.teku.spec.datastructures.operations.AttesterSlashing;
import tech.pegasys.teku.spec.datastructures.state.AnchorPoint;
import tech.pegasys.teku.spec.datastructures.state.Checkpoint;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.datastructures.util.AttestationProcessingResult;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannelStub;
import tech.pegasys.teku.spec.executionlayer.ExecutionPayloadStatus;
Expand Down Expand Up @@ -92,6 +102,13 @@ public class ForkChoiceTestExecutor implements TestExecutor {
.put("sync/optimistic", new ForkChoiceTestExecutor())
.put("fork_choice/should_override_forkchoice_update", new ForkChoiceTestExecutor())
.put("fork_choice/get_proposer_head", new ForkChoiceTestExecutor("basic_is_parent_root"))
// Fork choice generated test types
.put("fork_choice_generated/block_weight_test", new ForkChoiceTestExecutor())
.put("fork_choice_generated/block_tree_test", new ForkChoiceTestExecutor())
.put("fork_choice_generated/attester_slashing_test", new ForkChoiceTestExecutor())
.put("fork_choice_generated/invalid_message_test", new ForkChoiceTestExecutor())
.put("fork_choice_generated/block_cover_test", new ForkChoiceTestExecutor())
.put("fork_choice_generated/shuffling_test", new ForkChoiceTestExecutor())
.build();

private final List<?> testsToSkip;
Expand All @@ -107,9 +124,10 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {
"Test " + testDefinition.getDisplayName() + " has been ignored");
}

// Note: The fork choice spec says there may be settings in a meta.yaml file but currently no
// tests actually have one, so we currently don't bother trying to load it.
final Spec spec = testDefinition.getSpec();
// Load `meta.yaml` and read the BLS setting
final ForkChoiceMetaData metaData = getMetaData(testDefinition);
final boolean blsDisabled = metaData.getBlsSetting() == IGNORED;
final Spec spec = testDefinition.getSpec(blsDisabled);
final BeaconState anchorState =
TestDataUtils.loadStateFromSsz(testDefinition, "anchor_state" + SSZ_SNAPPY_EXTENSION);
final SignedBeaconBlock anchorBlock = loadAnchorBlock(testDefinition);
Expand Down Expand Up @@ -197,7 +215,7 @@ private void runSteps(
applyChecks(recentChainData, forkChoice, step);

} else if (step.containsKey("tick")) {
forkChoice.onTick(secondsToMillis(getUInt64(step, "tick")), Optional.empty());
forkChoice.onTick(secondsToMillis(getUInt64(step, "tick")), Optional.empty(), true);
final UInt64 currentSlot = recentChainData.getCurrentSlot().orElse(UInt64.ZERO);
LOG.info("Current slot: {} Epoch: {}", currentSlot, spec.computeEpochAtSlot(currentSlot));
} else if (step.containsKey("block")) {
Expand Down Expand Up @@ -279,14 +297,20 @@ private void applyAttestation(
final ForkChoice forkChoice,
final Map<String, Object> step) {
final String attestationName = get(step, "attestation");
final boolean valid = !step.containsKey("valid") || (boolean) step.get("valid");
final Attestation attestation =
TestDataUtils.loadSsz(
testDefinition,
attestationName + SSZ_SNAPPY_EXTENSION,
testDefinition.getSpec().getGenesisSchemaDefinitions().getAttestationSchema());
final Spec spec = testDefinition.getSpec();
assertThat(forkChoice.onAttestation(ValidatableAttestation.from(spec, attestation)))
.isCompleted();
final SafeFuture<AttestationProcessingResult> result =
forkChoice.onAttestation(ValidatableAttestation.from(spec, attestation));
assertThat(result).isCompleted();
AttestationProcessingResult processingResult = safeJoin(result);
assertThat(processingResult.isSuccessful())
.withFailMessage(processingResult.getInvalidReason())
.isEqualTo(valid);
}

private void applyAttesterSlashing(
Expand Down Expand Up @@ -483,6 +507,32 @@ private void applyChecks(
assertThat(expectedValidatorIsConnected).isTrue();
}

case "viable_for_head_roots_and_weights" -> {
final List<Map<String, Object>> viableHeadRootsAndWeightsData = get(checks, checkType);
final Map<Bytes32, UInt64> viableHeadRootsAndWeights =
viableHeadRootsAndWeightsData.stream()
.collect(
Collectors.toMap(
entry -> Bytes32.fromHexString((String) entry.get("root")),
entry -> UInt64.valueOf(entry.get("weight").toString())));
List<ProtoNodeData> chainHeads =
recentChainData
.getForkChoiceStrategy()
.map(ReadOnlyForkChoiceStrategy::getViableChainHeads)
.orElse(Collections.emptyList());
Set<Bytes32> actualViableHeadRoots =
chainHeads.stream().map(ProtoNodeData::getRoot).collect(Collectors.toSet());
assertThat(actualViableHeadRoots)
.describedAs("viable head roots")
.isEqualTo(viableHeadRootsAndWeights.keySet());

for (ProtoNodeData chainHead : chainHeads) {
assertThat(chainHead.getWeight())
.describedAs("viable head weight")
.isEqualTo(viableHeadRootsAndWeights.get(chainHead.getRoot()));
}
}

default -> throw new UnsupportedOperationException(
"Unsupported check type: " + checkType);
}
Expand Down Expand Up @@ -534,4 +584,34 @@ private static Optional<Bytes32> getOptionallyBytes32(
final Map<String, Object> yamlData, final String key) {
return ForkChoiceTestExecutor.<String>getOptionally(yamlData, key).map(Bytes32::fromHexString);
}

private static ForkChoiceMetaData getMetaData(final TestDefinition testDefinition)
throws IOException {
final ForkChoiceMetaData metaData;
final Path metaPath = testDefinition.getTestDirectory().resolve("meta.yaml");
if (metaPath.toFile().exists()) {
metaData = loadYaml(testDefinition, "meta.yaml", ForkChoiceMetaData.class);
} else {
metaData = ForkChoiceMetaData.DEFAULT;
}

return metaData;
}

@JsonIgnoreProperties(ignoreUnknown = true)
private static class ForkChoiceMetaData {
static final ForkChoiceMetaData DEFAULT = new ForkChoiceMetaData(0);

private ForkChoiceMetaData(
@JsonProperty(value = "bls_setting", required = false, defaultValue = "0")
final int blsSetting) {
this.blsSetting = blsSetting;
}

private final int blsSetting;

public BlsSetting getBlsSetting() {
return BlsSetting.forCode(blsSetting);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ public String getFork() {
}

public Spec getSpec() {
return getSpec(Boolean.FALSE);
}

public Spec getSpec(final Boolean blsDisabled) {
if (spec == null) {
createSpec();
createSpec(blsDisabled);
}
return spec;
}

private void createSpec() {
private void createSpec(final Boolean blsDisabled) {
final Eth2Network network =
switch (configName) {
case TestSpecConfig.MAINNET -> Eth2Network.MAINNET;
Expand All @@ -75,7 +79,7 @@ private void createSpec() {
case TestFork.ELECTRA -> SpecMilestone.ELECTRA;
default -> throw new IllegalArgumentException("Unknown fork: " + fork);
};
spec = TestSpecFactory.create(milestone, network);
spec = TestSpecFactory.create(milestone, network, builder -> builder.blsDisabled(blsDisabled));
}

public String getTestType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ public int getReorgParentWeightThreshold() {
return specConfig.getReorgParentWeightThreshold();
}

@Override
public boolean isBlsDisabled() {
return specConfig.isBlsDisabled();
}

@Override
public long getDepositChainId() {
return specConfig.getDepositChainId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ default int getMillisPerSlot() {

int getReorgParentWeightThreshold();

// Handle spec tests with BLS disabled
boolean isBlsDisabled();

// Casters
default Optional<SpecConfigAltair> toVersionAltair() {
return Optional.empty();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ public class SpecConfigPhase0 implements SpecConfig {

private final UInt64 maxPerEpochActivationExitChurnLimit;

private final boolean blsDisabled;

public SpecConfigPhase0(
final Map<String, Object> rawConfig,
final UInt64 eth1FollowDistance,
Expand Down Expand Up @@ -191,7 +193,8 @@ public SpecConfigPhase0(
final int reorgMaxEpochsSinceFinalization,
final int reorgHeadWeightThreshold,
final int reorgParentWeightThreshold,
final UInt64 maxPerEpochActivationExitChurnLimit) {
final UInt64 maxPerEpochActivationExitChurnLimit,
final boolean blsDisabled) {
this.rawConfig = rawConfig;
this.eth1FollowDistance = eth1FollowDistance;
this.maxCommitteesPerSlot = maxCommitteesPerSlot;
Expand Down Expand Up @@ -262,6 +265,7 @@ public SpecConfigPhase0(
this.reorgHeadWeightThreshold = reorgHeadWeightThreshold;
this.reorgParentWeightThreshold = reorgParentWeightThreshold;
this.maxPerEpochActivationExitChurnLimit = maxPerEpochActivationExitChurnLimit;
this.blsDisabled = blsDisabled;
}

@Override
Expand Down Expand Up @@ -539,6 +543,11 @@ public int getReorgParentWeightThreshold() {
return reorgParentWeightThreshold;
}

@Override
public boolean isBlsDisabled() {
return blsDisabled;
}

@Override
public int getProposerScoreBoost() {
return proposerScoreBoost;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ public class SpecConfigBuilder {
.appendBuilder(new DenebBuilder())
.appendBuilder(new ElectraBuilder());

// Allows to handle spec tests with BLS disabled
private Boolean blsDisabled = Boolean.FALSE;

public SpecConfig build() {
builderChain.addOverridableItemsToRawConfig(
(key, value) -> {
Expand Down Expand Up @@ -216,7 +219,8 @@ public SpecConfig build() {
reorgMaxEpochsSinceFinalization,
reorgHeadWeightThreshold,
reorgParentWeightThreshold,
maxPerEpochActivationExitChurnLimit);
maxPerEpochActivationExitChurnLimit,
blsDisabled);

return builderChain.build(config);
}
Expand Down Expand Up @@ -740,4 +744,9 @@ public SpecConfigBuilder electraBuilder(final Consumer<ElectraBuilder> consumer)
builderChain.withBuilder(ElectraBuilder.class, consumer);
return this;
}

public SpecConfigBuilder blsDisabled(final Boolean blsDisabled) {
this.blsDisabled = blsDisabled;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ default List<ProtoNodeData> getChainHeads() {

List<ProtoNodeData> getChainHeads(boolean includeNonViableHeads);

List<ProtoNodeData> getViableChainHeads();

Optional<Bytes32> getOptimisticallySyncedTransitionBlockRoot(Bytes32 head);

List<ProtoNodeData> getBlockData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ public BeaconState processAndValidateBlock(
signedBlock,
blockSlotState,
indexedAttestationCache,
signatureVerifier,
// Handle spec test run with BLS disabled
specConfig.isBlsDisabled() ? BLSSignatureVerifier.NO_OP : signatureVerifier,
payloadExecutor);
if (!signatureVerifier.batchVerify()) {
throw new StateTransitionException(
Expand Down Expand Up @@ -341,7 +342,8 @@ protected void processBlock(
processBlockHeader(state, block);
processRandaoNoValidation(state, block.getBody());
processEth1Data(state, block.getBody());
processOperationsNoValidation(state, block.getBody(), indexedAttestationCache);
processOperationsNoValidation(
state, block.getBody(), indexedAttestationCache, signatureVerifier);
}

@Override
Expand Down Expand Up @@ -432,7 +434,8 @@ public long getVoteCount(final BeaconState state, final Eth1Data eth1Data) {
protected void processOperationsNoValidation(
final MutableBeaconState state,
final BeaconBlockBody body,
final IndexedAttestationCache indexedAttestationCache)
final IndexedAttestationCache indexedAttestationCache,
final BLSSignatureVerifier signatureVerifier)
throws BlockProcessingException {
safelyProcess(
() -> {
Expand All @@ -444,7 +447,7 @@ protected void processOperationsNoValidation(
processProposerSlashingsNoValidation(
state, body.getProposerSlashings(), validatorExitContextSupplier);
processAttesterSlashings(
state, body.getAttesterSlashings(), validatorExitContextSupplier);
state, body.getAttesterSlashings(), validatorExitContextSupplier, signatureVerifier);
processAttestationsNoVerification(state, body.getAttestations(), indexedAttestationCache);
processDeposits(state, body.getDeposits());
processVoluntaryExitsNoValidation(
Expand Down Expand Up @@ -533,15 +536,17 @@ public void processAttesterSlashings(
() -> {
final Supplier<ValidatorExitContext> validatorExitContextSupplier =
beaconStateMutators.createValidatorExitContextSupplier(state);
processAttesterSlashings(state, attesterSlashings, validatorExitContextSupplier);
processAttesterSlashings(
state, attesterSlashings, validatorExitContextSupplier, BLSSignatureVerifier.SIMPLE);
});
}

@Override
public void processAttesterSlashings(
final MutableBeaconState state,
final SszList<AttesterSlashing> attesterSlashings,
final Supplier<ValidatorExitContext> validatorExitContextSupplier)
final Supplier<ValidatorExitContext> validatorExitContextSupplier,
final BLSSignatureVerifier signatureVerifier)
throws BlockProcessingException {
safelyProcess(
() -> {
Expand All @@ -550,7 +555,11 @@ public void processAttesterSlashings(
List<UInt64> indicesToSlash = new ArrayList<>();
final Optional<OperationInvalidReason> invalidReason =
operationValidator.validateAttesterSlashing(
state.getFork(), state, attesterSlashing, indicesToSlash::add);
state.getFork(),
state,
attesterSlashing,
indicesToSlash::add,
signatureVerifier);

checkArgument(
invalidReason.isEmpty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ void processAttesterSlashings(
void processAttesterSlashings(
MutableBeaconState state,
SszList<AttesterSlashing> attesterSlashings,
Supplier<ValidatorExitContext> validatorExitContextSupplier)
Supplier<ValidatorExitContext> validatorExitContextSupplier,
BLSSignatureVerifier signatureVerifier)
throws BlockProcessingException;

void processAttestations(
Expand Down
Loading