Fix bugs in proposer calculation post-Fulu (#8101)

As identified by a researcher during the Fusaka security competition, we were computing the proposer index incorrectly in some places by computing without lookahead.


  - [x] Add "low level" checks to computation functions in `consensus/types` to ensure they error cleanly
- [x] Re-work the determination of proposer shuffling decision roots, which are now fork aware.
- [x] Re-work and simplify the beacon proposer cache to be fork-aware.
- [x] Optimise `with_proposer_cache` to use `OnceCell`.
- [x] All tests passing.
- [x] Resolve all remaining `FIXME(sproul)`s.
- [x] Unit tests for `ProtoBlock::proposer_shuffling_root_for_child_block`.
- [x] End-to-end regression test.
- [x] Test on pre-Fulu network.
- [x] Test on post-Fulu network.


Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Michael Sproul
2025-09-27 00:44:50 +10:00
committed by GitHub
parent 20c6ce4553
commit c754234b2c
19 changed files with 765 additions and 351 deletions

View File

@@ -1,9 +1,7 @@
use crate::common::update_progressive_balances_cache::initialize_progressive_balances_cache;
use crate::epoch_cache::initialize_epoch_cache;
use tracing::instrument;
use types::{
BeaconState, ChainSpec, EpochCacheError, EthSpec, FixedBytesExtended, Hash256, RelativeEpoch,
};
use types::{BeaconState, ChainSpec, EpochCacheError, EthSpec, Hash256, RelativeEpoch};
/// Mixin trait for the beacon state that provides operations on *all* caches.
///
@@ -34,8 +32,7 @@ impl<E: EthSpec> AllCaches for BeaconState<E> {
fn all_caches_built(&self) -> bool {
let current_epoch = self.current_epoch();
let Ok(epoch_cache_decision_block_root) =
self.proposer_shuffling_decision_root(Hash256::zero())
let Ok(epoch_cache_decision_block_root) = self.epoch_cache_decision_root(Hash256::ZERO)
else {
return false;
};

View File

@@ -123,7 +123,7 @@ pub fn is_epoch_cache_initialized<E: EthSpec>(
let current_epoch = state.current_epoch();
let epoch_cache: &EpochCache = state.epoch_cache();
let decision_block_root = state
.proposer_shuffling_decision_root(Hash256::zero())
.epoch_cache_decision_root(Hash256::zero())
.map_err(EpochCacheError::BeaconState)?;
Ok(epoch_cache
@@ -146,7 +146,7 @@ pub fn initialize_epoch_cache<E: EthSpec>(
let current_epoch = state.current_epoch();
let next_epoch = state.next_epoch().map_err(EpochCacheError::BeaconState)?;
let decision_block_root = state
.proposer_shuffling_decision_root(Hash256::zero())
.epoch_cache_decision_root(Hash256::zero())
.map_err(EpochCacheError::BeaconState)?;
state.build_total_active_balance_cache(spec)?;

View File

@@ -33,9 +33,7 @@ fn initialize_proposer_lookahead<E: EthSpec>(
);
}
Vector::new(lookahead).map_err(|e| {
Error::PleaseNotifyTheDevs(format!("Failed to initialize proposer lookahead: {:?}", e))
})
Vector::new(lookahead).map_err(|e| e.into())
}
pub fn upgrade_state_to_fulu<E: EthSpec>(