Merge branch 'unstable' of https://github.com/sigp/lighthouse into electra-focil

This commit is contained in:
Eitan Seri-Levi
2025-04-05 17:49:57 -07:00
84 changed files with 1358 additions and 1313 deletions

View File

@@ -779,7 +779,7 @@ impl ProtoArray {
///
/// - The child is already the best child but it's now invalid due to a FFG change and should be removed.
/// - The child is already the best child and the parent is updated with the new
/// best-descendant.
/// best-descendant.
/// - The child is not the best child but becomes the best child.
/// - The child is not the best child and does not become the best child.
fn maybe_update_best_child_and_descendant<E: EthSpec>(

View File

@@ -1124,7 +1124,7 @@ mod test_compute_deltas {
///
/// - `A` (slot 31) is the common descendant.
/// - `B` (slot 33) descends from `A`, but there is a single skip slot
/// between it and `A`.
/// between it and `A`.
/// - `C` (slot 32) descends from `A` and conflicts with `B`.
///
/// Imagine that the `B` chain is finalized at epoch 1. This means that the

View File

@@ -175,6 +175,7 @@ pub fn process_epoch_single_pass<E: EthSpec>(
let mut earliest_exit_epoch = state.earliest_exit_epoch().ok();
let mut exit_balance_to_consume = state.exit_balance_to_consume().ok();
let validators_in_consolidations = get_validators_in_consolidations(state);
// Split the state into several disjoint mutable borrows.
let (
@@ -317,17 +318,26 @@ pub fn process_epoch_single_pass<E: EthSpec>(
// `process_effective_balance_updates`
if conf.effective_balance_updates {
process_single_effective_balance_update(
validator_info.index,
*balance,
&mut validator,
validator_info.current_epoch_participation,
&mut next_epoch_cache,
progressive_balances,
effective_balances_ctxt,
state_ctxt,
spec,
)?;
if validators_in_consolidations.contains(&validator_info.index) {
process_single_dummy_effective_balance_update(
validator_info.index,
&validator,
&mut next_epoch_cache,
state_ctxt,
)?;
} else {
process_single_effective_balance_update(
validator_info.index,
*balance,
&mut validator,
validator_info.current_epoch_participation,
&mut next_epoch_cache,
progressive_balances,
effective_balances_ctxt,
state_ctxt,
spec,
)?;
}
}
}
@@ -430,6 +440,7 @@ pub fn process_epoch_single_pass<E: EthSpec>(
if fork_name.electra_enabled() && conf.pending_consolidations {
process_pending_consolidations(
state,
&validators_in_consolidations,
&mut next_epoch_cache,
effective_balances_ctxt,
conf.effective_balance_updates,
@@ -1026,12 +1037,38 @@ fn process_pending_deposits_for_validator(
Ok(())
}
/// Return the set of validators referenced by consolidations, either as source or target.
///
/// This function is blind to whether the consolidations are valid and capable of being processed,
/// it just returns the set of all indices present in consolidations. This is *sufficient* to
/// make consolidations play nicely with effective balance updates. The algorithm used is:
///
/// - In the single pass: apply effective balance updates for all validators *not* referenced by
/// consolidations.
/// - Apply consolidations.
/// - Apply effective balance updates for all validators previously skipped.
///
/// Prior to Electra, the empty set is returned.
fn get_validators_in_consolidations<E: EthSpec>(state: &BeaconState<E>) -> BTreeSet<usize> {
let mut referenced_validators = BTreeSet::new();
if let Ok(pending_consolidations) = state.pending_consolidations() {
for pending_consolidation in pending_consolidations {
referenced_validators.insert(pending_consolidation.source_index as usize);
referenced_validators.insert(pending_consolidation.target_index as usize);
}
}
referenced_validators
}
/// We process pending consolidations after all of single-pass epoch processing, and then patch up
/// the effective balances for affected validators.
///
/// This is safe because processing consolidations does not depend on the `effective_balance`.
fn process_pending_consolidations<E: EthSpec>(
state: &mut BeaconState<E>,
validators_in_consolidations: &BTreeSet<usize>,
next_epoch_cache: &mut PreEpochCache,
effective_balances_ctxt: &EffectiveBalancesContext,
perform_effective_balance_updates: bool,
@@ -1042,8 +1079,6 @@ fn process_pending_consolidations<E: EthSpec>(
let next_epoch = state.next_epoch()?;
let pending_consolidations = state.pending_consolidations()?.clone();
let mut affected_validators = BTreeSet::new();
for pending_consolidation in &pending_consolidations {
let source_index = pending_consolidation.source_index as usize;
let target_index = pending_consolidation.target_index as usize;
@@ -1069,9 +1104,6 @@ fn process_pending_consolidations<E: EthSpec>(
decrease_balance(state, source_index, source_effective_balance)?;
increase_balance(state, target_index, source_effective_balance)?;
affected_validators.insert(source_index);
affected_validators.insert(target_index);
next_pending_consolidation.safe_add_assign(1)?;
}
@@ -1087,7 +1119,7 @@ fn process_pending_consolidations<E: EthSpec>(
// Re-process effective balance updates for validators affected by consolidations.
let (validators, balances, _, current_epoch_participation, _, progressive_balances, _, _) =
state.mutable_validator_fields()?;
for validator_index in affected_validators {
for &validator_index in validators_in_consolidations {
let balance = *balances
.get(validator_index)
.ok_or(BeaconStateError::UnknownValidator(validator_index))?;
@@ -1129,6 +1161,28 @@ impl EffectiveBalancesContext {
}
}
/// This function is called for validators that do not have their effective balance updated as
/// part of the single-pass loop. For these validators we compute their true effective balance
/// update after processing consolidations. However, to maintain the invariants of the
/// `PreEpochCache` we must register _some_ effective balance for them immediately.
fn process_single_dummy_effective_balance_update(
validator_index: usize,
validator: &Cow<Validator>,
next_epoch_cache: &mut PreEpochCache,
state_ctxt: &StateContext,
) -> Result<(), Error> {
// Populate the effective balance cache with the current effective balance. This will be
// overriden when `process_single_effective_balance_update` is called.
let is_active_next_epoch = validator.is_active_at(state_ctxt.next_epoch);
let temporary_effective_balance = validator.effective_balance;
next_epoch_cache.update_effective_balance(
validator_index,
temporary_effective_balance,
is_active_next_epoch,
)?;
Ok(())
}
/// This function abstracts over phase0 and Electra effective balance processing.
#[allow(clippy::too_many_arguments)]
fn process_single_effective_balance_update(

View File

@@ -42,8 +42,8 @@ impl<E: EthSpec> SyncCommitteeContribution<E> {
///
/// - `message`: A single `SyncCommitteeMessage`.
/// - `subcommittee_index`: The subcommittee this contribution pertains to out of the broader
/// sync committee. This can be determined from the `SyncSubnetId` of the gossip subnet
/// this message was seen on.
/// sync committee. This can be determined from the `SyncSubnetId` of the gossip subnet
/// this message was seen on.
/// - `validator_sync_committee_index`: The index of the validator **within** the subcommittee.
pub fn from_message(
message: &SyncCommitteeMessage,

View File

@@ -3,7 +3,7 @@ use smallvec::smallvec;
impl<N: Unsigned + Clone> TestRandom for BitList<N> {
fn random_for_test(rng: &mut impl RngCore) -> Self {
let initial_len = std::cmp::max(1, (N::to_usize() + 7) / 8);
let initial_len = std::cmp::max(1, N::to_usize().div_ceil(8));
let mut raw_bytes = smallvec![0; initial_len];
rng.fill_bytes(&mut raw_bytes);
@@ -24,7 +24,7 @@ impl<N: Unsigned + Clone> TestRandom for BitList<N> {
impl<N: Unsigned + Clone> TestRandom for BitVector<N> {
fn random_for_test(rng: &mut impl RngCore) -> Self {
let mut raw_bytes = smallvec![0; std::cmp::max(1, (N::to_usize() + 7) / 8)];
let mut raw_bytes = smallvec![0; std::cmp::max(1, N::to_usize().div_ceil(8))];
rng.fill_bytes(&mut raw_bytes);
// If N isn't divisible by 8
// zero out bits greater than N