Skip to content

Commit 1bd8f31

Browse files
Clean up temporary state flags while running (#6422)
* Clean up temporary state flags while running * Add regression test * Simplify
1 parent 8cf686f commit 1bd8f31

File tree

3 files changed

+66
-3
lines changed

3 files changed

+66
-3
lines changed

beacon_node/beacon_chain/tests/store_tests.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2514,7 +2514,7 @@ async fn pruning_test(
25142514
}
25152515

25162516
#[tokio::test]
2517-
async fn garbage_collect_temp_states_from_failed_block() {
2517+
async fn garbage_collect_temp_states_from_failed_block_on_startup() {
25182518
let db_path = tempdir().unwrap();
25192519

25202520
// Wrap these functions to ensure the variables are dropped before we try to open another
@@ -2571,6 +2571,61 @@ async fn garbage_collect_temp_states_from_failed_block() {
25712571
assert_eq!(store.iter_temporary_state_roots().count(), 0);
25722572
}
25732573

2574+
#[tokio::test]
2575+
async fn garbage_collect_temp_states_from_failed_block_on_finalization() {
2576+
let db_path = tempdir().unwrap();
2577+
2578+
let store = get_store(&db_path);
2579+
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
2580+
2581+
let slots_per_epoch = E::slots_per_epoch();
2582+
2583+
let genesis_state = harness.get_current_state();
2584+
let block_slot = Slot::new(2 * slots_per_epoch);
2585+
let ((signed_block, _), state) = harness.make_block(genesis_state, block_slot).await;
2586+
2587+
let (mut block, _) = (*signed_block).clone().deconstruct();
2588+
2589+
// Mutate the block to make it invalid, and re-sign it.
2590+
*block.state_root_mut() = Hash256::repeat_byte(0xff);
2591+
let proposer_index = block.proposer_index() as usize;
2592+
let block = Arc::new(block.sign(
2593+
&harness.validator_keypairs[proposer_index].sk,
2594+
&state.fork(),
2595+
state.genesis_validators_root(),
2596+
&harness.spec,
2597+
));
2598+
2599+
// The block should be rejected, but should store a bunch of temporary states.
2600+
harness.set_current_slot(block_slot);
2601+
harness
2602+
.process_block_result((block, None))
2603+
.await
2604+
.unwrap_err();
2605+
2606+
assert_eq!(
2607+
store.iter_temporary_state_roots().count(),
2608+
block_slot.as_usize() - 1
2609+
);
2610+
2611+
// Finalize the chain without the block, which should result in pruning of all temporary states.
2612+
let blocks_required_to_finalize = 3 * slots_per_epoch;
2613+
harness.advance_slot();
2614+
harness
2615+
.extend_chain(
2616+
blocks_required_to_finalize as usize,
2617+
BlockStrategy::OnCanonicalHead,
2618+
AttestationStrategy::AllValidators,
2619+
)
2620+
.await;
2621+
2622+
// Check that the finalization migration ran.
2623+
assert_ne!(store.get_split_slot(), 0);
2624+
2625+
// Check that temporary states have been pruned.
2626+
assert_eq!(store.iter_temporary_state_roots().count(), 0);
2627+
}
2628+
25742629
#[tokio::test]
25752630
async fn weak_subjectivity_sync_easy() {
25762631
let num_initial_slots = E::slots_per_epoch() * 11;

beacon_node/store/src/garbage_collection.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ where
2121
.try_fold(vec![], |mut ops, state_root| {
2222
let state_root = state_root?;
2323
ops.push(StoreOp::DeleteState(state_root, None));
24-
ops.push(StoreOp::DeleteStateTemporaryFlag(state_root));
2524
Result::<_, Error>::Ok(ops)
2625
})?;
2726

2827
if !delete_ops.is_empty() {
2928
debug!(
3029
self.log,
3130
"Garbage collecting {} temporary states",
32-
delete_ops.len() / 2
31+
delete_ops.len()
3332
);
3433
self.do_atomically_with_block_and_blobs_cache(delete_ops)?;
3534
}

beacon_node/store/src/hot_cold_store.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,10 +1160,19 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
11601160
}
11611161

11621162
StoreOp::DeleteState(state_root, slot) => {
1163+
// Delete the hot state summary.
11631164
let state_summary_key =
11641165
get_key_for_col(DBColumn::BeaconStateSummary.into(), state_root.as_slice());
11651166
key_value_batch.push(KeyValueStoreOp::DeleteKey(state_summary_key));
11661167

1168+
// Delete the state temporary flag (if any). Temporary flags are commonly
1169+
// created by the state advance routine.
1170+
let state_temp_key = get_key_for_col(
1171+
DBColumn::BeaconStateTemporary.into(),
1172+
state_root.as_slice(),
1173+
);
1174+
key_value_batch.push(KeyValueStoreOp::DeleteKey(state_temp_key));
1175+
11671176
if slot.map_or(true, |slot| slot % E::slots_per_epoch() == 0) {
11681177
let state_key =
11691178
get_key_for_col(DBColumn::BeaconState.into(), state_root.as_slice());

0 commit comments

Comments
 (0)