Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2022-10-19 13:21:47 +11:00
152 changed files with 3788 additions and 3067 deletions

View File

@@ -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"),

View File

@@ -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;

View File

@@ -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();

View File

@@ -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
)