Single-pass epoch processing and optimised block processing (#5279)

* Single-pass epoch processing (#4483, #4573)

Co-authored-by: Michael Sproul <michael@sigmaprime.io>

* Delete unused epoch processing code (#5170)

* Delete unused epoch processing code

* Compare total deltas

* Remove unnecessary apply_pending

* cargo fmt

* Remove newline

* Use epoch cache in block packing (#5223)

* Remove progressive balances mode (#5224)

* inline inactivity_penalty_quotient_for_state

* drop previous_epoch_total_active_balance

* fc lint

* spec compliant process_sync_aggregate (#15)

* spec compliant process_sync_aggregate

* Update consensus/state_processing/src/per_block_processing/altair/sync_committee.rs

Co-authored-by: Michael Sproul <micsproul@gmail.com>

---------

Co-authored-by: Michael Sproul <micsproul@gmail.com>

* Delete the participation cache (#16)

* update help

* Fix op_pool tests

* Fix fork choice tests

* Merge remote-tracking branch 'sigp/unstable' into epoch-single-pass

* Simplify exit cache (#5280)

* Fix clippy on exit cache

* Clean up single-pass a bit (#5282)

* Address Mark's review of single-pass (#5386)

* Merge remote-tracking branch 'origin/unstable' into epoch-single-pass

* Address Sean's review comments (#5414)

* Address most of Sean's review comments

* Simplify total balance cache building

* Clean up unused junk

* Merge remote-tracking branch 'origin/unstable' into epoch-single-pass

* More self-review

* Merge remote-tracking branch 'origin/unstable' into epoch-single-pass

* Merge branch 'unstable' into epoch-single-pass

* Fix imports for beta compiler

* Fix tests, probably
This commit is contained in:
Michael Sproul
2024-04-05 00:14:36 +11:00
committed by GitHub
parent f4cdcea7b1
commit feb531f85b
81 changed files with 2545 additions and 1316 deletions

View File

@@ -1,42 +1,23 @@
use super::ParticipationCache;
use crate::per_epoch_processing::single_pass::{process_epoch_single_pass, SinglePassConfig};
use crate::EpochProcessingError;
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<E: EthSpec>(
/// Slow version of `process_inactivity_updates` that runs a subset of single-pass processing.
///
/// Should not be used for block processing, but is useful for testing & analytics.
pub fn process_inactivity_updates_slow<E: EthSpec>(
state: &mut BeaconState<E>,
participation_cache: &ParticipationCache,
spec: &ChainSpec,
) -> Result<(), EpochProcessingError> {
let previous_epoch = state.previous_epoch();
// Score updates based on previous epoch participation, skip genesis epoch
if state.current_epoch() == E::genesis_epoch() {
return Ok(());
}
let unslashed_indices = participation_cache
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, state.previous_epoch())?;
for &index in participation_cache.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(previous_epoch, spec)? {
let inactivity_score = state.get_inactivity_score_mut(index)?;
inactivity_score
.safe_sub_assign(min(spec.inactivity_score_recovery_rate, *inactivity_score))?;
}
}
process_epoch_single_pass(
state,
spec,
SinglePassConfig {
inactivity_updates: true,
..SinglePassConfig::disable_all()
},
)?;
Ok(())
}

View File

@@ -1,32 +1,27 @@
use super::ParticipationCache;
use crate::per_epoch_processing::Error;
use crate::per_epoch_processing::{
weigh_justification_and_finalization, JustificationAndFinalizationState,
};
use safe_arith::SafeArith;
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
use types::{BeaconState, EthSpec};
/// Update the justified and finalized checkpoints for matching target attestations.
/// Process justification and finalization using the progressive balances cache.
pub fn process_justification_and_finalization<E: EthSpec>(
state: &BeaconState<E>,
participation_cache: &ParticipationCache,
) -> Result<JustificationAndFinalizationState<E>, Error> {
let justification_and_finalization_state = JustificationAndFinalizationState::new(state);
if state.current_epoch() <= E::genesis_epoch().safe_add(1)? {
return Ok(justification_and_finalization_state);
}
let previous_epoch = state.previous_epoch();
let current_epoch = state.current_epoch();
let previous_indices = participation_cache
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, previous_epoch)?;
let current_indices = participation_cache
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, current_epoch)?;
let total_active_balance = participation_cache.current_epoch_total_active_balance();
let previous_target_balance = previous_indices.total_balance()?;
let current_target_balance = current_indices.total_balance()?;
// Load cached balances
let progressive_balances_cache = state.progressive_balances_cache();
let previous_target_balance =
progressive_balances_cache.previous_epoch_target_attesting_balance()?;
let current_target_balance =
progressive_balances_cache.current_epoch_target_attesting_balance()?;
let total_active_balance = state.get_total_active_balance()?;
weigh_justification_and_finalization(
justification_and_finalization_state,
total_active_balance,

View File

@@ -1,98 +1,25 @@
use super::ParticipationCache;
use safe_arith::SafeArith;
use types::consts::altair::{
PARTICIPATION_FLAG_WEIGHTS, TIMELY_HEAD_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX,
WEIGHT_DENOMINATOR,
use crate::per_epoch_processing::{
single_pass::{process_epoch_single_pass, SinglePassConfig},
Error,
};
use types::consts::altair::PARTICIPATION_FLAG_WEIGHTS;
use types::{BeaconState, ChainSpec, EthSpec};
use crate::common::{
altair::{get_base_reward, BaseRewardPerIncrement},
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<E: EthSpec>(
/// This function should only be used for testing.
pub fn process_rewards_and_penalties_slow<E: EthSpec>(
state: &mut BeaconState<E>,
participation_cache: &ParticipationCache,
spec: &ChainSpec,
) -> Result<(), Error> {
if state.current_epoch() == E::genesis_epoch() {
return Ok(());
}
let mut deltas = vec![Delta::default(); state.validators().len()];
let total_active_balance = participation_cache.current_epoch_total_active_balance();
for flag_index in 0..PARTICIPATION_FLAG_WEIGHTS.len() {
get_flag_index_deltas(
&mut deltas,
state,
flag_index,
total_active_balance,
participation_cache,
spec,
)?;
}
get_inactivity_penalty_deltas(&mut deltas, state, participation_cache, 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<E: EthSpec>(
deltas: &mut [Delta],
state: &BeaconState<E>,
flag_index: usize,
total_active_balance: u64,
participation_cache: &ParticipationCache,
spec: &ChainSpec,
) -> Result<(), Error> {
let previous_epoch = state.previous_epoch();
let unslashed_participating_indices =
participation_cache.get_unslashed_participating_indices(flag_index, previous_epoch)?;
let weight = get_flag_weight(flag_index)?;
let unslashed_participating_balance = unslashed_participating_indices.total_balance()?;
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)?;
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
for &index in participation_cache.eligible_validator_indices() {
let base_reward = get_base_reward(state, index, base_reward_per_increment, spec)?;
let mut delta = Delta::default();
if unslashed_participating_indices.contains(index)? {
if !state.is_in_inactivity_leak(previous_epoch, 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)
.ok_or(Error::DeltaOutOfBounds(index))?
.combine(delta)?;
}
process_epoch_single_pass(
state,
spec,
SinglePassConfig {
rewards_and_penalties: true,
..SinglePassConfig::disable_all()
},
)?;
Ok(())
}
@@ -103,33 +30,3 @@ pub fn get_flag_weight(flag_index: usize) -> Result<u64, Error> {
.copied()
.ok_or(Error::InvalidFlagIndex(flag_index))
}
pub fn get_inactivity_penalty_deltas<E: EthSpec>(
deltas: &mut [Delta],
state: &BeaconState<E>,
participation_cache: &ParticipationCache,
spec: &ChainSpec,
) -> Result<(), Error> {
let previous_epoch = state.previous_epoch();
let matching_target_indices = participation_cache
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, previous_epoch)?;
for &index in participation_cache.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_for_state(state))?;
delta.penalize(penalty_numerator.safe_div(penalty_denominator)?)?;
}
deltas
.get_mut(index)
.ok_or(Error::DeltaOutOfBounds(index))?
.combine(delta)?;
}
Ok(())
}