Skip to content

Commit

Permalink
Support Deneb fork choice reference tests (Consensys#7541)
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov authored Oct 23, 2023
1 parent 5800509 commit 98724d3
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,6 @@ private TestExecutor getExecutorFor(final TestDefinition testDefinition) {
testExecutor = COMMON_TEST_TYPES.get(testDefinition.getTestType());
}

// TODO: https://github.com/Consensys/teku/issues/7539
if (testDefinition.getFork().equals(TestFork.DENEB)
&& testExecutor instanceof ForkChoiceTestExecutor) {
testExecutor = TestExecutor.IGNORE_TESTS;
}

if (testExecutor == null) {
return Assertions.fail("Unsupported test type " + testDefinition.getTestType());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.io.IOException;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand All @@ -41,10 +42,15 @@
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.async.eventthread.InlineEventThread;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.kzg.KZG;
import tech.pegasys.teku.kzg.KZGProof;
import tech.pegasys.teku.reference.TestDataUtils;
import tech.pegasys.teku.reference.TestExecutor;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.SpecVersion;
import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob;
import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
Expand All @@ -60,8 +66,9 @@
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannelStub;
import tech.pegasys.teku.spec.executionlayer.ExecutionPayloadStatus;
import tech.pegasys.teku.spec.executionlayer.PayloadStatus;
import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers;
import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult;
import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager;
import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb;
import tech.pegasys.teku.statetransition.forkchoice.ForkChoice;
import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceStateProvider;
import tech.pegasys.teku.statetransition.forkchoice.MergeTransitionBlockValidator;
Expand All @@ -76,6 +83,7 @@

public class ForkChoiceTestExecutor implements TestExecutor {
private static final Logger LOG = LogManager.getLogger();
private static final String SSZ_SNAPPY_EXTENSION = ".ssz_snappy";
public static final ImmutableMap<String, TestExecutor> FORK_CHOICE_TEST_TYPES =
ImmutableMap.<String, TestExecutor>builder()
.put("fork_choice/get_head", new ForkChoiceTestExecutor())
Expand All @@ -102,9 +110,9 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {

// 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(false);
final BeaconState anchorState =
TestDataUtils.loadStateFromSsz(testDefinition, "anchor_state.ssz_snappy");
final Spec spec = testDefinition.getSpec();
TestDataUtils.loadStateFromSsz(testDefinition, "anchor_state" + SSZ_SNAPPY_EXTENSION);
final SignedBeaconBlock anchorBlock = loadAnchorBlock(testDefinition);

final StorageSystem storageSystem =
Expand All @@ -118,12 +126,13 @@ spec, new SignedBlockAndState(anchorBlock, anchorState)),
final MergeTransitionBlockValidator transitionBlockValidator =
new MergeTransitionBlockValidator(spec, recentChainData, ExecutionLayerChannel.NOOP);
final InlineEventThread eventThread = new InlineEventThread();
final StubBlobSidecarManager blobSidecarManager = new StubBlobSidecarManager(spec);
final ForkChoice forkChoice =
new ForkChoice(
spec,
eventThread,
recentChainData,
BlobSidecarManager.NOOP,
blobSidecarManager,
new StubForkChoiceNotifier(),
new ForkChoiceStateProvider(eventThread, recentChainData),
new TickProcessor(spec, recentChainData),
Expand All @@ -135,7 +144,8 @@ spec, new SignedBlockAndState(anchorBlock, anchorState)),
new ExecutionLayerChannelStub(spec, false, Optional.empty());

try {
runSteps(testDefinition, spec, recentChainData, forkChoice, executionLayer);
runSteps(
testDefinition, spec, recentChainData, blobSidecarManager, forkChoice, executionLayer);
} catch (final AssertionError e) {
final String protoArrayData =
recentChainData.getForkChoiceStrategy().orElseThrow().getBlockData().stream()
Expand All @@ -151,6 +161,8 @@ spec, new SignedBlockAndState(anchorBlock, anchorState)),
+ "\nProtoarray data:\n"
+ protoArrayData,
e);
} finally {
freeTrustedSetupIfRequired(spec);
}
}

