Fix bug in database pruning (#1564)

## Issue Addressed

Closes #1488

## Proposed Changes

* Prevent the pruning algorithm from over-eagerly deleting states at skipped slots when they are shared with the canonical chain.
* Add `debug` logging to the pruning algorithm so we have so better chance of debugging future issues from logs.
* Modify the handling of the "finalized state" in the beacon chain, so that it's always the state at the first slot of the finalized epoch (previously it was the state at the finalized block). This gives database pruning a clearer and cleaner view of things, and will marginally impact the pruning of the op pool, observed proposers, etc (in ways that are safe as far as I can tell).
* Remove duplicated `RevertedFinalizedEpoch` check from `after_finalization`
* Delete useless and unused `max_finality_distance`
* Add tests that exercise pruning with shared states at skip slots
* Delete unnecessary `block_strategy` argument from `add_blocks` and friends in the test harness (will likely conflict with #1380 slightly, sorry @adaszko -- but we can fix that)
* Bonus: add a `BeaconChain::with_head` method. I didn't end up needing it, but it turned out quite nice, so I figured we could keep it?

## Additional Info

Any users who have experienced pruning errors on Medalla will need to resync after upgrading to a release including this change. This should end unbounded `chain_db` growth! 🎉
This commit is contained in:
Michael Sproul
2020-08-26 00:01:06 +00:00
parent 175471a64b
commit 4763f03dcc
7 changed files with 563 additions and 273 deletions

View File

@@ -298,7 +298,7 @@ where
let mut head_block_root = None;
loop {
let (block, new_state) = self.build_block(state.clone(), slot, block_strategy);
let (block, new_state) = self.build_block(state.clone(), slot);
if !predicate(&block, &new_state) {
break;
@@ -339,7 +339,7 @@ where
let slot = self.chain.slot().unwrap();
self.build_block(state, slot, BlockStrategy::OnCanonicalHead)
self.build_block(state, slot)
}
/// A simple method to produce and process all attestation at the current slot. Always uses
@@ -368,12 +368,9 @@ where
self.chain.head().unwrap().beacon_state
}
/// Adds a single block (synchronously) onto either the canonical chain (block_strategy ==
/// OnCanonicalHead) or a fork (block_strategy == ForkCanonicalChainAt).
pub fn add_block(
&self,
state: &BeaconState<E>,
block_strategy: BlockStrategy,
slot: Slot,
validators: &[usize],
) -> (SignedBeaconBlockHash, BeaconState<E>) {
@@ -381,7 +378,7 @@ where
self.advance_slot();
}
let (block, new_state) = self.build_block(state.clone(), slot, block_strategy);
let (block, new_state) = self.build_block(state.clone(), slot);
let block_root = self
.chain
@@ -395,15 +392,14 @@ where
(block_root.into(), new_state)
}
#[allow(clippy::type_complexity)]
/// `add_block()` repeated `num_blocks` times.
#[allow(clippy::type_complexity)]
pub fn add_blocks(
&self,
mut state: BeaconState<E>,
mut slot: Slot,
num_blocks: usize,
attesting_validators: &[usize],
block_strategy: BlockStrategy,
) -> (
HashMap<Slot, SignedBeaconBlockHash>,
HashMap<Slot, BeaconStateHash>,
@@ -414,8 +410,7 @@ where
let mut blocks: HashMap<Slot, SignedBeaconBlockHash> = HashMap::with_capacity(num_blocks);
let mut states: HashMap<Slot, BeaconStateHash> = HashMap::with_capacity(num_blocks);
for _ in 0..num_blocks {
let (new_root_hash, new_state) =
self.add_block(&state, block_strategy, slot, attesting_validators);
let (new_root_hash, new_state) = self.add_block(&state, slot, attesting_validators);
blocks.insert(slot, new_root_hash);
states.insert(slot, new_state.tree_hash_root().into());
state = new_state;
@@ -426,7 +421,6 @@ where
}
#[allow(clippy::type_complexity)]
/// A wrapper on `add_blocks()` to avoid passing enums explicitly.
pub fn add_canonical_chain_blocks(
&self,
state: BeaconState<E>,
@@ -440,18 +434,10 @@ where
SignedBeaconBlockHash,
BeaconState<E>,
) {
let block_strategy = BlockStrategy::OnCanonicalHead;
self.add_blocks(
state,
slot,
num_blocks,
attesting_validators,
block_strategy,
)
self.add_blocks(state, slot, num_blocks, attesting_validators)
}
#[allow(clippy::type_complexity)]
/// A wrapper on `add_blocks()` to avoid passing enums explicitly.
pub fn add_stray_blocks(
&self,
state: BeaconState<E>,
@@ -465,17 +451,7 @@ where
SignedBeaconBlockHash,
BeaconState<E>,
) {
let block_strategy = BlockStrategy::ForkCanonicalChainAt {
previous_slot: slot,
first_slot: slot + 2,
};
self.add_blocks(
state,
slot + 2,
num_blocks,
attesting_validators,
block_strategy,
)
self.add_blocks(state, slot + 2, num_blocks, attesting_validators)
}
/// Returns a newly created block, signed by the proposer for the given slot.
@@ -483,8 +459,8 @@ where
&self,
mut state: BeaconState<E>,
slot: Slot,
block_strategy: BlockStrategy,
) -> (SignedBeaconBlock<E>, BeaconState<E>) {
assert_ne!(slot, 0);
if slot < state.slot {
panic!("produce slot cannot be prior to the state slot");
}
@@ -498,15 +474,9 @@ where
.build_all_caches(&self.spec)
.expect("should build caches");
let proposer_index = match block_strategy {
BlockStrategy::OnCanonicalHead => self
.chain
.block_proposer(slot)
.expect("should get block proposer from chain"),
_ => state
.get_beacon_proposer_index(slot, &self.spec)
.expect("should get block proposer from state"),
};
let proposer_index = state
.get_beacon_proposer_index(slot, &self.spec)
.expect("should get block proposer from state");
let sk = &self.keypairs[proposer_index].sk;
let fork = &state.fork;