Skip to content

Commit

Permalink
Support Deneb fork choice reference tests
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov committed Sep 22, 2023
1 parent 78a929f commit 27ad1ac
Show file tree
Hide file tree
Showing 9 changed files with 288 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,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 @@ -41,10 +41,12 @@
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.KZGProof;
import tech.pegasys.teku.reference.TestDataUtils;
import tech.pegasys.teku.reference.TestExecutor;
import tech.pegasys.teku.spec.Spec;
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 @@ -61,7 +63,6 @@
import tech.pegasys.teku.spec.executionlayer.ExecutionPayloadStatus;
import tech.pegasys.teku.spec.executionlayer.PayloadStatus;
import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult;
import tech.pegasys.teku.statetransition.blobs.BlobSidecarManager;
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 +77,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 Down Expand Up @@ -103,7 +105,7 @@ 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 BeaconState anchorState =
TestDataUtils.loadStateFromSsz(testDefinition, "anchor_state.ssz_snappy");
TestDataUtils.loadStateFromSsz(testDefinition, "anchor_state" + SSZ_SNAPPY_EXTENSION);
final Spec spec = testDefinition.getSpec();
final SignedBeaconBlock anchorBlock = loadAnchorBlock(testDefinition);

Expand All @@ -118,12 +120,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 +138,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 Down Expand Up @@ -165,7 +169,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 +178,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 +193,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 +226,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 +274,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 +289,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 +300,36 @@ 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);
getOptionally(step, "blobs")
.ifPresent(
blobsName -> {
final List<Blob> blobs =
TestDataUtils.loadSsz(
testDefinition,
blobsName + SSZ_SNAPPY_EXTENSION,
sszBytes -> spec.deserializeBlobsInBlock(sszBytes, block.getSlot()))
.asList();
@SuppressWarnings("unchecked")
final List<KZGProof> proofs =
((List<String>) get(step, "proofs"))
.stream().map(KZGProof::fromHexString).toList();
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* 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.blobs.BlobSidecarManagerImpl;
import tech.pegasys.teku.statetransition.validation.InternalValidationResult;

/** Simplified version of {@link BlobSidecarManagerImpl} which is used in 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;
}

/** Manually provide blobs and proofs for a block * */
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 already provided blobs
* and proofs by the reference test
*/
@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.get(block.getRoot());
if (blobsAndProofs == null) {
return SafeFuture.completedFuture(BlobSidecarsAndValidationResult.NOT_REQUIRED);
}
return SafeFuture.completedFuture(validateBatch(block, blobsAndProofs));
}

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

public BlobSidecarsAndValidationResult validateBatch(
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) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@
package tech.pegasys.teku.ethtests.finder;

import java.nio.file.Path;
import java.util.Objects;
import java.util.function.Consumer;
import tech.pegasys.teku.ethtests.TestFork;
import tech.pegasys.teku.ethtests.TestSpecConfig;
import tech.pegasys.teku.networks.Eth2NetworkConfiguration;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.spec.TestSpecFactory;
import tech.pegasys.teku.spec.config.builder.DenebBuilder;
import tech.pegasys.teku.spec.networks.Eth2Network;

public class TestDefinition {
Expand Down Expand Up @@ -55,7 +59,6 @@ public Spec getSpec() {
if (spec == null) {
createSpec();
}

return spec;
}

Expand All @@ -75,7 +78,26 @@ private void createSpec() {
case TestFork.DENEB -> SpecMilestone.DENEB;
default -> throw new IllegalArgumentException("Unknown fork: " + fork);
};
spec = TestSpecFactory.create(highestSupportedMilestone, network);
spec =
TestSpecFactory.create(
highestSupportedMilestone,
network,
configBuilder -> configBuilder.denebBuilder(denebConfigModifier(network)));
}

private Consumer<DenebBuilder> denebConfigModifier(final Eth2Network network) {
return builder -> {
final String trustedSetupFilename =
network.equals(Eth2Network.MAINNET)
? "mainnet-trusted-setup.txt"
: "minimal-trusted-setup.txt";
builder.trustedSetupPath(getTrustedSetupPath(trustedSetupFilename)).kzgNoop(false);
};
}

private String getTrustedSetupPath(final String trustedSetupFilename) {
return Objects.requireNonNull(Eth2NetworkConfiguration.class.getResource(trustedSetupFilename))
.toExternalForm();
}

public String getTestType() {
Expand Down
11 changes: 11 additions & 0 deletions ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.bytes.Bytes4;
import tech.pegasys.teku.infrastructure.ssz.Merkleizable;
import tech.pegasys.teku.infrastructure.ssz.SszList;
import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.cache.IndexedAttestationCache;
Expand All @@ -51,6 +52,7 @@
import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation;
import tech.pegasys.teku.spec.datastructures.blobs.SignedBlobSidecarsUnblinder;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlindedBlobSidecar;
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.SignedBlindedBlobSidecar;
import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.SignedBlobSidecar;
Expand Down Expand Up @@ -376,6 +378,15 @@ public BeaconBlock deserializeBeaconBlock(final Bytes serializedBlock) {
.sszDeserialize(serializedBlock);
}

public SszList<Blob> deserializeBlobsInBlock(final Bytes serializedBlobs, final UInt64 slot) {
return atSlot(slot)
.getSchemaDefinitions()
.toVersionDeneb()
.orElseThrow(() -> new RuntimeException("Deneb milestone is required to load blobs"))
.getBlobsInBlockSchema()
.sszDeserialize(serializedBlobs);
}

public BlobSidecar deserializeBlobSidecar(final Bytes serializedBlobSidecar, final UInt64 slot) {
return atSlot(slot)
.getSchemaDefinitions()
Expand Down
Loading

0 comments on commit 27ad1ac

Please sign in to comment.