1717import static org .junit .jupiter .api .Assertions .assertDoesNotThrow ;
1818import static tech .pegasys .teku .infrastructure .async .SafeFutureAssert .safeJoin ;
1919import static tech .pegasys .teku .infrastructure .time .TimeUtilities .secondsToMillis ;
20+ import static tech .pegasys .teku .reference .BlsSetting .IGNORED ;
21+ import static tech .pegasys .teku .reference .TestDataUtils .loadYaml ;
2022
23+ import com .fasterxml .jackson .annotation .JsonIgnoreProperties ;
24+ import com .fasterxml .jackson .annotation .JsonProperty ;
2125import com .google .common .collect .ImmutableMap ;
2226import java .io .IOException ;
27+ import java .nio .file .Path ;
2328import java .util .ArrayList ;
2429import java .util .Collections ;
2530import java .util .List ;
4045import tech .pegasys .teku .infrastructure .unsigned .UInt64 ;
4146import tech .pegasys .teku .kzg .KZG ;
4247import tech .pegasys .teku .kzg .KZGProof ;
48+ import tech .pegasys .teku .reference .BlsSetting ;
4349import tech .pegasys .teku .reference .KzgRetriever ;
4450import tech .pegasys .teku .reference .TestDataUtils ;
4551import tech .pegasys .teku .reference .TestExecutor ;
5359import tech .pegasys .teku .spec .datastructures .blocks .SignedBlockAndState ;
5460import tech .pegasys .teku .spec .datastructures .blocks .SlotAndBlockRoot ;
5561import tech .pegasys .teku .spec .datastructures .execution .PowBlock ;
62+ import tech .pegasys .teku .spec .datastructures .forkchoice .ProtoNodeData ;
63+ import tech .pegasys .teku .spec .datastructures .forkchoice .ReadOnlyForkChoiceStrategy ;
5664import tech .pegasys .teku .spec .datastructures .forkchoice .VoteUpdater ;
5765import tech .pegasys .teku .spec .datastructures .operations .Attestation ;
5866import tech .pegasys .teku .spec .datastructures .operations .AttesterSlashing ;
5967import tech .pegasys .teku .spec .datastructures .state .AnchorPoint ;
6068import tech .pegasys .teku .spec .datastructures .state .Checkpoint ;
6169import tech .pegasys .teku .spec .datastructures .state .beaconstate .BeaconState ;
70+ import tech .pegasys .teku .spec .datastructures .util .AttestationProcessingResult ;
6271import tech .pegasys .teku .spec .executionlayer .ExecutionLayerChannelStub ;
6372import tech .pegasys .teku .spec .executionlayer .ExecutionPayloadStatus ;
6473import tech .pegasys .teku .spec .executionlayer .PayloadStatus ;
@@ -97,6 +106,13 @@ public class ForkChoiceTestExecutor implements TestExecutor {
97106 .put ("fork_choice/should_override_forkchoice_update" , new ForkChoiceTestExecutor ())
98107 .put ("fork_choice/get_proposer_head" , new ForkChoiceTestExecutor ("basic_is_parent_root" ))
99108 .put ("fork_choice/deposit_with_reorg" , new ForkChoiceTestExecutor ())
109+ // Fork choice generated test types
110+ .put ("fork_choice_compliance/block_weight_test" , new ForkChoiceTestExecutor ())
111+ .put ("fork_choice_compliance/block_tree_test" , new ForkChoiceTestExecutor ())
112+ .put ("fork_choice_compliance/attester_slashing_test" , new ForkChoiceTestExecutor ())
113+ .put ("fork_choice_compliance/invalid_message_test" , new ForkChoiceTestExecutor ())
114+ .put ("fork_choice_compliance/block_cover_test" , new ForkChoiceTestExecutor ())
115+ .put ("fork_choice_compliance/shuffling_test" , new ForkChoiceTestExecutor ())
100116 .build ();
101117
102118 private final List <?> testsToSkip ;
@@ -112,9 +128,10 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {
112128 "Test " + testDefinition .getDisplayName () + " has been ignored" );
113129 }
114130
115- // Note: The fork choice spec says there may be settings in a meta.yaml file but currently no
116- // tests actually have one, so we currently don't bother trying to load it.
117- final Spec spec = testDefinition .getSpec ();
131+ // Load `meta.yaml` and read the BLS setting
132+ final ForkChoiceMetaData metaData = getMetaData (testDefinition );
133+ final boolean blsDisabled = metaData .getBlsSetting () == IGNORED ;
134+ final Spec spec = testDefinition .getSpec (!blsDisabled );
118135 final BeaconState anchorState =
119136 TestDataUtils .loadStateFromSsz (testDefinition , "anchor_state" + SSZ_SNAPPY_EXTENSION );
120137 final SignedBeaconBlock anchorBlock = loadAnchorBlock (testDefinition );
@@ -300,14 +317,20 @@ private void applyAttestation(
300317 final ForkChoice forkChoice ,
301318 final Map <String , Object > step ) {
302319 final String attestationName = get (step , "attestation" );
320+ final boolean valid = !step .containsKey ("valid" ) || (boolean ) step .get ("valid" );
303321 final Attestation attestation =
304322 TestDataUtils .loadSsz (
305323 testDefinition ,
306324 attestationName + SSZ_SNAPPY_EXTENSION ,
307325 testDefinition .getSpec ().getGenesisSchemaDefinitions ().getAttestationSchema ());
308326 final Spec spec = testDefinition .getSpec ();
309- assertThat (forkChoice .onAttestation (ValidatableAttestation .from (spec , attestation )))
310- .isCompleted ();
327+ final SafeFuture <AttestationProcessingResult > result =
328+ forkChoice .onAttestation (ValidatableAttestation .from (spec , attestation ));
329+ assertThat (result ).isCompleted ();
330+ AttestationProcessingResult processingResult = safeJoin (result );
331+ assertThat (processingResult .isSuccessful ())
332+ .withFailMessage (processingResult .getInvalidReason ())
333+ .isEqualTo (valid );
311334 }
312335
313336 private void applyAttesterSlashing (
@@ -530,6 +553,32 @@ private void applyChecks(
530553 assertThat (expectedValidatorIsConnected ).isTrue ();
531554 }
532555
556+ case "viable_for_head_roots_and_weights" -> {
557+ final List <Map <String , Object >> viableHeadRootsAndWeightsData = get (checks , checkType );
558+ final Map <Bytes32 , UInt64 > viableHeadRootsAndWeights =
559+ viableHeadRootsAndWeightsData .stream ()
560+ .collect (
561+ Collectors .toMap (
562+ entry -> Bytes32 .fromHexString ((String ) entry .get ("root" )),
563+ entry -> UInt64 .valueOf (entry .get ("weight" ).toString ())));
564+ final Map <Bytes32 , UInt64 > chainHeadRootsAndWeights =
565+ recentChainData
566+ .getForkChoiceStrategy ()
567+ .map (ReadOnlyForkChoiceStrategy ::getChainHeads )
568+ .orElse (Collections .emptyList ())
569+ .stream ()
570+ .collect (Collectors .toMap (ProtoNodeData ::getRoot , ProtoNodeData ::getWeight ));
571+
572+ assertThat (chainHeadRootsAndWeights .keySet ())
573+ .containsAll (viableHeadRootsAndWeights .keySet ());
574+
575+ for (Bytes32 root : viableHeadRootsAndWeights .keySet ()) {
576+ UInt64 weight = viableHeadRootsAndWeights .get (root );
577+ UInt64 actualWeight = chainHeadRootsAndWeights .get (root );
578+ assertThat (actualWeight ).describedAs ("block %s's weight" , root ).isEqualTo (weight );
579+ }
580+ }
581+
533582 default ->
534583 throw new UnsupportedOperationException ("Unsupported check type: " + checkType );
535584 }
@@ -581,4 +630,34 @@ private static Optional<Bytes32> getOptionallyBytes32(
581630 final Map <String , Object > yamlData , final String key ) {
582631 return ForkChoiceTestExecutor .<String >getOptionally (yamlData , key ).map (Bytes32 ::fromHexString );
583632 }
633+
634+ private static ForkChoiceMetaData getMetaData (final TestDefinition testDefinition )
635+ throws IOException {
636+ final ForkChoiceMetaData metaData ;
637+ final Path metaPath = testDefinition .getTestDirectory ().resolve ("meta.yaml" );
638+ if (metaPath .toFile ().exists ()) {
639+ metaData = loadYaml (testDefinition , "meta.yaml" , ForkChoiceMetaData .class );
640+ } else {
641+ metaData = ForkChoiceMetaData .DEFAULT ;
642+ }
643+
644+ return metaData ;
645+ }
646+
647+ @ JsonIgnoreProperties (ignoreUnknown = true )
648+ private static class ForkChoiceMetaData {
649+ static final ForkChoiceMetaData DEFAULT = new ForkChoiceMetaData (0 );
650+
651+ private ForkChoiceMetaData (
652+ @ JsonProperty (value = "bls_setting" , required = false , defaultValue = "0" )
653+ final int blsSetting ) {
654+ this .blsSetting = blsSetting ;
655+ }
656+
657+ private final int blsSetting ;
658+
659+ public BlsSetting getBlsSetting () {
660+ return BlsSetting .forCode (blsSetting );
661+ }
662+ }
584663}
0 commit comments