Commit 172dd92
authored
fix(storage): protect head root from state pruning (lambdaclass#396)
## Summary
- Add the fork-choice head root to `prune_old_data`'s `protected_roots`
so the pruner never evicts the state currently in use by fork choice.
## Problem
`prune_old_data` only protected the latest finalized and justified block
roots:
```rust
let protected_roots = [self.latest_finalized().root, self.latest_justified().root];
```
`prune_old_states` then keeps the top `STATES_TO_KEEP = 3_000` entries
by **slot** (descending) and deletes the rest of the `States` table,
ignoring fork-choice membership.
When finalization stalls and a competing branch keeps producing blocks
on top of an unfinalized region, that branch's high-slot headers fill
the retention window even though it isn't the fork-choice head. The
actual head can fall outside the top 3000 by slot, and its state row is
deleted. The very next call into:
```rust
pub fn head_state(&self) -> State {
self.get_state(&self.head())
.expect("head state is always available")
}
```
panics, taking down the blockchain actor (the container stays up, P2P
keeps running, but every gossip message logs `err=Actor stopped`).
## Observed
In a stalled 8-node devnet, finalization froze around slot 2866 while a
minority fork from 2 surviving aggregators kept proposing one empty
block per slot, reaching slot ~15000+. On the next gossip block (slot
15010, proposer 2), 6 of 8 nodes pruned 2666 states and panicked
simultaneously at `store.rs:1314` with `head state is always available`.
Logs on every affected node:
```
2026-05-28T13:15:07.133 INFO Pruned old states and blocks pruned_states=2666 pruned_blocks=0
2026-05-28T13:15:07.802 thread 'tokio-rt-worker' panicked at crates/storage/src/store.rs:1314:14:
head state is always available
2026-05-28T13:15:07.802 ERROR spawned_concurrency::tasks::actor: Panic in message handler
```
## Fix
Add `self.head()` to `protected_roots`. The head is now retained
regardless of its slot position relative to other branches' tips.
## Test plan
- [ ] Existing `prune_old_states_*` tests still pass.
- [ ] Add regression test: head root at a low slot, retention window
otherwise filled by a higher-slot branch; assert head's state is
preserved after `prune_old_data`.
- [ ] Re-run the long-lived devnet scenario that stalled finalization
and verify nodes no longer panic when fork-choice head lags behind
competing branch tips.1 parent 873ddf8 commit 172dd92
1 file changed
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
774 | 774 | | |
775 | 775 | | |
776 | 776 | | |
777 | | - | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
| 781 | + | |
778 | 782 | | |
779 | 783 | | |
780 | 784 | | |
| |||
0 commit comments