Expand All @@ -165,7 +177,7 @@ private SignedBeaconBlock loadAnchorBlock(final TestDefinition testDefinition) {
final BeaconBlock anchorBlock =
TestDataUtils.loadSsz(
testDefinition,
"anchor_block.ssz_snappy",
"anchor_block" + SSZ_SNAPPY_EXTENSION,
testDefinition.getSpec()::deserializeBeaconBlock);
return SignedBeaconBlock.create(testDefinition.getSpec(), anchorBlock, BLSSignature.empty());
}
Expand All @@ -174,6 +186,7 @@ private void runSteps(
final TestDefinition testDefinition,
final Spec spec,
final RecentChainData recentChainData,
final StubBlobSidecarManager blobSidecarManager,
final ForkChoice forkChoice,
final ExecutionLayerChannelStub executionLayer)
throws IOException {
Expand All @@ -188,7 +201,14 @@ private void runSteps(
final UInt64 currentSlot = recentChainData.getCurrentSlot().orElse(UInt64.ZERO);
LOG.info("Current slot: {} Epoch: {}", currentSlot, spec.computeEpochAtSlot(currentSlot));
} else if (step.containsKey("block")) {
applyBlock(testDefinition, spec, recentChainData, forkChoice, step, executionLayer);
applyBlock(
testDefinition,
spec,
recentChainData,
blobSidecarManager,
forkChoice,
step,
executionLayer);

} else if (step.containsKey("attestation")) {
applyAttestation(testDefinition, forkChoice, step);
Expand All @@ -214,7 +234,7 @@ private void applyPowBlock(
final ExecutionLayerChannelStub executionLayer) {
final String filename = (String) step.get("pow_block");
final PowBlock block =
TestDataUtils.loadSsz(testDefinition, filename + ".ssz_snappy", this::parsePowBlock);
TestDataUtils.loadSsz(testDefinition, filename + SSZ_SNAPPY_EXTENSION, this::parsePowBlock);
executionLayer.addPowBlock(block);
}

Expand Down Expand Up @@ -262,7 +282,7 @@ private void applyAttestation(
final Attestation attestation =
TestDataUtils.loadSsz(
testDefinition,
attestationName + ".ssz_snappy",
attestationName + SSZ_SNAPPY_EXTENSION,
testDefinition.getSpec().getGenesisSchemaDefinitions().getAttestationSchema());
final Spec spec = testDefinition.getSpec();
assertThat(forkChoice.onAttestation(ValidatableAttestation.from(spec, attestation)))
Expand All @@ -277,7 +297,7 @@ private void applyAttesterSlashing(
final AttesterSlashing attesterSlashing =
TestDataUtils.loadSsz(
testDefinition,
slashingName + ".ssz_snappy",
slashingName + SSZ_SNAPPY_EXTENSION,
testDefinition.getSpec().getGenesisSchemaDefinitions().getAttesterSlashingSchema());
assertDoesNotThrow(
() ->
Expand All @@ -288,14 +308,37 @@ private void applyBlock(
final TestDefinition testDefinition,
final Spec spec,
final RecentChainData recentChainData,
final StubBlobSidecarManager blobSidecarManager,
final ForkChoice forkChoice,
final Map<String, Object> step,
final ExecutionLayerChannelStub executionLayer) {
final String blockName = get(step, "block");
final boolean valid = !step.containsKey("valid") || (boolean) step.get("valid");
final SignedBeaconBlock block =
TestDataUtils.loadSsz(
testDefinition, blockName + ".ssz_snappy", spec::deserializeSignedBeaconBlock);
testDefinition, blockName + SSZ_SNAPPY_EXTENSION, spec::deserializeSignedBeaconBlock);
final List<Blob> blobs =
getOptionally(step, "blobs")
.map(
blobsName ->
TestDataUtils.loadSsz(
testDefinition,
blobsName + SSZ_SNAPPY_EXTENSION,
sszBytes -> spec.deserializeBlobsInBlock(sszBytes, block.getSlot()))
.asList())
.orElse(Collections.emptyList());
@SuppressWarnings("unchecked")
final List<KZGProof> proofs =
getOptionally(step, "proofs")
.map(
proofsArray ->
((List<String>) proofsArray).stream().map(KZGProof::fromHexString).toList())
.orElse(Collections.emptyList());
if (block.getMessage().getBody().toVersionDeneb().isPresent()) {
LOG.info(
"Preparing {} blobs with proofs {} for block {}", blobs.size(), proofs, block.getRoot());
blobSidecarManager.prepareBlobsAndProofsForBlock(block, blobs, proofs);
}
LOG.info(
"Importing block {} at slot {} with parent {}",
block.getRoot(),
Expand Down Expand Up @@ -446,6 +489,14 @@ private void assertCheckpoint(
.isEqualTo(new Checkpoint(expectedEpoch, expectedRoot));
}

private void freeTrustedSetupIfRequired(final Spec spec) {
Optional.ofNullable(spec.forMilestone(SpecMilestone.DENEB))
.map(SpecVersion::miscHelpers)
.flatMap(MiscHelpers::toVersionDeneb)
.map(MiscHelpersDeneb::getKzg)
.ifPresent(KZG::freeTrustedSetup);
}

@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
private static <T> T get(final Map<String, Object> yamlData, final String key) {
return (T) yamlData.get(key);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright Consensys Software Inc., 2023
*
* 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.forkchoice;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.kzg.KZGCommitment;
import tech.pegasys.teku.kzg.KZGProof;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.Blob;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.SignedBlobSidecar;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb;
import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment;
import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers;
import tech.pegasys.teku.spec.logic.versions.deneb.blobs.BlobSidecarsAndValidationResult;
import tech.pegasys.teku.spec.logic.versions.deneb.blobs.BlobSidecarsAvailabilityChecker;
import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager;
import tech.pegasys.teku.statetransition.validation.InternalValidationResult;

/** Simplified version of {@link BlobSidecarManager} which is used in fork choice reference tests */
class StubBlobSidecarManager implements BlobSidecarManager {

private final Map<Bytes32, BlobsAndProofs> blobsAndProofsByBlockRoot = new HashMap<>();

private final Spec spec;

StubBlobSidecarManager(final Spec spec) {
this.spec = spec;
}

/** Prepare the blobs and proofs for a block provided by the reference test * */
public void prepareBlobsAndProofsForBlock(
final SignedBeaconBlock block, final List<Blob> blobs, final List<KZGProof> proofs) {
blobsAndProofsByBlockRoot.put(block.getRoot(), new BlobsAndProofs(blobs, proofs));
}

@Override
public SafeFuture<InternalValidationResult> validateAndPrepareForBlockImport(
final SignedBlobSidecar signedBlobSidecar) {
return SafeFuture.failedFuture(
new UnsupportedOperationException("Not available in fork choice reference tests"));
}

@Override
public void prepareForBlockImport(final BlobSidecar blobSidecar) {
// NOOP
}

@Override
public void subscribeToReceivedBlobSidecar(
final ReceivedBlobSidecarListener receivedBlobSidecarListener) {
// NOOP
}

@Override
public boolean isAvailabilityRequiredAtSlot(final UInt64 slot) {
// NOOP
return false;
}

/**
* Creates an implementation of {@link BlobSidecarsAvailabilityChecker} which uses the simplified
* {@link MiscHelpers#isDataAvailable(List, List, List)} method with the blobs and proofs that the
* reference test has provided
*/
@Override
public BlobSidecarsAvailabilityChecker createAvailabilityChecker(final SignedBeaconBlock block) {
return new BlobSidecarsAvailabilityChecker() {
@Override
public boolean initiateDataAvailabilityCheck() {
return true;
}

@Override
public SafeFuture<BlobSidecarsAndValidationResult> getAvailabilityCheckResult() {
final BlobsAndProofs blobsAndProofs = blobsAndProofsByBlockRoot.remove(block.getRoot());
if (blobsAndProofs == null) {
return SafeFuture.completedFuture(BlobSidecarsAndValidationResult.NOT_REQUIRED);
}
return SafeFuture.completedFuture(validateImmediately(block, blobsAndProofs));
}

@Override
public BlobSidecarsAndValidationResult validateImmediately(
final List<BlobSidecar> blobSidecars) {
throw new UnsupportedOperationException("Not available in fork choice reference tests");
}

private BlobSidecarsAndValidationResult validateImmediately(
final SignedBeaconBlock block, final BlobsAndProofs blobsAndProofs) {
final List<KZGCommitment> kzgCommitments =
BeaconBlockBodyDeneb.required(block.getMessage().getBody())
.getBlobKzgCommitments()
.stream()
.map(SszKZGCommitment::getKZGCommitment)
.toList();
final List<Bytes> blobs = blobsAndProofs.blobs.stream().map(Blob::getBytes).toList();
try {
if (!spec.atSlot(block.getSlot())
.miscHelpers()
.isDataAvailable(blobs, kzgCommitments, blobsAndProofs.proofs)) {
return BlobSidecarsAndValidationResult.invalidResult(Collections.emptyList());
}
} catch (final Exception ex) {
return BlobSidecarsAndValidationResult.invalidResult(Collections.emptyList(), ex);
}
return BlobSidecarsAndValidationResult.validResult(Collections.emptyList());
}
};
}

@Override
public BlobSidecarsAndValidationResult createAvailabilityCheckerAndValidateImmediately(
final SignedBeaconBlock block, final List<BlobSidecar> blobSidecars) {
throw new UnsupportedOperationException("Not available in fork choice reference tests");
}

private record BlobsAndProofs(List<Blob> blobs, List<KZGProof> proofs) {}
}
Loading

0 comments on commit 98724d3

Please sign in to comment.