mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-16 12:28:24 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
// #![cfg(not(debug_assertions))]
|
||||
#![cfg(not(debug_assertions))]
|
||||
|
||||
use beacon_chain::block_verification_types::{AsBlock, ExecutedBlock, RpcBlock};
|
||||
use beacon_chain::{
|
||||
|
||||
@@ -1818,81 +1818,94 @@ struct InvalidHeadSetup {
|
||||
}
|
||||
|
||||
impl InvalidHeadSetup {
|
||||
/// This function aims to produce two things:
|
||||
///
|
||||
/// 1. A chain where the only viable head block has an invalid execution payload.
|
||||
/// 2. A block (`fork_block`) which will become the head of the chain when
|
||||
/// it is imported.
|
||||
async fn new() -> InvalidHeadSetup {
|
||||
let slots_per_epoch = E::slots_per_epoch();
|
||||
let mut rig = InvalidPayloadRig::new().enable_attestations();
|
||||
rig.move_to_terminal_block();
|
||||
rig.import_block(Payload::Valid).await; // Import a valid transition block.
|
||||
|
||||
// Import blocks until the first time the chain finalizes.
|
||||
// Import blocks until the first time the chain finalizes. This avoids
|
||||
// some edge-cases around genesis.
|
||||
while rig.cached_head().finalized_checkpoint().epoch == 0 {
|
||||
rig.import_block(Payload::Syncing).await;
|
||||
}
|
||||
|
||||
let slots_per_epoch = E::slots_per_epoch();
|
||||
let start_slot = rig.cached_head().head_slot() + 1;
|
||||
let mut opt_fork_block = None;
|
||||
// Define a helper function.
|
||||
let chain = rig.harness.chain.clone();
|
||||
let get_unrealized_justified_epoch = move || {
|
||||
chain
|
||||
.canonical_head
|
||||
.fork_choice_read_lock()
|
||||
.unrealized_justified_checkpoint()
|
||||
.epoch
|
||||
};
|
||||
|
||||
assert_eq!(start_slot % slots_per_epoch, 1);
|
||||
for i in 0..slots_per_epoch - 1 {
|
||||
let slot = start_slot + i;
|
||||
let slot_offset = slot.as_u64() % slots_per_epoch;
|
||||
|
||||
rig.harness.set_current_slot(slot);
|
||||
|
||||
if slot_offset == slots_per_epoch - 1 {
|
||||
// Optimistic head block right before epoch boundary.
|
||||
let is_valid = Payload::Syncing;
|
||||
rig.import_block_parametric(is_valid, is_valid, Some(slot), |error| {
|
||||
matches!(
|
||||
error,
|
||||
BlockError::ExecutionPayloadError(
|
||||
ExecutionPayloadError::RejectedByExecutionEngine { .. }
|
||||
)
|
||||
)
|
||||
})
|
||||
.await;
|
||||
} else if 3 * slot_offset < 2 * slots_per_epoch {
|
||||
// Valid block in previous epoch.
|
||||
rig.import_block(Payload::Valid).await;
|
||||
} else if slot_offset == slots_per_epoch - 2 {
|
||||
// Fork block one slot prior to invalid head, not applied immediately.
|
||||
let parent_state = rig
|
||||
.harness
|
||||
.chain
|
||||
.state_at_slot(slot - 1, StateSkipConfig::WithStateRoots)
|
||||
.unwrap();
|
||||
let (fork_block_tuple, _) = rig.harness.make_block(parent_state, slot).await;
|
||||
opt_fork_block = Some(fork_block_tuple.0);
|
||||
} else {
|
||||
// Skipped slot.
|
||||
};
|
||||
// Import more blocks until there is a new and higher unrealized
|
||||
// justified checkpoint.
|
||||
//
|
||||
// The result will be a single chain where the head block has a higher
|
||||
// unrealized justified checkpoint than all other blocks in the chain.
|
||||
let initial_unrealized_justified = get_unrealized_justified_epoch();
|
||||
while get_unrealized_justified_epoch() == initial_unrealized_justified {
|
||||
rig.import_block(Payload::Syncing).await;
|
||||
}
|
||||
|
||||
let invalid_head = rig.cached_head();
|
||||
assert_eq!(
|
||||
invalid_head.head_slot() % slots_per_epoch,
|
||||
slots_per_epoch - 1
|
||||
);
|
||||
// Create a forked block that competes with the head block. Both the
|
||||
// head block and this fork block will share the same parent.
|
||||
//
|
||||
// The fork block and head block will both have an unrealized justified
|
||||
// checkpoint at epoch `N` whilst their parent is at `N - 1`.
|
||||
let head_slot = rig.cached_head().head_slot();
|
||||
let parent_slot = head_slot - 1;
|
||||
let fork_block_slot = head_slot + 1;
|
||||
let parent_state = rig
|
||||
.harness
|
||||
.chain
|
||||
.state_at_slot(parent_slot, StateSkipConfig::WithStateRoots)
|
||||
.unwrap();
|
||||
let (fork_block_tuple, _) = rig.harness.make_block(parent_state, fork_block_slot).await;
|
||||
let fork_block = fork_block_tuple.0;
|
||||
|
||||
// Advance clock to new epoch to realize the justification of soon-to-be-invalid head block.
|
||||
rig.harness.set_current_slot(invalid_head.head_slot() + 1);
|
||||
let invalid_head = rig.cached_head();
|
||||
|
||||
// Advance the chain forward two epochs past the current head block.
|
||||
//
|
||||
// This ensures that `voting_source.epoch + 2 >= current_epoch` is
|
||||
// `false` in the `node_is_viable_for_head` function. In effect, this
|
||||
// ensures that no other block but the current head block is viable as a
|
||||
// head block.
|
||||
let invalid_head_epoch = invalid_head.head_slot().epoch(slots_per_epoch);
|
||||
let new_wall_clock_epoch = invalid_head_epoch + 2;
|
||||
rig.harness
|
||||
.set_current_slot(new_wall_clock_epoch.start_slot(slots_per_epoch));
|
||||
|
||||
// Invalidate the head block.
|
||||
rig.invalidate_manually(invalid_head.head_block_root())
|
||||
.await;
|
||||
|
||||
// Since our setup ensures that there is only a single, invalid block
|
||||
// that's viable for head (according to FFG filtering), setting the
|
||||
// head block as invalid should not result in another head being chosen.
|
||||
// Rather, it should fail to run fork choice and leave the invalid block as
|
||||
// the head.
|
||||
assert!(rig
|
||||
.canonical_head()
|
||||
.head_execution_status()
|
||||
.unwrap()
|
||||
.is_invalid());
|
||||
|
||||
// Finding a new head should fail since the only possible head is not valid.
|
||||
// Ensure that we're getting the correct error when trying to find a new
|
||||
// head.
|
||||
rig.assert_get_head_error_contains("InvalidBestNode");
|
||||
|
||||
Self {
|
||||
rig,
|
||||
fork_block: opt_fork_block.unwrap(),
|
||||
fork_block,
|
||||
invalid_head,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2154,6 +2154,7 @@ async fn weak_subjectivity_sync_test(slots: Vec<Slot>, checkpoint_slot: Slot) {
|
||||
.get_full_block(&wss_block_root, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let wss_blobs_opt = harness.chain.store.get_blobs(&wss_block_root).unwrap();
|
||||
let wss_state = full_store
|
||||
.get_state(&wss_state_root, Some(checkpoint_slot))
|
||||
.unwrap()
|
||||
@@ -2196,7 +2197,12 @@ async fn weak_subjectivity_sync_test(slots: Vec<Slot>, checkpoint_slot: Slot) {
|
||||
.custom_spec(test_spec::<E>())
|
||||
.task_executor(harness.chain.task_executor.clone())
|
||||
.logger(log.clone())
|
||||
.weak_subjectivity_state(wss_state, wss_block.clone(), genesis_state)
|
||||
.weak_subjectivity_state(
|
||||
wss_state,
|
||||
wss_block.clone(),
|
||||
wss_blobs_opt.clone(),
|
||||
genesis_state,
|
||||
)
|
||||
.unwrap()
|
||||
.store_migrator_config(MigratorConfig::default().blocking())
|
||||
.dummy_eth1_backend()
|
||||
@@ -2214,6 +2220,17 @@ async fn weak_subjectivity_sync_test(slots: Vec<Slot>, checkpoint_slot: Slot) {
|
||||
.expect("should build");
|
||||
|
||||
let beacon_chain = Arc::new(beacon_chain);
|
||||
let wss_block_root = wss_block.canonical_root();
|
||||
let store_wss_block = harness
|
||||
.chain
|
||||
.get_block(&wss_block_root)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let store_wss_blobs_opt = beacon_chain.store.get_blobs(&wss_block_root).unwrap();
|
||||
|
||||
assert_eq!(store_wss_block, wss_block);
|
||||
assert_eq!(store_wss_blobs_opt, wss_blobs_opt);
|
||||
|
||||
// Apply blocks forward to reach head.
|
||||
let chain_dump = harness.chain.chain_dump().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user