diff --git a/CHANGELOG.md b/CHANGELOG.md index d5c1973360d..dfd124ec30f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,5 +13,6 @@ the [releases page](https://github.com/Consensys/teku/releases). - By default, Teku won't allow syncing from genesis, users should use `--checkpoint-sync-url` when starting a new node. It is possible to revert back to the previous behaviour using the flag `--ignore-weak-subjectivity-period-enabled`. ### Additions and Improvements +- Added configuration attributes in support of honest validator late block reorg, which adds `REORG_HEAD_WEIGHT_THRESHOLD`, `REORG_PARENT_WEIGHT_THRESHOLD`, and `REORG_MAX_EPOCHS_SINCE_FINALIZATION` to phase 0 configurations. Mainnet values have been added as defaults for configurations that have not explicitly listed them. ### Bug Fixes diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/DelegatingSpecConfig.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/DelegatingSpecConfig.java index 8096d7cc0d1..19d4970cc86 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/DelegatingSpecConfig.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/DelegatingSpecConfig.java @@ -281,6 +281,21 @@ public int getSafeSlotsToUpdateJustified() { return specConfig.getSafeSlotsToUpdateJustified(); } + @Override + public int getReorgMaxEpochsSinceFinalization() { + return specConfig.getReorgMaxEpochsSinceFinalization(); + } + + @Override + public int getReorgHeadWeightThreshold() { + return specConfig.getReorgHeadWeightThreshold(); + } + + @Override + public int getReorgParentWeightThreshold() { + return specConfig.getReorgParentWeightThreshold(); + } + @Override public long getDepositChainId() { return specConfig.getDepositChainId(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfig.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfig.java index 49ff65e14c4..a3273edbea0 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfig.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfig.java @@ -160,6 +160,12 @@ default int getMillisPerSlot() { // Misc int getSafeSlotsToUpdateJustified(); + int getReorgMaxEpochsSinceFinalization(); + + int getReorgHeadWeightThreshold(); + + int getReorgParentWeightThreshold(); + // Casters default Optional toVersionAltair() { return Optional.empty(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfigPhase0.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfigPhase0.java index 1fed6e02f8b..8b249209b00 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfigPhase0.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/SpecConfigPhase0.java @@ -116,6 +116,10 @@ public class SpecConfigPhase0 implements SpecConfig { private final int attestationSubnetCount; private final int attestationSubnetExtraBits; private final int attestationSubnetPrefixBits; + private final int reorgMaxEpochsSinceFinalization; + + private final int reorgHeadWeightThreshold; + private final int reorgParentWeightThreshold; public SpecConfigPhase0( final Map rawConfig, @@ -182,7 +186,10 @@ public SpecConfigPhase0( final int subnetsPerNode, final int attestationSubnetCount, final int attestationSubnetExtraBits, - final int attestationSubnetPrefixBits) { + final int attestationSubnetPrefixBits, + final int reorgMaxEpochsSinceFinalization, + final int reorgHeadWeightThreshold, + final int reorgParentWeightThreshold) { this.rawConfig = rawConfig; this.eth1FollowDistance = eth1FollowDistance; this.maxCommitteesPerSlot = maxCommitteesPerSlot; @@ -249,6 +256,9 @@ public SpecConfigPhase0( this.attestationSubnetCount = attestationSubnetCount; this.attestationSubnetExtraBits = attestationSubnetExtraBits; this.attestationSubnetPrefixBits = attestationSubnetPrefixBits; + this.reorgMaxEpochsSinceFinalization = reorgMaxEpochsSinceFinalization; + this.reorgHeadWeightThreshold = reorgHeadWeightThreshold; + this.reorgParentWeightThreshold = reorgParentWeightThreshold; } @Override @@ -506,6 +516,21 @@ public int getSafeSlotsToUpdateJustified() { return safeSlotsToUpdateJustified; } + @Override + public int getReorgMaxEpochsSinceFinalization() { + return reorgMaxEpochsSinceFinalization; + } + + @Override + public int getReorgHeadWeightThreshold() { + return reorgHeadWeightThreshold; + } + + @Override + public int getReorgParentWeightThreshold() { + return reorgParentWeightThreshold; + } + @Override public int getProposerScoreBoost() { return proposerScoreBoost; diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/builder/SpecConfigBuilder.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/builder/SpecConfigBuilder.java index b141fc8cf7e..5947e4c79f3 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/builder/SpecConfigBuilder.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/config/builder/SpecConfigBuilder.java @@ -123,6 +123,13 @@ public class SpecConfigBuilder { private Integer attestationSubnetExtraBits; private Integer attestationSubnetPrefixBits; + // added after Phase0, so add default values, or will be compatability issue + private Integer reorgMaxEpochsSinceFinalization = 2; + + private Integer reorgHeadWeightThreshold = 20; + + private Integer reorgParentWeightThreshold = 160; + private final BuilderChain builderChain = BuilderChain.create(new AltairBuilder()) .appendBuilder(new BellatrixBuilder()) @@ -203,7 +210,10 @@ public SpecConfig build() { subnetsPerNode, attestationSubnetCount, attestationSubnetExtraBits, - attestationSubnetPrefixBits); + attestationSubnetPrefixBits, + reorgMaxEpochsSinceFinalization, + reorgHeadWeightThreshold, + reorgParentWeightThreshold); return builderChain.build(config); } @@ -274,6 +284,9 @@ private Map getValidationMap() { constants.put("attestationSubnetCount", attestationSubnetCount); constants.put("attestationSubnetExtraBits", attestationSubnetExtraBits); constants.put("attestationSubnetPrefixBits", attestationSubnetPrefixBits); + constants.put("reorgMaxEpochsSinceFinalization", reorgMaxEpochsSinceFinalization); + constants.put("reorgHeadWeightThreshold", reorgHeadWeightThreshold); + constants.put("reorgParentWeightThreshold", reorgParentWeightThreshold); return constants; } @@ -677,6 +690,22 @@ public SpecConfigBuilder attestationSubnetPrefixBits(final Integer attestationSu return this; } + public SpecConfigBuilder reorgMaxEpochsSinceFinalization( + final Integer reorgMaxEpochsSinceFinalization) { + this.reorgMaxEpochsSinceFinalization = reorgMaxEpochsSinceFinalization; + return this; + } + + public SpecConfigBuilder reorgHeadWeightThreshold(final Integer reorgHeadWeightThreshold) { + this.reorgHeadWeightThreshold = reorgHeadWeightThreshold; + return this; + } + + public SpecConfigBuilder reorgParentWeightThreshold(final Integer reorgParentWeightThreshold) { + this.reorgParentWeightThreshold = reorgParentWeightThreshold; + return this; + } + public SpecConfigBuilder altairBuilder(final Consumer consumer) { builderChain.withBuilder(AltairBuilder.class, consumer); return this; diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessors.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessors.java index 0ea29f591a6..b6851c3259e 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessors.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessors.java @@ -162,6 +162,20 @@ public Bytes32 getSeed(BeaconState state, UInt64 epoch, Bytes4 domainType) return Hash.sha256(domainType.getWrappedBytes(), epochBytes, mix); } + /** + * calculate_committee_fraction Refer to fork-choice specification. + * + * @param beaconState + * @param committeePercent + * @return + */ + public UInt64 calculateCommitteeFraction( + final BeaconState beaconState, final UInt64 committeePercent) { + final UInt64 committeeWeight = + getTotalActiveBalance(beaconState).dividedBy(config.getSlotsPerEpoch()); + return committeeWeight.times(committeePercent).dividedBy(100); + } + /** * return the number of committees in each slot for the given `epoch`. * diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtil.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtil.java index a4d5903c67b..4a901e54e6e 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtil.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtil.java @@ -167,6 +167,27 @@ public NavigableMap getAncestorsOnFork( return roots; } + /** + * is_shuffling_stable + * + *

Refer to fork-choice specification. + * + * @param slot + * @return + */ + public boolean isShufflingStable(final UInt64 slot) { + return !slot.mod(specConfig.getSlotsPerEpoch()).isZero(); + } + + public boolean isFinalizationOk(final ReadOnlyStore store, final UInt64 slot) { + final UInt64 epochsSinceFinalization = + miscHelpers + .computeEpochAtSlot(slot) + .minusMinZero(store.getFinalizedCheckpoint().getEpoch()); + return epochsSinceFinalization.isLessThanOrEqualTo( + specConfig.getReorgMaxEpochsSinceFinalization()); + } + private void maybeAddRoot( final UInt64 startSlot, final UInt64 step, diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/chiado.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/chiado.yaml index 484cba66f6b..45c6871cd78 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/chiado.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/chiado.yaml @@ -43,6 +43,12 @@ HYSTERESIS_UPWARD_MULTIPLIER: 5 # --------------------------------------------------------------- # 2**3 (= 8) SAFE_SLOTS_TO_UPDATE_JUSTIFIED: 8 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Validator # --------------------------------------------------------------- # 2**10 (= 1024) ~1.4 hour diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/gnosis.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/gnosis.yaml index bddc337e226..5cc13c73f82 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/gnosis.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/gnosis.yaml @@ -76,7 +76,12 @@ CHURN_LIMIT_QUOTIENT: 4096 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 - +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/holesky.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/holesky.yaml index 23323258e6b..f56c1ff8d2e 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/holesky.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/holesky.yaml @@ -66,6 +66,12 @@ MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/less-swift.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/less-swift.yaml index 707e10c1990..d1e0872e32b 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/less-swift.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/less-swift.yaml @@ -79,6 +79,12 @@ CHURN_LIMIT_QUOTIENT: 32 # --------------------------------------------------------------- # 70% PROPOSER_SCORE_BOOST: 70 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/lukso.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/lukso.yaml index bd90f42a159..e825952987d 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/lukso.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/lukso.yaml @@ -63,6 +63,12 @@ CAPELLA_FORK_EPOCH: 8100 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Time parameters # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml index 020325021b0..1b4493f1fae 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml @@ -92,6 +92,12 @@ MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/minimal.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/minimal.yaml index 8078c4db331..d7a965b87e5 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/minimal.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/minimal.yaml @@ -91,7 +91,12 @@ MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 - +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml index 62f0dd3e145..6dda352af77 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/prater.yaml @@ -77,7 +77,12 @@ CHURN_LIMIT_QUOTIENT: 65536 # --------------------------------------------------------------- # 70% PROPOSER_SCORE_BOOST: 70 - +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/sepolia.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/sepolia.yaml index 2a6ff8f44ef..a94b0d7587c 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/sepolia.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/sepolia.yaml @@ -68,6 +68,12 @@ CHURN_LIMIT_QUOTIENT: 65536 # --------------------------------------------------------------- # 40% PROPOSER_SCORE_BOOST: 40 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract # --------------------------------------------------------------- diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/swift.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/swift.yaml index cd57125aa05..66e33784d50 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/swift.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/swift.yaml @@ -80,6 +80,12 @@ MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8 # --------------------------------------------------------------- # 70% PROPOSER_SCORE_BOOST: 70 +# 20% +REORG_HEAD_WEIGHT_THRESHOLD: 20 +# 160% +REORG_PARENT_WEIGHT_THRESHOLD: 160 +# `2` epochs +REORG_MAX_EPOCHS_SINCE_FINALIZATION: 2 # Deposit contract diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessorsTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessorsTest.java index 60b0c06c64c..524aca45626 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessorsTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/BeaconStateAccessorsTest.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.spec.logic.common.helpers; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -129,6 +130,32 @@ public void getBeaconCommittee_stateIsNewerThanSlot() { assertDoesNotThrow(() -> beaconStateAccessors.getBeaconCommittee(state, oldSlot, ONE)); } + @Test + void calculateCommitteeFraction_full() { + final BeaconState state = dataStructureUtil.randomBeaconState(1024); + final UInt64 totalActiveBalance = + spec.atSlot(state.getSlot()).beaconStateAccessors().getTotalActiveBalance(state); + final UInt64 totalActiveBalancePerSlot = + totalActiveBalance.dividedBy(spec.getGenesisSpec().getSlotsPerEpoch()); + final UInt64 fraction = + beaconStateAccessors.calculateCommitteeFraction(state, UInt64.valueOf(100)); + // at its simplest, if we've divided by slots in the function, this should be + // totalActiveBalance/slots (because fraction is 100%) + assertThat(fraction).isEqualTo(totalActiveBalancePerSlot); + } + + @Test + void calculateCommitteeFraction_minimal() { + final BeaconState state = dataStructureUtil.randomBeaconState(1024); + final UInt64 totalActiveBalance = + spec.atSlot(state.getSlot()).beaconStateAccessors().getTotalActiveBalance(state); + final UInt64 totalActiveBalancePerSlot = + totalActiveBalance.dividedBy(spec.getGenesisSpec().getSlotsPerEpoch()); + final UInt64 fraction = beaconStateAccessors.calculateCommitteeFraction(state, UInt64.ONE); + // should be 1% of balance per slot... + assertThat(fraction).isEqualTo(totalActiveBalancePerSlot.dividedBy(100)); + } + private BeaconState createBeaconState() { return new BeaconStateTestBuilder(dataStructureUtil) .forkVersion(specConfig.getGenesisForkVersion()) diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtilTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtilTest.java index 9c0ca127743..924d8dca101 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtilTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/ForkChoiceUtilTest.java @@ -21,13 +21,18 @@ import static org.mockito.Mockito.when; import static tech.pegasys.teku.infrastructure.time.TimeUtilities.secondsToMillis; +import java.util.ArrayList; import java.util.Map; import java.util.NavigableMap; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; +import java.util.stream.Stream; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; @@ -307,6 +312,52 @@ void canOptimisticallyImport_shouldBeFalseWhenBlockIsMergeButNotOldEnough() { assertThat(forkChoiceUtil.canOptimisticallyImport(store, blockToImport)).isFalse(); } + @ParameterizedTest + @MethodSource("isShufflingStableConditions") + void isShufflingStable(final int slot, final boolean expectedResult) { + assertThat(forkChoiceUtil.isShufflingStable(UInt64.valueOf(slot))).isEqualTo(expectedResult); + } + + public static Stream isShufflingStableConditions() { + // slot , expectedResult + ArrayList args = new ArrayList<>(); + for (int i = 80; i < 100; i++) { + // every start of epoch, shuffling is considered not stable + args.add(Arguments.of(i, i % 8 != 0)); + } + + return args.stream(); + } + + @ParameterizedTest + @MethodSource("isFinalizationOkConditions") + void isFinalizationOk(final int epoch, final int testSlot, final boolean isFinalizationOk) { + final UInt64 finalizedEpoch = UInt64.valueOf(epoch); + final UInt64 slot = UInt64.valueOf(testSlot); + + final ReadOnlyStore myStore = mock(ReadOnlyStore.class); + final Checkpoint myCheckpoint = mock(Checkpoint.class); + when(myStore.getFinalizedCheckpoint()).thenReturn(myCheckpoint); + when(myCheckpoint.getEpoch()).thenReturn(finalizedEpoch); + assertThat(forkChoiceUtil.isFinalizationOk(myStore, slot)).isEqualTo(isFinalizationOk); + } + + // slots per epoch in this suite is 8 + // reorgMaxEpochsSinceFinalization is 2 + private static Stream isFinalizationOkConditions() { + // epoch 10 starts at slot (8 x 10) 80 + // epoch 13 starts at slot (8 x 13) 104 + // epoch , slot , true/false + ArrayList args = new ArrayList<>(); + args.add(Arguments.of(10, 0, true)); + args.add(Arguments.of(10, 80, true)); + args.add(Arguments.of(10, 96, true)); + args.add(Arguments.of(10, 103, true)); + args.add(Arguments.of(10, 104, false)); + + return args.stream(); + } + @Test void canOptimisticallyImport_shouldBeTrueWhenBlockIsMergeAndIsOldEnough() { final int blockSlot = 11;