Skip to content

Commit

Permalink
Added is_ffg_competitive
Browse files Browse the repository at this point in the history
partially addresses Consensys#6595

Signed-off-by: Paul Harris <[email protected]>
  • Loading branch information
rolfyone committed Nov 23, 2023
1 parent 8586476 commit 7e28069
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,7 @@ SafeFuture<Optional<BeaconState>> retrieveCheckpointState(

// 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 @@ -256,6 +256,11 @@ 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
19 changes: 19 additions & 0 deletions storage/src/main/java/tech/pegasys/teku/storage/store/Store.java
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,25 @@ public SafeFuture<Optional<Boolean>> isParentStrong(final Bytes32 parentRoot) {
});
}

@Override
public Optional<Boolean> isFfgCompetitive(Bytes32 headRoot, Bytes32 parentRoot) {
final Optional<ProtoNodeData> maybeHeadData = getBlockDataFromForkChoiceStrategy(headRoot);
final Optional<ProtoNodeData> maybeParentData = getBlockDataFromForkChoiceStrategy(parentRoot);
if (maybeParentData.isEmpty() || maybeHeadData.isEmpty()) {
return Optional.empty();
}
final Checkpoint headUnrealizedJustifiedCheckpoint =
maybeHeadData.get().getCheckpoints().getUnrealizedJustifiedCheckpoint();
final Checkpoint parentUnrealizedJustifiedCheckpoint =
maybeParentData.get().getCheckpoints().getUnrealizedJustifiedCheckpoint();
LOG.debug(
"head {}, compared to parent {}",
headUnrealizedJustifiedCheckpoint,
parentUnrealizedJustifiedCheckpoint);
return Optional.of(
headUnrealizedJustifiedCheckpoint.equals(parentUnrealizedJustifiedCheckpoint));
}

private Optional<ProtoNodeData> getBlockDataFromForkChoiceStrategy(final Bytes32 root) {
readLock.lock();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,11 @@ public SafeFuture<Optional<Boolean>> isParentStrong(Bytes32 parentRoot) {
return store.isParentStrong(parentRoot);
}

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

@Override
public Optional<SignedBeaconBlock> getBlockIfAvailable(final Bytes32 blockRoot) {
return Optional.ofNullable(blockData.get(blockRoot))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.assertThatSafeFuture;
import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.safeJoin;
Expand All @@ -33,6 +34,7 @@
import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.BlockCheckpoints;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot;
Expand Down Expand Up @@ -182,6 +184,79 @@ public void isParentStrong_wityZeroWeight() {
});
}

@Test
public void isFfgCompetitive_checkpointMatches() {
final BlockCheckpoints headBlockCheckpoint = mock(BlockCheckpoints.class);
final BlockCheckpoints parentBlockCheckpoint = mock(BlockCheckpoints.class);
final Checkpoint checkpoint = new Checkpoint(UInt64.ZERO, Bytes32.random());
when(headBlockCheckpoint.getUnrealizedJustifiedCheckpoint()).thenReturn(checkpoint);
when(parentBlockCheckpoint.getUnrealizedJustifiedCheckpoint()).thenReturn(checkpoint);
processChainHeadWithMockForkChoiceStrategy(
(store, blockAndState) -> {
final Bytes32 root = blockAndState.getRoot();
final Bytes32 parentRoot = blockAndState.getBlock().getParentRoot();
setProtoNodeDataForBlock(blockAndState, headBlockCheckpoint, parentBlockCheckpoint);
assertThat(store.isFfgCompetitive(root, parentRoot)).contains(true);
});
}

@Test
public void isFfgCompetitive_checkpointDifferent() {
final BlockCheckpoints headBlockCheckpoint = mock(BlockCheckpoints.class);
final BlockCheckpoints parentBlockCheckpoint = mock(BlockCheckpoints.class);
final Checkpoint checkpoint = new Checkpoint(UInt64.ZERO, Bytes32.random());
final Checkpoint checkpointParent = new Checkpoint(UInt64.ONE, Bytes32.random());
when(headBlockCheckpoint.getUnrealizedJustifiedCheckpoint()).thenReturn(checkpoint);
when(parentBlockCheckpoint.getUnrealizedJustifiedCheckpoint()).thenReturn(checkpointParent);
processChainHeadWithMockForkChoiceStrategy(
(store, blockAndState) -> {
final Bytes32 root = blockAndState.getRoot();
final Bytes32 parentRoot = blockAndState.getBlock().getParentRoot();
setProtoNodeDataForBlock(blockAndState, headBlockCheckpoint, parentBlockCheckpoint);
assertThat(store.isFfgCompetitive(root, parentRoot)).contains(false);
});
}

@Test
public void isFfgCompetitive_missingProtoNodeEntries() {
processChainHeadWithMockForkChoiceStrategy(
(store, blockAndState) -> {
final Bytes32 root = blockAndState.getRoot();
final Bytes32 parentRoot = blockAndState.getBlock().getParentRoot();
assertThat(store.isFfgCompetitive(root, parentRoot)).isEmpty();
});
}

private void setProtoNodeDataForBlock(
SignedBlockAndState blockAndState,
BlockCheckpoints headCheckpoint,
BlockCheckpoints parentCheckpoint) {
final Bytes32 root = blockAndState.getRoot();
final Bytes32 parentRoot = blockAndState.getParentRoot();
final ProtoNodeData protoNodeData =
new ProtoNodeData(
UInt64.ONE,
root,
blockAndState.getParentRoot(),
blockAndState.getStateRoot(),
Bytes32.random(),
ProtoNodeValidationStatus.VALID,
headCheckpoint,
UInt64.ZERO);
final ProtoNodeData parentNodeData =
new ProtoNodeData(
UInt64.ZERO,
parentRoot,
Bytes32.random(),
blockAndState.getStateRoot(),
Bytes32.random(),
ProtoNodeValidationStatus.VALID,
parentCheckpoint,
UInt64.ZERO);
when(dummyForkChoiceStrategy.getBlockData(root)).thenReturn(Optional.of(protoNodeData));
when(dummyForkChoiceStrategy.getBlockData(parentRoot)).thenReturn(Optional.of(parentNodeData));
}

private void setProtoNodeDataForBlock(
SignedBlockAndState blockAndState, final UInt64 headValue, final UInt64 parentValue) {
final Bytes32 root = blockAndState.getRoot();
Expand Down

0 comments on commit 7e28069

Please sign in to comment.