mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-11 18:04:18 +00:00
Altair consensus changes and refactors (#2279)
## Proposed Changes Implement the consensus changes necessary for the upcoming Altair hard fork. ## Additional Info This is quite a heavy refactor, with pivotal types like the `BeaconState` and `BeaconBlock` changing from structs to enums. This ripples through the whole codebase with field accesses changing to methods, e.g. `state.slot` => `state.slot()`. Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
use crate::EpochProcessingError;
|
||||
use core::result::Result;
|
||||
use core::result::Result::Ok;
|
||||
use safe_arith::SafeArith;
|
||||
use std::cmp::min;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::chain_spec::ChainSpec;
|
||||
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
|
||||
use types::eth_spec::EthSpec;
|
||||
|
||||
pub fn process_inactivity_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), EpochProcessingError> {
|
||||
// Score updates based on previous epoch participation, skip genesis epoch
|
||||
if state.current_epoch() == T::genesis_epoch() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let unslashed_indices = state.get_unslashed_participating_indices(
|
||||
TIMELY_TARGET_FLAG_INDEX,
|
||||
state.previous_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
for index in state.get_eligible_validator_indices()? {
|
||||
// Increase inactivity score of inactive validators
|
||||
if unslashed_indices.contains(&index) {
|
||||
let inactivity_score = state.get_inactivity_score_mut(index)?;
|
||||
inactivity_score.safe_sub_assign(min(1, *inactivity_score))?;
|
||||
} else {
|
||||
state
|
||||
.get_inactivity_score_mut(index)?
|
||||
.safe_add_assign(spec.inactivity_score_bias)?;
|
||||
}
|
||||
// Decrease the score of all validators for forgiveness when not during a leak
|
||||
if !state.is_in_inactivity_leak(spec) {
|
||||
let inactivity_score = state.get_inactivity_score_mut(index)?;
|
||||
inactivity_score
|
||||
.safe_sub_assign(min(spec.inactivity_score_recovery_rate, *inactivity_score))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use crate::per_epoch_processing::weigh_justification_and_finalization;
|
||||
use crate::per_epoch_processing::Error;
|
||||
use safe_arith::SafeArith;
|
||||
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
|
||||
use types::{BeaconState, ChainSpec, EthSpec};
|
||||
|
||||
/// Update the justified and finalized checkpoints for matching target attestations.
|
||||
pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
if state.current_epoch() <= T::genesis_epoch().safe_add(1)? {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let previous_epoch = state.previous_epoch();
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_indices = state.get_unslashed_participating_indices(
|
||||
TIMELY_TARGET_FLAG_INDEX,
|
||||
previous_epoch,
|
||||
spec,
|
||||
)?;
|
||||
let current_indices =
|
||||
state.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, current_epoch, spec)?;
|
||||
let total_active_balance = state.get_total_balance(
|
||||
state
|
||||
.get_active_validator_indices(current_epoch, spec)?
|
||||
.as_slice(),
|
||||
spec,
|
||||
)?;
|
||||
let previous_target_balance = state.get_total_balance(&previous_indices, spec)?;
|
||||
let current_target_balance = state.get_total_balance(¤t_indices, spec)?;
|
||||
weigh_justification_and_finalization(
|
||||
state,
|
||||
total_active_balance,
|
||||
previous_target_balance,
|
||||
current_target_balance,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::EpochProcessingError;
|
||||
use core::result::Result;
|
||||
use core::result::Result::Ok;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::participation_flags::ParticipationFlags;
|
||||
use types::VariableList;
|
||||
|
||||
pub fn process_participation_flag_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
) -> Result<(), EpochProcessingError> {
|
||||
*state.previous_epoch_participation_mut()? =
|
||||
std::mem::take(state.current_epoch_participation_mut()?);
|
||||
*state.current_epoch_participation_mut()? = VariableList::new(vec![
|
||||
ParticipationFlags::default(
|
||||
);
|
||||
state.validators().len()
|
||||
])?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
use safe_arith::SafeArith;
|
||||
use types::consts::altair::{
|
||||
PARTICIPATION_FLAG_WEIGHTS, TIMELY_HEAD_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX,
|
||||
WEIGHT_DENOMINATOR,
|
||||
};
|
||||
use types::{BeaconState, ChainSpec, EthSpec};
|
||||
|
||||
use crate::common::{altair::get_base_reward, decrease_balance, increase_balance};
|
||||
use crate::per_epoch_processing::{Delta, Error};
|
||||
|
||||
/// Apply attester and proposer rewards.
|
||||
///
|
||||
/// Spec v1.1.0
|
||||
pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
if state.current_epoch() == T::genesis_epoch() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut deltas = vec![Delta::default(); state.validators().len()];
|
||||
|
||||
let total_active_balance = state.get_total_active_balance(spec)?;
|
||||
|
||||
for flag_index in 0..PARTICIPATION_FLAG_WEIGHTS.len() {
|
||||
get_flag_index_deltas(&mut deltas, state, flag_index, total_active_balance, spec)?;
|
||||
}
|
||||
|
||||
get_inactivity_penalty_deltas(&mut deltas, state, spec)?;
|
||||
|
||||
// Apply the deltas, erroring on overflow above but not on overflow below (saturating at 0
|
||||
// instead).
|
||||
for (i, delta) in deltas.into_iter().enumerate() {
|
||||
increase_balance(state, i, delta.rewards)?;
|
||||
decrease_balance(state, i, delta.penalties)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the deltas for a given flag index by scanning through the participation flags.
|
||||
///
|
||||
/// Spec v1.1.0
|
||||
pub fn get_flag_index_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
flag_index: usize,
|
||||
total_active_balance: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let previous_epoch = state.previous_epoch();
|
||||
let unslashed_participating_indices =
|
||||
state.get_unslashed_participating_indices(flag_index, previous_epoch, spec)?;
|
||||
let weight = get_flag_weight(flag_index)?;
|
||||
let unslashed_participating_balance =
|
||||
state.get_total_balance(&unslashed_participating_indices, spec)?;
|
||||
let unslashed_participating_increments =
|
||||
unslashed_participating_balance.safe_div(spec.effective_balance_increment)?;
|
||||
let active_increments = total_active_balance.safe_div(spec.effective_balance_increment)?;
|
||||
|
||||
for index in state.get_eligible_validator_indices()? {
|
||||
let base_reward = get_base_reward(state, index, total_active_balance, spec)?;
|
||||
let mut delta = Delta::default();
|
||||
|
||||
if unslashed_participating_indices.contains(&(index as usize)) {
|
||||
if !state.is_in_inactivity_leak(spec) {
|
||||
let reward_numerator = base_reward
|
||||
.safe_mul(weight)?
|
||||
.safe_mul(unslashed_participating_increments)?;
|
||||
delta.reward(
|
||||
reward_numerator.safe_div(active_increments.safe_mul(WEIGHT_DENOMINATOR)?)?,
|
||||
)?;
|
||||
}
|
||||
} else if flag_index != TIMELY_HEAD_FLAG_INDEX {
|
||||
delta.penalize(base_reward.safe_mul(weight)?.safe_div(WEIGHT_DENOMINATOR)?)?;
|
||||
}
|
||||
deltas
|
||||
.get_mut(index as usize)
|
||||
.ok_or(Error::DeltaOutOfBounds(index as usize))?
|
||||
.combine(delta)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the weight for a `flag_index` from the constant list of all weights.
|
||||
pub fn get_flag_weight(flag_index: usize) -> Result<u64, Error> {
|
||||
PARTICIPATION_FLAG_WEIGHTS
|
||||
.get(flag_index)
|
||||
.copied()
|
||||
.ok_or(Error::InvalidFlagIndex(flag_index))
|
||||
}
|
||||
|
||||
pub fn get_inactivity_penalty_deltas<T: EthSpec>(
|
||||
deltas: &mut Vec<Delta>,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let previous_epoch = state.previous_epoch();
|
||||
let matching_target_indices = state.get_unslashed_participating_indices(
|
||||
TIMELY_TARGET_FLAG_INDEX,
|
||||
previous_epoch,
|
||||
spec,
|
||||
)?;
|
||||
for index in state.get_eligible_validator_indices()? {
|
||||
let mut delta = Delta::default();
|
||||
|
||||
if !matching_target_indices.contains(&index) {
|
||||
let penalty_numerator = state
|
||||
.get_validator(index)?
|
||||
.effective_balance
|
||||
.safe_mul(state.get_inactivity_score(index)?)?;
|
||||
let penalty_denominator = spec
|
||||
.inactivity_score_bias
|
||||
.safe_mul(spec.inactivity_penalty_quotient_altair)?;
|
||||
delta.penalize(penalty_numerator.safe_div(penalty_denominator)?)?;
|
||||
}
|
||||
deltas
|
||||
.get_mut(index)
|
||||
.ok_or(Error::DeltaOutOfBounds(index))?
|
||||
.combine(delta)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
use crate::EpochProcessingError;
|
||||
use safe_arith::SafeArith;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::chain_spec::ChainSpec;
|
||||
use types::eth_spec::EthSpec;
|
||||
|
||||
pub fn process_sync_committee_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), EpochProcessingError> {
|
||||
let next_epoch = state.next_epoch()?;
|
||||
if next_epoch.safe_rem(spec.epochs_per_sync_committee_period)? == 0 {
|
||||
*state.current_sync_committee_mut()? = state.next_sync_committee()?.clone();
|
||||
|
||||
*state.next_sync_committee_mut()? = state.get_next_sync_committee(spec)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user