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:
Michael Sproul
2021-07-09 06:15:32 +00:00
parent 89361573d4
commit b4689e20c6
271 changed files with 9652 additions and 8444 deletions

View File

@@ -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(())
}

View File

@@ -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(&current_indices, spec)?;
weigh_justification_and_finalization(
state,
total_active_balance,
previous_target_balance,
current_target_balance,
)
}

View File

@@ -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(())
}

View File

@@ -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(())
}

View File

@@ -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(())
}