Skip to content

Commit

Permalink
Added more utilities for late block reorg (Consensys#7750)
Browse files Browse the repository at this point in the history
* Added more utilities for late block reorg

 - is_parent_strong
 - is_head_weak
 - is_proposing_on_time
 - is_ffg_competitive

The head_weak and parent_strong functions I put into store ultimately, as it has all the context needed. It did mean a little restructuring to enable testing, but I think it currently is a sensible home for it.

partially addresses Consensys#6595

Signed-off-by: Paul Harris <[email protected]>
  • Loading branch information
rolfyone authored Nov 23, 2023
1 parent 4c20f78 commit 576e4b8
Show file tree
Hide file tree
Showing 12 changed files with 550 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,13 @@ default SafeFuture<Optional<BeaconBlock>> retrieveBlock(Bytes32 blockRoot) {

SafeFuture<Optional<BeaconState>> retrieveCheckpointState(
Checkpoint checkpoint, BeaconState latestStateAtEpoch);

// implements is_head_weak from fork-choice Consensus Spec
SafeFuture<Optional<Boolean>> isHeadWeak(final Bytes32 root);

// implements is_parent_strong from fork-choice Consensus Spec
SafeFuture<Optional<Boolean>> isParentStrong(final Bytes32 parentRoot);

// implements is_ffg_competitive from Consensus Spec
Optional<Boolean> isFfgCompetitive(final Bytes32 headRoot, final Bytes32 parentRoot);
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public Bytes32 getSeed(BeaconState state, UInt64 epoch, Bytes4 domainType)
* @return
*/
public UInt64 calculateCommitteeFraction(
final BeaconState beaconState, final UInt64 committeePercent) {
final BeaconState beaconState, final int committeePercent) {
final UInt64 committeeWeight =
getTotalActiveBalance(beaconState).dividedBy(config.getSlotsPerEpoch());
return committeeWeight.times(committeePercent).dividedBy(100);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ void calculateCommitteeFraction_full() {
spec.atSlot(state.getSlot()).beaconStateAccessors().getTotalActiveBalance(state);
final UInt64 totalActiveBalancePerSlot =
totalActiveBalance.dividedBy(spec.getGenesisSpec().getSlotsPerEpoch());
final UInt64 fraction =
beaconStateAccessors.calculateCommitteeFraction(state, UInt64.valueOf(100));
final UInt64 fraction = beaconStateAccessors.calculateCommitteeFraction(state, 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);
Expand All @@ -151,7 +150,7 @@ void calculateCommitteeFraction_minimal() {
spec.atSlot(state.getSlot()).beaconStateAccessors().getTotalActiveBalance(state);
final UInt64 totalActiveBalancePerSlot =
totalActiveBalance.dividedBy(spec.getGenesisSpec().getSlotsPerEpoch());
final UInt64 fraction = beaconStateAccessors.calculateCommitteeFraction(state, UInt64.ONE);
final UInt64 fraction = beaconStateAccessors.calculateCommitteeFraction(state, 1);
// should be 1% of balance per slot...
assertThat(fraction).isEqualTo(totalActiveBalancePerSlot.dividedBy(100));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,21 @@ public SafeFuture<Optional<BeaconState>> retrieveCheckpointState(
return SafeFuture.completedFuture(Optional.of(latestStateAtEpoch));
}

@Override
public SafeFuture<Optional<Boolean>> isHeadWeak(Bytes32 root) {
return SafeFuture.completedFuture(Optional.empty());
}

@Override
public SafeFuture<Optional<Boolean>> isParentStrong(Bytes32 parentRoot) {
return SafeFuture.completedFuture(Optional.empty());
}

@Override
public Optional<Boolean> isFfgCompetitive(Bytes32 headRoot, Bytes32 parentRoot) {
return Optional.empty();
}

@Override
public Optional<List<BlobSidecar>> getBlobSidecarsIfAvailable(
final SlotAndBlockRoot slotAndBlockRoot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import static tech.pegasys.teku.spec.constants.NetworkConstants.INTERVALS_PER_SLOT;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import java.util.Map;
import java.util.Optional;
import org.apache.logging.log4j.LogManager;
Expand All @@ -33,6 +35,9 @@ public class BlockTimelinessTracker {
private final Spec spec;
private final RecentChainData recentChainData;

private final Supplier<UInt64> genesisTimeSupplier;
private final Supplier<UInt64> genesisTimeMillisSupplier;

// implements is_timely from Consensus Spec
public BlockTimelinessTracker(
final Spec spec, final RecentChainData recentChainData, final TimeProvider timeProvider) {
Expand All @@ -44,12 +49,14 @@ public BlockTimelinessTracker(
spec.getGenesisSpec().getSlotsPerEpoch() * epochsForTimeliness);
this.timeProvider = timeProvider;
this.recentChainData = recentChainData;
this.genesisTimeSupplier = Suppliers.memoize(recentChainData::getGenesisTime);
this.genesisTimeMillisSupplier = Suppliers.memoize(recentChainData::getGenesisTimeMillis);
}

public void setBlockTimelinessFromArrivalTime(
final SignedBeaconBlock block, final UInt64 arrivalTimeMillis) {
final UInt64 genesisTime = recentChainData.getGenesisTime();
final UInt64 computedSlot = spec.getCurrentSlot(timeProvider.getTimeInSeconds(), genesisTime);
final UInt64 computedSlot =
spec.getCurrentSlot(timeProvider.getTimeInSeconds(), genesisTimeSupplier.get());
final Bytes32 root = block.getRoot();
if (computedSlot.isGreaterThan(block.getMessage().getSlot())) {
LOG.debug(
Expand All @@ -65,7 +72,7 @@ public void setBlockTimelinessFromArrivalTime(
.ifPresent(
slot -> {
final UInt64 slotStartTimeMillis =
spec.getSlotStartTimeMillis(slot, genesisTime.times(1000));
spec.getSlotStartTimeMillis(slot, genesisTimeMillisSupplier.get());
final int millisIntoSlot =
arrivalTimeMillis.minusMinZero(slotStartTimeMillis).intValue();

Expand All @@ -87,7 +94,34 @@ public void setBlockTimelinessFromArrivalTime(
});
}

public Optional<Boolean> isBlockTimely(final Bytes32 root) {
Optional<Boolean> isBlockTimely(final Bytes32 root) {
return Optional.ofNullable(blockTimeliness.get(root));
}

// is_proposing_on_time from consensus-spec
// 'on time' is before we're half-way to the attester time. logically, if the slot is 3 segments,
// then splitting into 6 segments is half-way to the attestation time.
public boolean isProposingOnTime(final UInt64 slot) {
final UInt64 slotStartTimeMillis =
spec.getSlotStartTimeMillis(slot, genesisTimeMillisSupplier.get());
final UInt64 timelinessLimit = spec.getMillisPerSlot(slot).dividedBy(INTERVALS_PER_SLOT * 2);
final UInt64 currentTimeMillis = timeProvider.getTimeInMillis();
final boolean isTimely =
currentTimeMillis.minusMinZero(slotStartTimeMillis).isLessThan(timelinessLimit);
LOG.debug(
"Check ProposingOnTime for slot {}, slot start time is {} ms and current time is {} ms, limit is {} ms result: {}",
slot,
slotStartTimeMillis,
currentTimeMillis,
timelinessLimit,
isTimely);
return isTimely;
}

// Implements is_head_late form consensus-spec
// caveat: if the root was not found, will default to it being timely,
// on the basis that it's not safe to make choices about blocks we don't know about
public boolean isBlockLate(final Bytes32 root) {
return !isBlockTimely(root).orElse(true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,12 @@ public void setBlockTimelinessFromArrivalTime(
blockTimelinessTracker.setBlockTimelinessFromArrivalTime(block, arrivalTime);
}

public Optional<Boolean> getBlockTimeliness(final Bytes32 blockRoot) {
return blockTimelinessTracker.isBlockTimely(blockRoot);
// implements is_head_late from consensus spec
public boolean isBlockLate(final Bytes32 blockRoot) {
return blockTimelinessTracker.isBlockLate(blockRoot);
}

public boolean isProposingOnTime(final UInt64 slot) {
return blockTimelinessTracker.isProposingOnTime(slot);
}
}
Loading

0 comments on commit 576e4b8

Please sign in to comment.