mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-20 22:38:34 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
@@ -346,6 +346,7 @@ async fn assert_invalid_signature(
|
||||
let process_res = harness
|
||||
.chain
|
||||
.process_block(
|
||||
snapshots[block_index].beacon_block.canonical_root(),
|
||||
snapshots[block_index].beacon_block.clone(),
|
||||
CountUnrealized::True,
|
||||
)
|
||||
@@ -403,12 +404,14 @@ async fn invalid_signature_gossip_block() {
|
||||
.await
|
||||
.into_block_error()
|
||||
.expect("should import all blocks prior to the one being tested");
|
||||
let signed_block = SignedBeaconBlock::from_block(block, junk_signature());
|
||||
assert!(
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(
|
||||
Arc::new(SignedBeaconBlock::from_block(block, junk_signature())),
|
||||
signed_block.canonical_root(),
|
||||
Arc::new(signed_block),
|
||||
CountUnrealized::True
|
||||
)
|
||||
.await,
|
||||
@@ -718,7 +721,11 @@ async fn block_gossip_verification() {
|
||||
|
||||
harness
|
||||
.chain
|
||||
.process_block(gossip_verified, CountUnrealized::True)
|
||||
.process_block(
|
||||
gossip_verified.block_root,
|
||||
gossip_verified,
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.expect("should import valid gossip verified block");
|
||||
}
|
||||
@@ -985,7 +992,11 @@ async fn verify_block_for_gossip_slashing_detection() {
|
||||
.unwrap();
|
||||
harness
|
||||
.chain
|
||||
.process_block(verified_block, CountUnrealized::True)
|
||||
.process_block(
|
||||
verified_block.block_root,
|
||||
verified_block,
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
unwrap_err(
|
||||
@@ -1020,7 +1031,11 @@ async fn verify_block_for_gossip_doppelganger_detection() {
|
||||
let attestations = verified_block.block.message().body().attestations().clone();
|
||||
harness
|
||||
.chain
|
||||
.process_block(verified_block, CountUnrealized::True)
|
||||
.process_block(
|
||||
verified_block.block_root,
|
||||
verified_block,
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -1124,6 +1139,7 @@ async fn add_base_block_to_altair_chain() {
|
||||
// Ensure that it would be impossible to apply this block to `per_block_processing`.
|
||||
{
|
||||
let mut state = state;
|
||||
let mut ctxt = ConsensusContext::new(base_block.slot());
|
||||
per_slot_processing(&mut state, None, &harness.chain.spec).unwrap();
|
||||
let mut ctxt = ConsensusContext::new(state.slot());
|
||||
assert!(matches!(
|
||||
@@ -1162,7 +1178,11 @@ async fn add_base_block_to_altair_chain() {
|
||||
assert!(matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(Arc::new(base_block.clone()), CountUnrealized::True)
|
||||
.process_block(
|
||||
base_block.canonical_root(),
|
||||
Arc::new(base_block.clone()),
|
||||
CountUnrealized::True
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
.expect("should error when processing base block"),
|
||||
@@ -1253,6 +1273,7 @@ async fn add_altair_block_to_base_chain() {
|
||||
// Ensure that it would be impossible to apply this block to `per_block_processing`.
|
||||
{
|
||||
let mut state = state;
|
||||
let mut ctxt = ConsensusContext::new(altair_block.slot());
|
||||
per_slot_processing(&mut state, None, &harness.chain.spec).unwrap();
|
||||
let mut ctxt = ConsensusContext::new(state.slot());
|
||||
assert!(matches!(
|
||||
@@ -1291,7 +1312,11 @@ async fn add_altair_block_to_base_chain() {
|
||||
assert!(matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(Arc::new(altair_block.clone()), CountUnrealized::True)
|
||||
.process_block(
|
||||
altair_block.canonical_root(),
|
||||
Arc::new(altair_block.clone()),
|
||||
CountUnrealized::True
|
||||
)
|
||||
.await
|
||||
.err()
|
||||
.expect("should error when processing altair block"),
|
||||
|
||||
@@ -281,7 +281,7 @@ impl InvalidPayloadRig {
|
||||
}
|
||||
let root = self
|
||||
.harness
|
||||
.process_block(slot, block.clone())
|
||||
.process_block(slot, block.canonical_root(), block.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -320,7 +320,11 @@ impl InvalidPayloadRig {
|
||||
set_new_payload(new_payload_response);
|
||||
set_forkchoice_updated(forkchoice_response);
|
||||
|
||||
match self.harness.process_block(slot, block).await {
|
||||
match self
|
||||
.harness
|
||||
.process_block(slot, block.canonical_root(), block)
|
||||
.await
|
||||
{
|
||||
Err(error) if evaluate_error(&error) => (),
|
||||
Err(other) => {
|
||||
panic!("evaluate_error returned false with {:?}", other)
|
||||
@@ -685,7 +689,11 @@ async fn invalidates_all_descendants() {
|
||||
let fork_block_root = rig
|
||||
.harness
|
||||
.chain
|
||||
.process_block(Arc::new(fork_block), CountUnrealized::True)
|
||||
.process_block(
|
||||
fork_block.canonical_root(),
|
||||
Arc::new(fork_block),
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
rig.recompute_head().await;
|
||||
@@ -777,7 +785,11 @@ async fn switches_heads() {
|
||||
let fork_block_root = rig
|
||||
.harness
|
||||
.chain
|
||||
.process_block(Arc::new(fork_block), CountUnrealized::True)
|
||||
.process_block(
|
||||
fork_block.canonical_root(),
|
||||
Arc::new(fork_block),
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
rig.recompute_head().await;
|
||||
@@ -1023,7 +1035,7 @@ async fn invalid_parent() {
|
||||
|
||||
// Ensure the block built atop an invalid payload is invalid for import.
|
||||
assert!(matches!(
|
||||
rig.harness.chain.process_block(block.clone(), CountUnrealized::True).await,
|
||||
rig.harness.chain.process_block(block.canonical_root(), block.clone(), CountUnrealized::True).await,
|
||||
Err(BlockError::ParentExecutionPayloadInvalid { parent_root: invalid_root })
|
||||
if invalid_root == parent_root
|
||||
));
|
||||
@@ -1305,7 +1317,7 @@ async fn build_optimistic_chain(
|
||||
for block in blocks {
|
||||
rig.harness
|
||||
.chain
|
||||
.process_block(block, CountUnrealized::True)
|
||||
.process_block(block.canonical_root(), block, CountUnrealized::True)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1863,7 +1875,11 @@ async fn recover_from_invalid_head_by_importing_blocks() {
|
||||
// Import the fork block, it should become the head.
|
||||
rig.harness
|
||||
.chain
|
||||
.process_block(fork_block.clone(), CountUnrealized::True)
|
||||
.process_block(
|
||||
fork_block.canonical_root(),
|
||||
fork_block.clone(),
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
rig.recompute_head().await;
|
||||
|
||||
@@ -26,6 +26,8 @@ use store::{
|
||||
HotColdDB, LevelDB, StoreConfig,
|
||||
};
|
||||
use tempfile::{tempdir, TempDir};
|
||||
use tokio::time::sleep;
|
||||
use tree_hash::TreeHash;
|
||||
use types::test_utils::{SeedableRng, XorShiftRng};
|
||||
use types::*;
|
||||
|
||||
@@ -751,7 +753,6 @@ async fn shuffling_compatible_linear_chain() {
|
||||
let store = get_store(&db_path);
|
||||
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
|
||||
|
||||
// Skip the block at the end of the first epoch.
|
||||
let head_block_root = harness
|
||||
.extend_chain(
|
||||
4 * E::slots_per_epoch() as usize,
|
||||
@@ -764,10 +765,6 @@ async fn shuffling_compatible_linear_chain() {
|
||||
&harness,
|
||||
&get_state_for_block(&harness, head_block_root),
|
||||
head_block_root,
|
||||
true,
|
||||
true,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -799,10 +796,6 @@ async fn shuffling_compatible_missing_pivot_block() {
|
||||
&harness,
|
||||
&get_state_for_block(&harness, head_block_root),
|
||||
head_block_root,
|
||||
true,
|
||||
true,
|
||||
Some(E::slots_per_epoch() - 2),
|
||||
Some(E::slots_per_epoch() - 2),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -820,10 +813,10 @@ async fn shuffling_compatible_simple_fork() {
|
||||
let head1_state = get_state_for_block(&harness, head1);
|
||||
let head2_state = get_state_for_block(&harness, head2);
|
||||
|
||||
check_shuffling_compatible(&harness, &head1_state, head1, true, true, None, None);
|
||||
check_shuffling_compatible(&harness, &head1_state, head2, false, false, None, None);
|
||||
check_shuffling_compatible(&harness, &head2_state, head1, false, false, None, None);
|
||||
check_shuffling_compatible(&harness, &head2_state, head2, true, true, None, None);
|
||||
check_shuffling_compatible(&harness, &head1_state, head1);
|
||||
check_shuffling_compatible(&harness, &head1_state, head2);
|
||||
check_shuffling_compatible(&harness, &head2_state, head1);
|
||||
check_shuffling_compatible(&harness, &head2_state, head2);
|
||||
|
||||
drop(db_path);
|
||||
}
|
||||
@@ -842,21 +835,10 @@ async fn shuffling_compatible_short_fork() {
|
||||
let head1_state = get_state_for_block(&harness, head1);
|
||||
let head2_state = get_state_for_block(&harness, head2);
|
||||
|
||||
check_shuffling_compatible(&harness, &head1_state, head1, true, true, None, None);
|
||||
check_shuffling_compatible(&harness, &head1_state, head2, false, true, None, None);
|
||||
// NOTE: don't check this case, as block 14 from the first chain appears valid on the second
|
||||
// chain due to it matching the second chain's block 15.
|
||||
// check_shuffling_compatible(&harness, &head2_state, head1, false, true, None, None);
|
||||
check_shuffling_compatible(
|
||||
&harness,
|
||||
&head2_state,
|
||||
head2,
|
||||
true,
|
||||
true,
|
||||
// Required because of the skipped slot.
|
||||
Some(2 * E::slots_per_epoch() - 2),
|
||||
None,
|
||||
);
|
||||
check_shuffling_compatible(&harness, &head1_state, head1);
|
||||
check_shuffling_compatible(&harness, &head1_state, head2);
|
||||
check_shuffling_compatible(&harness, &head2_state, head1);
|
||||
check_shuffling_compatible(&harness, &head2_state, head2);
|
||||
|
||||
drop(db_path);
|
||||
}
|
||||
@@ -880,54 +862,82 @@ fn check_shuffling_compatible(
|
||||
harness: &TestHarness,
|
||||
head_state: &BeaconState<E>,
|
||||
head_block_root: Hash256,
|
||||
current_epoch_valid: bool,
|
||||
previous_epoch_valid: bool,
|
||||
current_epoch_cutoff_slot: Option<u64>,
|
||||
previous_epoch_cutoff_slot: Option<u64>,
|
||||
) {
|
||||
let shuffling_lookahead = harness.chain.spec.min_seed_lookahead.as_u64() + 1;
|
||||
let current_pivot_slot =
|
||||
(head_state.current_epoch() - shuffling_lookahead).end_slot(E::slots_per_epoch());
|
||||
let previous_pivot_slot =
|
||||
(head_state.previous_epoch() - shuffling_lookahead).end_slot(E::slots_per_epoch());
|
||||
|
||||
for maybe_tuple in harness
|
||||
.chain
|
||||
.rev_iter_block_roots_from(head_block_root)
|
||||
.unwrap()
|
||||
{
|
||||
let (block_root, slot) = maybe_tuple.unwrap();
|
||||
// Shuffling is compatible targeting the current epoch,
|
||||
// if slot is greater than or equal to the current epoch pivot block.
|
||||
assert_eq!(
|
||||
harness.chain.shuffling_is_compatible(
|
||||
&block_root,
|
||||
|
||||
// Would an attestation to `block_root` at the current epoch be compatible with the head
|
||||
// state's shuffling?
|
||||
let current_epoch_shuffling_is_compatible = harness.chain.shuffling_is_compatible(
|
||||
&block_root,
|
||||
head_state.current_epoch(),
|
||||
&head_state,
|
||||
);
|
||||
|
||||
// Check for consistency with the more expensive shuffling lookup.
|
||||
harness
|
||||
.chain
|
||||
.with_committee_cache(
|
||||
block_root,
|
||||
head_state.current_epoch(),
|
||||
&head_state
|
||||
),
|
||||
current_epoch_valid
|
||||
&& slot >= current_epoch_cutoff_slot.unwrap_or(current_pivot_slot.as_u64())
|
||||
);
|
||||
|committee_cache, _| {
|
||||
let state_cache = head_state.committee_cache(RelativeEpoch::Current).unwrap();
|
||||
if current_epoch_shuffling_is_compatible {
|
||||
assert_eq!(committee_cache, state_cache, "block at slot {slot}");
|
||||
} else {
|
||||
assert_ne!(committee_cache, state_cache, "block at slot {slot}");
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
// If the lookup fails then the shuffling must be invalid in some way, e.g. the
|
||||
// block with `block_root` is from a later epoch than `previous_epoch`.
|
||||
assert!(
|
||||
!current_epoch_shuffling_is_compatible,
|
||||
"block at slot {slot} has compatible shuffling at epoch {} \
|
||||
but should be incompatible due to error: {e:?}",
|
||||
head_state.current_epoch()
|
||||
);
|
||||
});
|
||||
|
||||
// Similarly for the previous epoch
|
||||
assert_eq!(
|
||||
harness.chain.shuffling_is_compatible(
|
||||
&block_root,
|
||||
let previous_epoch_shuffling_is_compatible = harness.chain.shuffling_is_compatible(
|
||||
&block_root,
|
||||
head_state.previous_epoch(),
|
||||
&head_state,
|
||||
);
|
||||
harness
|
||||
.chain
|
||||
.with_committee_cache(
|
||||
block_root,
|
||||
head_state.previous_epoch(),
|
||||
&head_state
|
||||
),
|
||||
previous_epoch_valid
|
||||
&& slot >= previous_epoch_cutoff_slot.unwrap_or(previous_pivot_slot.as_u64())
|
||||
);
|
||||
// Targeting the next epoch should always return false
|
||||
assert_eq!(
|
||||
harness.chain.shuffling_is_compatible(
|
||||
&block_root,
|
||||
head_state.current_epoch() + 1,
|
||||
&head_state
|
||||
),
|
||||
false
|
||||
);
|
||||
// Targeting two epochs before the current epoch should also always return false
|
||||
|committee_cache, _| {
|
||||
let state_cache = head_state.committee_cache(RelativeEpoch::Previous).unwrap();
|
||||
if previous_epoch_shuffling_is_compatible {
|
||||
assert_eq!(committee_cache, state_cache);
|
||||
} else {
|
||||
assert_ne!(committee_cache, state_cache);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
// If the lookup fails then the shuffling must be invalid in some way, e.g. the
|
||||
// block with `block_root` is from a later epoch than `previous_epoch`.
|
||||
assert!(
|
||||
!previous_epoch_shuffling_is_compatible,
|
||||
"block at slot {slot} has compatible shuffling at epoch {} \
|
||||
but should be incompatible due to error: {e:?}",
|
||||
head_state.previous_epoch()
|
||||
);
|
||||
});
|
||||
|
||||
// Targeting two epochs before the current epoch should always return false
|
||||
if head_state.current_epoch() >= 2 {
|
||||
assert_eq!(
|
||||
harness.chain.shuffling_is_compatible(
|
||||
@@ -1927,45 +1937,55 @@ async fn pruning_test(
|
||||
}
|
||||
|
||||
/* FIXME(sproul): adapt this test for new paradigm
|
||||
#[test]
|
||||
fn delete_states_from_failed_block() {
|
||||
#[tokio::test]
|
||||
async fn garbage_collect_temp_states_from_failed_block() {
|
||||
let db_path = tempdir().unwrap();
|
||||
let store = get_store(&db_path);
|
||||
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
|
||||
|
||||
// Use a `block_on_dangerous` rather than an async test to stop spawned processes from holding
|
||||
// a reference to the store.
|
||||
harness.chain.task_executor.clone().block_on_dangerous(
|
||||
async move {
|
||||
let slots_per_epoch = E::slots_per_epoch();
|
||||
// Wrap these functions to ensure the variables are dropped before we try to open another
|
||||
// instance of the store.
|
||||
let mut store = {
|
||||
let store = get_store(&db_path);
|
||||
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
|
||||
|
||||
let genesis_state = harness.get_current_state();
|
||||
let block_slot = Slot::new(2 * slots_per_epoch);
|
||||
let (signed_block, state) = harness.make_block(genesis_state, block_slot).await;
|
||||
let slots_per_epoch = E::slots_per_epoch();
|
||||
|
||||
let (mut block, _) = signed_block.deconstruct();
|
||||
let genesis_state = harness.get_current_state();
|
||||
let block_slot = Slot::new(2 * slots_per_epoch);
|
||||
let (signed_block, state) = harness.make_block(genesis_state, block_slot).await;
|
||||
|
||||
// Mutate the block to make it invalid, and re-sign it.
|
||||
*block.state_root_mut() = Hash256::repeat_byte(0xff);
|
||||
let proposer_index = block.proposer_index() as usize;
|
||||
let block = block.sign(
|
||||
&harness.validator_keypairs[proposer_index].sk,
|
||||
&state.fork(),
|
||||
state.genesis_validators_root(),
|
||||
&harness.spec,
|
||||
);
|
||||
let (mut block, _) = signed_block.deconstruct();
|
||||
|
||||
// The block should be rejected, but should store a bunch of temporary states.
|
||||
harness.set_current_slot(block_slot);
|
||||
harness.process_block_result(block).await.unwrap_err();
|
||||
// Mutate the block to make it invalid, and re-sign it.
|
||||
*block.state_root_mut() = Hash256::repeat_byte(0xff);
|
||||
let proposer_index = block.proposer_index() as usize;
|
||||
let block = block.sign(
|
||||
&harness.validator_keypairs[proposer_index].sk,
|
||||
&state.fork(),
|
||||
state.genesis_validators_root(),
|
||||
&harness.spec,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
store.iter_temporary_state_roots().count(),
|
||||
block_slot.as_usize() - 1
|
||||
);
|
||||
},
|
||||
"test",
|
||||
);
|
||||
// The block should be rejected, but should store a bunch of temporary states.
|
||||
harness.set_current_slot(block_slot);
|
||||
harness.process_block_result(block).await.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
store.iter_temporary_state_roots().count(),
|
||||
block_slot.as_usize() - 1
|
||||
);
|
||||
store
|
||||
};
|
||||
|
||||
// Wait until all the references to the store have been dropped, this helps ensure we can
|
||||
// re-open the store later.
|
||||
loop {
|
||||
store = if let Err(store_arc) = Arc::try_unwrap(store) {
|
||||
sleep(Duration::from_millis(500)).await;
|
||||
store_arc
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// On startup, the store should garbage collect all the temporary states.
|
||||
let store = get_store(&db_path);
|
||||
@@ -2068,7 +2088,11 @@ async fn weak_subjectivity_sync() {
|
||||
|
||||
beacon_chain.slot_clock.set_slot(slot.as_u64());
|
||||
beacon_chain
|
||||
.process_block(Arc::new(full_block), CountUnrealized::True)
|
||||
.process_block(
|
||||
full_block.canonical_root(),
|
||||
Arc::new(full_block),
|
||||
CountUnrealized::True,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
beacon_chain.recompute_head_at_current_slot().await;
|
||||
@@ -2325,8 +2349,14 @@ async fn revert_minority_fork_on_resume() {
|
||||
|
||||
let (block, new_state) = harness1.make_block(state, slot).await;
|
||||
|
||||
harness1.process_block(slot, block.clone()).await.unwrap();
|
||||
harness2.process_block(slot, block.clone()).await.unwrap();
|
||||
harness1
|
||||
.process_block(slot, block.canonical_root(), block.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
harness2
|
||||
.process_block(slot, block.canonical_root(), block.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
state = new_state;
|
||||
block_root = block.canonical_root();
|
||||
@@ -2359,12 +2389,18 @@ async fn revert_minority_fork_on_resume() {
|
||||
|
||||
// Minority chain block (no attesters).
|
||||
let (block1, new_state1) = harness1.make_block(state1, slot).await;
|
||||
harness1.process_block(slot, block1).await.unwrap();
|
||||
harness1
|
||||
.process_block(slot, block1.canonical_root(), block1)
|
||||
.await
|
||||
.unwrap();
|
||||
state1 = new_state1;
|
||||
|
||||
// Majority chain block (all attesters).
|
||||
let (block2, new_state2) = harness2.make_block(state2, slot).await;
|
||||
harness2.process_block(slot, block2.clone()).await.unwrap();
|
||||
harness2
|
||||
.process_block(slot, block2.canonical_root(), block2.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
state2 = new_state2;
|
||||
block_root = block2.canonical_root();
|
||||
|
||||
@@ -685,6 +685,7 @@ async fn run_skip_slot_test(skip_slots: u64) {
|
||||
harness_b
|
||||
.chain
|
||||
.process_block(
|
||||
harness_a.chain.head_snapshot().beacon_block_root,
|
||||
harness_a.chain.head_snapshot().beacon_block.clone(),
|
||||
CountUnrealized::True
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user