Skip to content

Commit

Permalink
building out solution, tests.
Browse files Browse the repository at this point in the history
Signed-off-by: Paul Harris <paul.harris@consensys.net>
  • Loading branch information
rolfyone committed Jan 16, 2025
1 parent a7ce4de commit e1f9c42
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -598,10 +598,8 @@ public void createAttestationData_shouldFailWhenHeadIsOptimistic() {
final UInt64 slot = spec.computeStartSlotAtEpoch(EPOCH).plus(ONE);
when(chainDataClient.getCurrentSlot()).thenReturn(slot);

final BeaconState state = createStateWithActiveValidators(epochStartSlot);
final SignedBeaconBlock block =
dataStructureUtil.randomSignedBeaconBlock(state.getSlot(), state);
final SignedBlockAndState blockAndState = new SignedBlockAndState(block, state);
final SignedBlockAndState blockAndState =
dataStructureUtil.randomSignedBlockAndState(epochStartSlot);

final SafeFuture<Optional<SignedBlockAndState>> blockAndStateResult =
completedFuture(Optional.of(blockAndState));
Expand All @@ -624,10 +622,9 @@ public void createAttestationData_shouldCreateAttestation() {
final UInt64 slot = spec.computeStartSlotAtEpoch(EPOCH).plus(ONE);
when(chainDataClient.getCurrentSlot()).thenReturn(slot);

final BeaconState state = createStateWithActiveValidators(epochStartSlot);
final SignedBeaconBlock block =
dataStructureUtil.randomSignedBeaconBlock(state.getSlot(), state);
final SignedBlockAndState blockAndState = new SignedBlockAndState(block, state);
dataStructureUtil.randomBlockAndState(epochStartSlot);
final SignedBlockAndState blockAndState =
dataStructureUtil.randomSignedBlockAndState(epochStartSlot);

final SafeFuture<Optional<SignedBlockAndState>> blockAndStateResult =
completedFuture(Optional.of(blockAndState));
Expand All @@ -646,7 +643,10 @@ public void createAttestationData_shouldCreateAttestation() {
assertThat(attestationData)
.isEqualTo(
spec.getGenericAttestationData(
slot, state, block.getMessage(), UInt64.valueOf(committeeIndex)));
slot,
blockAndState.getState(),
blockAndState.getBlock().getMessage(),
UInt64.valueOf(committeeIndex)));
assertThat(attestationData.getSlot()).isEqualTo(slot);
final InOrder inOrder = inOrder(forkChoiceTrigger, chainDataClient);

Expand Down Expand Up @@ -674,11 +674,10 @@ public void createAttestationData_shouldUseCorrectSourceWhenEpochTransitionRequi
// Slot is from before the current epoch, so we need to ensure we process the epoch transition
final UInt64 blockSlot = slot.minus(1);

final BeaconState wrongState = createStateWithActiveValidators(blockSlot);
final BeaconState rightState = createStateWithActiveValidators(slot);
final SignedBeaconBlock block =
dataStructureUtil.randomSignedBeaconBlock(wrongState.getSlot(), wrongState);
final SignedBlockAndState blockAndState = new SignedBlockAndState(block, wrongState);
final SignedBlockAndState blockAndState =
dataStructureUtil.randomSignedBlockAndState(blockSlot);
final SignedBeaconBlock block = blockAndState.getBlock();

final SafeFuture<Optional<SignedBlockAndState>> blockAndStateResult =
completedFuture(Optional.of(blockAndState));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ public class StateAndBlockSummary implements BeaconBlockSummary {
protected StateAndBlockSummary(final BeaconBlockSummary blockSummary, final BeaconState state) {
checkNotNull(blockSummary);
checkNotNull(state);
checkArgument(
blockSummary.getStateRoot().equals(state.hashTreeRoot()),
"Block state root must match the supplied state");
final Bytes32 latestBlockHeaderBodyRoot = state.getLatestBlockHeader().getBodyRoot();
// if the state slot is 0, we're either at genesis or testing
if (!state.getSlot().isZero()) {
// This check would allow a state to have an empty slot, and still be a valid block and state.
checkArgument(
latestBlockHeaderBodyRoot.equals(blockSummary.getBodyRoot()),
"Block root for the state must match the supplied state at slot "
+ blockSummary.getSlot());
}
this.blockSummary = blockSummary;
this.state = state;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
/**
* Represents an "anchor" - a trusted, finalized (block, state, checkpoint) tuple from which we can
* sync.
*
* <p>NOTE: Anchor point is a teku concept. <br>
* - state needs to be the anchor_state as used in fork choice<br>
* - checkpoint is an actual checkpoint as defined in fork choice, is the first slot of epoch<br>
* - blockSummary is the block from the finalized anchor state
*/
public class AnchorPoint extends StateAndBlockSummary {
private final Spec spec;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import tech.pegasys.teku.spec.config.SpecConfig;
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.BeaconBlockHeader;
import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
Expand Down Expand Up @@ -223,11 +224,14 @@ public SafeFuture<SignedBlockAndState> createNewBlockSkippingStateTransition(
blockBody);

// Sign block and set block signature
BLSSignature blockSignature = signer.signBlock(block, state.getForkInfo()).join();
final BLSSignature blockSignature =
signer.signBlock(block, state.getForkInfo()).join();

final SignedBeaconBlock signedBlock =
SignedBeaconBlock.create(spec, block, blockSignature);
return new SignedBlockAndState(signedBlock, blockSlotState);
final BeaconState updatedState =
state.updated(w -> w.setLatestBlockHeader(BeaconBlockHeader.fromBlock(block)));
return new SignedBlockAndState(signedBlock, updatedState);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,10 @@ public List<SignedBlockAndState> randomSignedBlockAndStateSequence(
final Bytes32 stateRoot = state.hashTreeRoot();
final SignedBeaconBlock block =
signedBlock(randomBeaconBlock(nextSlot, parentRoot, stateRoot, full));
blocks.add(new SignedBlockAndState(block, state));
final BeaconState updatedState =
state.updated(
w -> w.setLatestBlockHeader(BeaconBlockHeader.fromBlock(block.getMessage())));
blocks.add(new SignedBlockAndState(block, updatedState));
parentBlock = block;
}
return blocks;
Expand Down Expand Up @@ -2027,8 +2030,10 @@ public AnchorPoint createAnchorFromState(final BeaconState anchorState) {
final Bytes32 anchorRoot = anchorBlock.hashTreeRoot();
final UInt64 anchorEpoch = spec.getCurrentEpoch(anchorState);
final Checkpoint anchorCheckpoint = new Checkpoint(anchorEpoch, anchorRoot);
final BeaconState updatedAnchorState =
anchorState.updated(w -> w.setLatestBlockHeader(BeaconBlockHeader.fromBlock(anchorBlock)));

return AnchorPoint.create(spec, anchorCheckpoint, signedAnchorBlock, anchorState);
return AnchorPoint.create(spec, anchorCheckpoint, signedAnchorBlock, updatedAnchorState);
}

public SignedContributionAndProof randomSignedContributionAndProof() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,13 +325,10 @@ public void loadedInitialStateResource(
}
}

public void errorIncompatibleInitialState(final UInt64 epoch) {
log.error(
"Cannot start with provided initial state for the epoch {}, "
+ "checkpoint occurred on the empty slot, which is not yet supported.\n"
+ "If you are using remote checkpoint source, "
+ "please wait for the next epoch to finalize and retry.",
epoch);
public void warningUnexpectedInitialState(final UInt64 epoch, final UInt64 stateSlot, final UInt64 blockSlot) {
log.warn(
"Starting from state at slot {}, with block slot {}, which is not an anchor state. " +
"Ensure that slots from {} - {} are empty. At epoch {}.", stateSlot, blockSlot, stateSlot, blockSlot, epoch);
}

public void warnInitialStateIgnored() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

package tech.pegasys.teku.services.beaconchain;

import static tech.pegasys.teku.infrastructure.exceptions.ExitConstants.ERROR_EXIT_CODE;
import static tech.pegasys.teku.infrastructure.logging.StatusLogger.STATUS_LOG;
import static tech.pegasys.teku.networks.Eth2NetworkConfiguration.FINALIZED_STATE_URL_PATH;

Expand Down Expand Up @@ -88,8 +87,10 @@ private AnchorPoint getAnchorPoint(
STATUS_LOG.loadingInitialStateResource(sanitizedResource);
final BeaconState state = ChainDataLoader.loadState(spec, stateResource);
if (state.getSlot().isGreaterThan(state.getLatestBlockHeader().getSlot())) {
STATUS_LOG.errorIncompatibleInitialState(spec.computeEpochAtSlot(state.getSlot()));
System.exit(ERROR_EXIT_CODE);
STATUS_LOG.warningUnexpectedInitialState(
spec.computeEpochAtSlot(state.getSlot()),
state.getSlot(),
state.getLatestBlockHeader().getSlot());
}
final AnchorPoint anchor = AnchorPoint.fromInitialState(spec, state);
STATUS_LOG.loadedInitialStateResource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,41 @@ public void initializeFromAnchorPoint_withTimeGreaterThanAnchorBlockTime() {
assertThat(recentChainData.getStore().getTimeSeconds()).isEqualTo(time);
}

@Test
void initializeFromAnchorPoint_withEmptySlotAtEndOfFinalizedEpoch() {
initPreGenesis();
final ChainBuilder chainBuilder = ChainBuilder.create(spec);
final UInt64 genesisTime = UInt64.valueOf(5000);
chainBuilder.generateGenesis(genesisTime, true);
final List<SignedBlockAndState> chain = chainBuilder.generateBlocksUpToSlot(15);
chainBuilder.generateBlockAtSlot(17);
final SignedBlockAndState anchor = chain.getLast();

final AnchorPoint anchorPoint = AnchorPoint.fromInitialState(spec, anchor.getState());
final UInt64 anchorBlockTime =
anchorPoint.getBlockSlot().times(genesisSpecConfig.getSecondsPerSlot()).plus(genesisTime);
final UInt64 time = anchorBlockTime.plus(100);
recentChainData.initializeFromAnchorPoint(anchorPoint, time);
assertThat(recentChainData.getStore().getTimeSeconds()).isEqualTo(time);
}

@Test
void initializeFromAnchorPoint_withEmptySlotAtStartOEpoch() {
initPreGenesis();
final ChainBuilder chainBuilder = ChainBuilder.create(spec);
final UInt64 genesisTime = UInt64.valueOf(5000);
chainBuilder.generateGenesis(genesisTime, true);
chainBuilder.generateBlocksUpToSlot(15);
final SignedBlockAndState anchor = chainBuilder.generateBlockAtSlot(18);

final AnchorPoint anchorPoint = AnchorPoint.fromInitialState(spec, anchor.getState());
final UInt64 anchorBlockTime =
anchorPoint.getBlockSlot().times(genesisSpecConfig.getSecondsPerSlot()).plus(genesisTime);
final UInt64 time = anchorBlockTime.plus(100);
recentChainData.initializeFromAnchorPoint(anchorPoint, time);
assertThat(recentChainData.getStore().getTimeSeconds()).isEqualTo(time);
}

@Test
void getGenesisData_shouldBeEmptyPreGenesis() {
initPreGenesis();
Expand Down

0 comments on commit e1f9c42

Please sign in to comment.