Implement tree states & hierarchical state DB

This commit is contained in:
Michael Sproul
2023-06-19 10:14:47 +10:00
parent 2bb62b7f7d
commit 23db089a7a
193 changed files with 6093 additions and 5925 deletions

View File

@@ -1032,7 +1032,7 @@ async fn attestation_that_skips_epochs() {
let block_slot = harness
.chain
.store
.get_blinded_block(&block_root)
.get_blinded_block(&block_root, None)
.expect("should not error getting block")
.expect("should find attestation block")
.message()

View File

@@ -219,7 +219,7 @@ impl InvalidPayloadRig {
let mock_execution_layer = self.harness.mock_execution_layer.as_ref().unwrap();
let head = self.harness.chain.head_snapshot();
let state = head.beacon_state.clone_with_only_committee_caches();
let state = head.beacon_state.clone();
let slot = slot_override.unwrap_or(state.slot() + 1);
let (block, post_state) = self.harness.make_block(state, slot).await;
let block_root = block.canonical_root();
@@ -312,7 +312,7 @@ impl InvalidPayloadRig {
self.harness
.chain
.store
.get_full_block(&block_root)
.get_full_block(&block_root, None)
.unwrap()
.unwrap(),
block,
@@ -2013,7 +2013,7 @@ async fn weights_after_resetting_optimistic_status() {
.fork_choice_read_lock()
.get_block_weight(&head.head_block_root())
.unwrap(),
head.snapshot.beacon_state.validators()[0].effective_balance,
head.snapshot.beacon_state.validators().get(0).unwrap().effective_balance(),
"proposer boost should be removed from the head block and the vote of a single validator applied"
);

View File

@@ -99,8 +99,8 @@ async fn test_sync_committee_rewards() {
.get_validator_index(&validator.pubkey)
.unwrap()
.unwrap();
let pre_state_balance = parent_state.balances()[validator_index];
let post_state_balance = state.balances()[validator_index];
let pre_state_balance = *parent_state.balances().get(validator_index).unwrap();
let post_state_balance = *state.balances().get(validator_index).unwrap();
let sync_committee_reward = rewards.get(&(validator_index as u64)).unwrap_or(&0);
if validator_index == proposer_index {

View File

@@ -66,12 +66,21 @@ fn get_harness(
store: Arc<HotColdDB<E, LevelDB<E>, LevelDB<E>>>,
validator_count: usize,
) -> TestHarness {
// Most tests were written expecting instant migration on finalization.
let migrator_config = MigratorConfig::default().blocking().epochs_per_run(0);
let log = store.log.clone();
let harness = BeaconChainHarness::builder(MinimalEthSpec)
.logger(log)
.default_spec()
.keypairs(KEYPAIRS[0..validator_count].to_vec())
.logger(store.logger().clone())
.fresh_disk_store(store)
.mock_execution_layer()
.initial_mutator(Box::new(|builder: BeaconChainBuilder<_>| {
builder.store_migrator_config(migrator_config)
}))
.build();
harness.advance_slot();
harness
@@ -275,6 +284,9 @@ async fn split_slot_restore() {
)
.await;
// Uhmm. FIXME(sproul)
// tokio::time::sleep(std::time::Duration::from_secs(10)).await;
store.get_split_slot()
};
assert_ne!(split_slot, Slot::new(0));
@@ -329,22 +341,6 @@ async fn epoch_boundary_state_attestation_processing() {
let mut checked_pre_fin = false;
for (attestation, subnet_id) in late_attestations.into_iter().flatten() {
// load_epoch_boundary_state is idempotent!
let block_root = attestation.data.beacon_block_root;
let block = store
.get_blinded_block(&block_root)
.unwrap()
.expect("block exists");
let epoch_boundary_state = store
.load_epoch_boundary_state(&block.state_root())
.expect("no error")
.expect("epoch boundary state exists");
let ebs_of_ebs = store
.load_epoch_boundary_state(&epoch_boundary_state.canonical_root())
.expect("no error")
.expect("ebs of ebs exists");
assert_eq!(epoch_boundary_state, ebs_of_ebs);
// If the attestation is pre-finalization it should be rejected.
let finalized_epoch = harness.finalized_checkpoint().epoch;
@@ -443,50 +439,6 @@ async fn forwards_iter_block_and_state_roots_until() {
test_range(Slot::new(0), head_state.slot());
}
#[tokio::test]
async fn block_replay_with_inaccurate_state_roots() {
let num_blocks_produced = E::slots_per_epoch() * 3 + 31;
let db_path = tempdir().unwrap();
let store = get_store(&db_path);
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
let chain = &harness.chain;
harness
.extend_chain(
num_blocks_produced as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
)
.await;
// Slot must not be 0 mod 32 or else no blocks will be replayed.
let (mut head_state, head_root) = harness.get_current_state_and_root();
assert_ne!(head_state.slot() % 32, 0);
let mut fast_head_state = store
.get_inconsistent_state_for_attestation_verification_only(
&head_root,
Some(head_state.slot()),
)
.unwrap()
.unwrap();
assert_eq!(head_state.validators(), fast_head_state.validators());
head_state.build_all_committee_caches(&chain.spec).unwrap();
fast_head_state
.build_all_committee_caches(&chain.spec)
.unwrap();
assert_eq!(
head_state
.get_cached_active_validator_indices(RelativeEpoch::Current)
.unwrap(),
fast_head_state
.get_cached_active_validator_indices(RelativeEpoch::Current)
.unwrap()
);
}
#[tokio::test]
async fn block_replayer_hooks() {
let db_path = tempdir().unwrap();
@@ -517,7 +469,7 @@ async fn block_replayer_hooks() {
let mut post_block_slots = vec![];
let mut replay_state = BlockReplayer::<MinimalEthSpec>::new(state, &chain.spec)
.pre_slot_hook(Box::new(|state| {
.pre_slot_hook(Box::new(|_, state| {
pre_slots.push(state.slot());
Ok(())
}))
@@ -556,6 +508,8 @@ async fn block_replayer_hooks() {
assert_eq!(post_block_slots, block_slots);
// States match.
end_state.apply_pending_mutations().unwrap();
replay_state.apply_pending_mutations().unwrap();
end_state.drop_all_caches().unwrap();
replay_state.drop_all_caches().unwrap();
assert_eq!(end_state, replay_state);
@@ -622,7 +576,7 @@ async fn delete_blocks_and_states() {
);
let faulty_head_block = store
.get_blinded_block(&faulty_head.into())
.get_blinded_block(&faulty_head.into(), None)
.expect("no errors")
.expect("faulty head block exists");
@@ -664,7 +618,7 @@ async fn delete_blocks_and_states() {
break;
}
store.delete_block(&block_root).unwrap();
assert_eq!(store.get_blinded_block(&block_root).unwrap(), None);
assert_eq!(store.get_blinded_block(&block_root, None).unwrap(), None);
}
// Deleting frozen states should do nothing
@@ -908,7 +862,7 @@ fn get_state_for_block(harness: &TestHarness, block_root: Hash256) -> BeaconStat
let head_block = harness
.chain
.store
.get_blinded_block(&block_root)
.get_blinded_block(&block_root, None)
.unwrap()
.unwrap();
harness
@@ -948,9 +902,17 @@ fn check_shuffling_compatible(
|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}");
assert_eq!(
committee_cache,
state_cache.as_ref(),
"block at slot {slot}"
);
} else {
assert_ne!(committee_cache, state_cache, "block at slot {slot}");
assert_ne!(
committee_cache,
state_cache.as_ref(),
"block at slot {slot}"
);
}
Ok(())
},
@@ -980,9 +942,9 @@ fn check_shuffling_compatible(
|committee_cache, _| {
let state_cache = head_state.committee_cache(RelativeEpoch::Previous).unwrap();
if previous_epoch_shuffling_is_compatible {
assert_eq!(committee_cache, state_cache);
assert_eq!(committee_cache, state_cache.as_ref());
} else {
assert_ne!(committee_cache, state_cache);
assert_ne!(committee_cache, state_cache.as_ref());
}
Ok(())
},
@@ -1997,6 +1959,7 @@ async fn pruning_test(
check_no_blocks_exist(&harness, stray_blocks.values());
}
/* FIXME(sproul): adapt this test for new paradigm
#[tokio::test]
async fn garbage_collect_temp_states_from_failed_block() {
let db_path = tempdir().unwrap();
@@ -2051,6 +2014,7 @@ async fn garbage_collect_temp_states_from_failed_block() {
let store = get_store(&db_path);
assert_eq!(store.iter_temporary_state_roots().count(), 0);
}
*/
#[tokio::test]
async fn weak_subjectivity_sync() {
@@ -2078,7 +2042,7 @@ async fn weak_subjectivity_sync() {
let wss_block = harness
.chain
.store
.get_full_block(&wss_checkpoint.root)
.get_full_block(&wss_checkpoint.root, None)
.unwrap()
.unwrap();
let wss_state = full_store
@@ -2223,7 +2187,7 @@ async fn weak_subjectivity_sync() {
.unwrap()
.map(Result::unwrap)
{
let block = store.get_blinded_block(&block_root).unwrap().unwrap();
let block = store.get_blinded_block(&block_root, None).unwrap().unwrap();
assert_eq!(block.slot(), slot);
}
@@ -2243,7 +2207,7 @@ async fn weak_subjectivity_sync() {
assert_eq!(store.get_anchor_slot(), Some(wss_slot));
// Reconstruct states.
store.clone().reconstruct_historic_states().unwrap();
store.clone().reconstruct_historic_states(None).unwrap();
assert_eq!(store.get_anchor_slot(), None);
}
@@ -2634,8 +2598,8 @@ fn assert_chains_pretty_much_the_same<T: BeaconChainTypes>(a: &BeaconChain<T>, b
// Clone with committee caches only to prevent other caches from messing with the equality
// check.
assert_eq!(
a_head.beacon_state.clone_with_only_committee_caches(),
b_head.beacon_state.clone_with_only_committee_caches(),
a_head.beacon_state.clone(),
b_head.beacon_state.clone(),
"head states should be equal"
);
assert_eq!(a.heads(), b.heads(), "heads() should be equal");
@@ -2706,16 +2670,16 @@ fn check_split_slot(harness: &TestHarness, store: Arc<HotColdDB<E, LevelDB<E>, L
/// Check that all the states in a chain dump have the correct tree hash.
fn check_chain_dump(harness: &TestHarness, expected_len: u64) {
let chain_dump = harness.chain.chain_dump().unwrap();
let mut chain_dump = harness.chain.chain_dump().unwrap();
let split_slot = harness.chain.store.get_split_slot();
assert_eq!(chain_dump.len() as u64, expected_len);
for checkpoint in &chain_dump {
for checkpoint in &mut chain_dump {
// Check that the tree hash of the stored state is as expected
assert_eq!(
checkpoint.beacon_state_root(),
checkpoint.beacon_state.tree_hash_root(),
checkpoint.beacon_state.update_tree_hash_cache().unwrap(),
"tree hash of stored state is incorrect"
);