use of tech.pegasys.teku.core.ChainBuilder in project teku by ConsenSys.
the class ForkChoiceTest method onBlock_shouldReorgWhenProposerWeightingMakesForkBestChain.
@Test
void onBlock_shouldReorgWhenProposerWeightingMakesForkBestChain() {
forkChoice = new ForkChoice(spec, new InlineEventThread(), recentChainData, forkChoiceNotifier, transitionBlockValidator, true);
final UInt64 currentSlot = recentChainData.getCurrentSlot().orElseThrow();
final UInt64 lateBlockSlot = currentSlot.minus(1);
final ChainBuilder chainB = chainBuilder.fork();
final SignedBlockAndState chainBBlock1 = chainB.generateBlockAtSlot(lateBlockSlot, BlockOptions.create().setEth1Data(new Eth1Data(Bytes32.ZERO, UInt64.valueOf(6), Bytes32.ZERO)));
final SignedBlockAndState chainABlock1 = chainBuilder.generateBlockAtSlot(lateBlockSlot);
// All blocks received late for slot 1
importBlock(chainABlock1);
importBlock(chainBBlock1);
// At this point fork choice is tied with no votes for either chain
// The winner is the block with the greatest hash which is hard to control.
// So just find which block won and check that we can switch forks based on proposer reward
final SignedBlockAndState expectedChainHead;
if (recentChainData.getChainHead().orElseThrow().getRoot().equals(chainABlock1.getRoot())) {
// ChainA won, so try to switch to chain B
expectedChainHead = chainB.generateBlockAtSlot(currentSlot);
} else {
// ChainB won so try to switch to chain A
expectedChainHead = chainBuilder.generateBlockAtSlot(currentSlot);
}
importBlock(expectedChainHead);
assertThat(recentChainData.getStore().getProposerBoostRoot()).contains(expectedChainHead.getRoot());
assertThat(forkChoice.processHead()).isCompleted();
// Check we switched chains, if proposer reward wasn't considered we'd stay on the other fork
assertThat(recentChainData.getBestBlockRoot()).contains(expectedChainHead.getRoot());
}
use of tech.pegasys.teku.core.ChainBuilder in project teku by ConsenSys.
the class AbstractDatabaseTest method orphanedBlockStorageTest_multiple.
@Test
public void orphanedBlockStorageTest_multiple() {
createStorage(storageMode, StoreConfig.createDefault(), true);
final ChainBuilder primaryChain = ChainBuilder.create(spec, VALIDATOR_KEYS);
primaryChain.generateGenesis(genesisTime, true);
primaryChain.generateBlocksUpToSlot(3);
final ChainBuilder forkChain = primaryChain.fork();
// Primary chain's next block is at 5
primaryChain.generateBlockAtSlot(5);
final ChainBuilder secondFork = primaryChain.fork();
// Primary chain's next block is at 7
final SignedBlockAndState finalizedBlock = primaryChain.generateBlockAtSlot(7);
final Checkpoint finalizedCheckpoint = getCheckpointForBlock(primaryChain.getBlockAtSlot(7));
final UInt64 firstHotBlockSlot = finalizedCheckpoint.getEpochStartSlot(spec).plus(UInt64.ONE);
primaryChain.generateBlockAtSlot(firstHotBlockSlot);
// Fork chain's next block is at 6
forkChain.generateBlockAtSlot(6);
forkChain.generateBlockAtSlot(firstHotBlockSlot);
secondFork.generateBlockAtSlot(6);
secondFork.generateBlockAtSlot(firstHotBlockSlot);
// Setup database
initGenesis();
final Set<SignedBlockAndState> allBlocksAndStates = Streams.concat(primaryChain.streamBlocksAndStates(), forkChain.streamBlocksAndStates(), secondFork.streamBlocksAndStates()).collect(Collectors.toSet());
// Finalize at block 7, making the fork blocks unavailable
add(allBlocksAndStates);
justifyAndFinalizeEpoch(finalizedCheckpoint.getEpoch(), finalizedBlock);
assertThat(database.getNonCanonicalBlocksAtSlot(UInt64.valueOf(6)).size()).isEqualTo(2);
}
use of tech.pegasys.teku.core.ChainBuilder in project teku by ConsenSys.
the class AbstractDatabaseTest method testShouldRecordFinalizedBlocksAndStates.
protected void testShouldRecordFinalizedBlocksAndStates(final StateStorageMode storageMode, final boolean batchUpdate, Consumer<StateStorageMode> initializeDatabase) {
// Setup chains
// Both chains share block up to slot 3
final ChainBuilder primaryChain = ChainBuilder.create(spec, VALIDATOR_KEYS);
primaryChain.generateGenesis(genesisTime, true);
primaryChain.generateBlocksUpToSlot(3);
final ChainBuilder forkChain = primaryChain.fork();
// Fork chain's next block is at 6
forkChain.generateBlockAtSlot(6);
forkChain.generateBlocksUpToSlot(7);
// Primary chain's next block is at 7
final SignedBlockAndState finalizedBlock = primaryChain.generateBlockAtSlot(7);
final Checkpoint finalizedCheckpoint = getCheckpointForBlock(finalizedBlock.getBlock());
final UInt64 pruneToSlot = finalizedCheckpoint.getEpochStartSlot(spec);
// Add some blocks in the next epoch
final UInt64 hotSlot = pruneToSlot.plus(UInt64.ONE);
primaryChain.generateBlockAtSlot(hotSlot);
forkChain.generateBlockAtSlot(hotSlot);
// Setup database
initializeDatabase.accept(storageMode);
initGenesis();
final Set<SignedBlockAndState> allBlocksAndStates = Streams.concat(primaryChain.streamBlocksAndStates(), forkChain.streamBlocksAndStates()).collect(Collectors.toSet());
if (batchUpdate) {
final StoreTransaction transaction = recentChainData.startStoreTransaction();
add(transaction, allBlocksAndStates);
justifyAndFinalizeEpoch(finalizedCheckpoint.getEpoch(), finalizedBlock, transaction);
assertThat(transaction.commit()).isCompleted();
} else {
add(allBlocksAndStates);
justifyAndFinalizeEpoch(finalizedCheckpoint.getEpoch(), finalizedBlock);
}
// Upon finalization, we should prune data
final Set<Bytes32> blocksToPrune = Streams.concat(primaryChain.streamBlocksAndStates(0, pruneToSlot.longValue()), forkChain.streamBlocksAndStates(0, pruneToSlot.longValue())).map(SignedBlockAndState::getRoot).collect(Collectors.toSet());
blocksToPrune.remove(finalizedBlock.getRoot());
final Set<Checkpoint> checkpointsToPrune = Set.of(genesisCheckpoint);
// Check data was pruned from store
assertRecentDataWasPruned(store, blocksToPrune, checkpointsToPrune);
restartStorage();
// Check hot data
final List<SignedBlockAndState> expectedHotBlocksAndStates = List.of(finalizedBlock, primaryChain.getBlockAndStateAtSlot(hotSlot));
assertHotBlocksAndStates(store, expectedHotBlocksAndStates);
final SignedBlockAndState prunedForkBlock = forkChain.getBlockAndStateAtSlot(hotSlot);
assertThat(store.containsBlock(prunedForkBlock.getRoot())).isFalse();
// Check finalized data
final List<SignedBeaconBlock> expectedFinalizedBlocks = primaryChain.streamBlocksAndStates(0, 7).map(SignedBlockAndState::getBlock).collect(toList());
assertBlocksFinalized(expectedFinalizedBlocks);
assertGetLatestFinalizedRootAtSlotReturnsFinalizedBlocks(expectedFinalizedBlocks);
assertBlocksAvailableByRoot(expectedFinalizedBlocks);
assertFinalizedBlocksAvailableViaStream(1, 3, primaryChain.getBlockAtSlot(1), primaryChain.getBlockAtSlot(2), primaryChain.getBlockAtSlot(3));
switch(storageMode) {
case ARCHIVE:
// Finalized states should be available
final Map<Bytes32, BeaconState> expectedStates = primaryChain.streamBlocksAndStates(0, 7).collect(toMap(SignedBlockAndState::getRoot, SignedBlockAndState::getState));
assertFinalizedStatesAvailable(expectedStates);
break;
case PRUNE:
// Check pruned states
final List<UInt64> unavailableSlots = allBlocksAndStates.stream().map(SignedBlockAndState::getSlot).collect(toList());
assertStatesUnavailable(unavailableSlots);
break;
}
}
use of tech.pegasys.teku.core.ChainBuilder in project teku by ConsenSys.
the class RecentChainDataTest method updateHead_reorgEventWhenChainSwitchesToNewBlockAtLaterSlot.
@Test
public void updateHead_reorgEventWhenChainSwitchesToNewBlockAtLaterSlot() {
initPreGenesis();
final ChainBuilder chainBuilder = ChainBuilder.create(spec, BLSKeyGenerator.generateKeyPairs(16));
final SignedBlockAndState genesis = chainBuilder.generateGenesis();
recentChainData.initializeFromGenesis(genesis.getState(), UInt64.ZERO);
assertThat(storageSystem.chainHeadChannel().getReorgEvents()).isEmpty();
chainBuilder.generateBlockAtSlot(1);
// Set target slot at which to create duplicate blocks
// and generate block options to make each block unique
final List<BlockOptions> blockOptions = chainBuilder.streamValidAttestationsForBlockAtSlot(ONE).map(attestation -> BlockOptions.create().addAttestation(attestation)).limit(2).collect(toList());
final ChainBuilder forkBuilder = chainBuilder.fork();
final SignedBlockAndState latestBlockAndState = chainBuilder.generateBlockAtSlot(UInt64.valueOf(2), blockOptions.get(0));
forkBuilder.generateBlockAtSlot(UInt64.valueOf(2), blockOptions.get(1));
// Fork extends a slot further
final SignedBlockAndState latestForkBlockAndState = forkBuilder.generateBlockAtSlot(3);
importBlocksAndStates(recentChainData, chainBuilder, forkBuilder);
// Update to head block of original chain.
recentChainData.updateHead(latestBlockAndState.getRoot(), latestBlockAndState.getSlot());
assertThat(storageSystem.chainHeadChannel().getReorgEvents()).isEmpty();
// Switch to fork.
recentChainData.updateHead(latestForkBlockAndState.getRoot(), latestForkBlockAndState.getSlot());
// Check reorg event
assertThat(storageSystem.chainHeadChannel().getReorgEvents()).containsExactly(new ReorgEvent(latestForkBlockAndState.getRoot(), latestForkBlockAndState.getSlot(), latestForkBlockAndState.getStateRoot(), latestBlockAndState.getRoot(), latestBlockAndState.getStateRoot(), ONE));
}
use of tech.pegasys.teku.core.ChainBuilder in project teku by ConsenSys.
the class AbstractDatabaseTest method shouldPruneHotBlocksInCurrentTransactionFromChainThatIsInvalided.
@Test
public void shouldPruneHotBlocksInCurrentTransactionFromChainThatIsInvalided() {
final UInt64 commonAncestorSlot = UInt64.valueOf(5);
chainBuilder.generateBlocksUpToSlot(commonAncestorSlot);
final ChainBuilder forkA = chainBuilder;
final ChainBuilder forkB = chainBuilder.fork();
// Add base blocks
addBlocks(chainBuilder.streamBlocksAndStates().collect(toList()));
// Forks diverge - generate block options to make each block unique
final UInt64 divergingSlot = commonAncestorSlot.plus(1);
final List<BlockOptions> blockOptions = chainBuilder.streamValidAttestationsForBlockAtSlot(divergingSlot).map(attestation -> BlockOptions.create().addAttestation(attestation)).limit(2).collect(toList());
// Create several different blocks at the same slot
final SignedBlockAndState blockA = forkA.generateBlockAtSlot(divergingSlot, blockOptions.get(0));
final SignedBlockAndState blockB = forkB.generateBlockAtSlot(divergingSlot, blockOptions.get(1));
// Add diverging blocks sequentially
add(List.of(blockA));
add(List.of(blockB));
// Then build on both chains, into the next epoch
final SignedBlockAndState blockA2 = forkA.generateBlockAtSlot(spec.slotsPerEpoch(ZERO) * 2 + 2);
final SignedBlockAndState blockB2 = forkB.generateBlockAtSlot(spec.slotsPerEpoch(ZERO) * 2 + 2);
// Add blocks while finalizing blockA at the same time
StoreTransaction tx = recentChainData.startStoreTransaction();
tx.putBlockAndState(blockA2);
tx.putBlockAndState(blockB2);
justifyAndFinalizeEpoch(UInt64.ONE, blockA, tx);
assertThat(tx.commit()).isCompleted();
// Verify all fork B blocks were pruned
assertThatSafeFuture(store.retrieveBlock(blockB.getRoot())).isCompletedWithEmptyOptional();
assertThatSafeFuture(store.retrieveBlock(blockB2.getRoot())).isCompletedWithEmptyOptional();
// And fork A should be available.
assertThat(store.retrieveSignedBlock(blockA.getRoot())).isCompletedWithValue(Optional.of(blockA.getBlock()));
assertThat(store.retrieveSignedBlock(blockA2.getRoot())).isCompletedWithValue(Optional.of(blockA2.getBlock()));
}
Aggregations