Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2022-09-14 13:51:23 +10:00
404 changed files with 28947 additions and 12000 deletions

View File

@@ -33,7 +33,9 @@ pub fn process_epoch<T: EthSpec>(
let sync_committee = state.current_sync_committee()?.clone();
// Justification and finalization.
process_justification_and_finalization(state, &participation_cache)?;
let justification_and_finalization_state =
process_justification_and_finalization(state, &participation_cache)?;
justification_and_finalization_state.apply_changes_to_state(state);
process_inactivity_updates(state, &mut participation_cache, spec)?;

View File

@@ -12,6 +12,7 @@ pub fn process_inactivity_updates<T: EthSpec>(
participation_cache: &mut 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() == T::genesis_epoch() {
return Ok(());
@@ -25,7 +26,7 @@ pub fn process_inactivity_updates<T: EthSpec>(
return Ok(());
}
let is_in_inactivity_leak = state.is_in_inactivity_leak(spec);
let is_in_inactivity_leak = state.is_in_inactivity_leak(previous_epoch, spec);
let mut inactivity_scores = state.inactivity_scores_mut()?.iter_cow();

View File

@@ -1,23 +1,27 @@
use super::ParticipationCache;
use crate::per_epoch_processing::weigh_justification_and_finalization;
use crate::per_epoch_processing::Error;
use crate::per_epoch_processing::{
weigh_justification_and_finalization, JustificationAndFinalizationState,
};
use safe_arith::SafeArith;
use types::{BeaconState, EthSpec};
/// Update the justified and finalized checkpoints for matching target attestations.
pub fn process_justification_and_finalization<T: EthSpec>(
state: &mut BeaconState<T>,
state: &BeaconState<T>,
participation_cache: &ParticipationCache,
) -> Result<(), Error> {
) -> Result<JustificationAndFinalizationState<T>, Error> {
let justification_and_finalization_state = JustificationAndFinalizationState::new(state);
if state.current_epoch() <= T::genesis_epoch().safe_add(1)? {
return Ok(());
return Ok(justification_and_finalization_state);
}
let total_active_balance = participation_cache.current_epoch_total_active_balance();
let previous_target_balance = participation_cache.previous_epoch_target_attesting_balance()?;
let current_target_balance = participation_cache.current_epoch_target_attesting_balance()?;
weigh_justification_and_finalization(
state,
justification_and_finalization_state,
total_active_balance,
previous_target_balance,
current_target_balance,

View File

@@ -11,7 +11,7 @@
//! Additionally, this cache is returned from the `altair::process_epoch` function and can be used
//! to get useful summaries about the validator participation in an epoch.
use crate::common::altair::get_base_reward;
use crate::common::altair::{get_base_reward, BaseRewardPerIncrement};
use safe_arith::{ArithError, SafeArith};
use types::milhouse::update_map::{MaxMap, UpdateMap};
use types::{
@@ -31,6 +31,7 @@ pub enum Error {
MissingValidator(usize),
BeaconState(BeaconStateError),
Arith(ArithError),
InvalidValidatorIndex(usize),
}
impl From<BeaconStateError> for Error {
@@ -115,11 +116,13 @@ impl SingleEpochParticipationCache {
val_index: usize,
validator: &Validator,
epoch_participation: &ParticipationFlags,
state: &BeaconState<T>,
// FIXME(sproul): remove state argument
_state: &BeaconState<T>,
current_epoch: Epoch,
relative_epoch: RelativeEpoch,
) -> Result<(), BeaconStateError> {
// Sanity check to ensure the validator is active.
let epoch = relative_epoch.into_epoch(state.current_epoch());
let epoch = relative_epoch.into_epoch(current_epoch);
if !validator.is_active_at(epoch) {
return Err(BeaconStateError::ValidatorIsInactive { val_index });
}
@@ -220,6 +223,8 @@ impl ParticipationCache {
let mut validators = ValidatorInfoCache::new(state.validators().len());
let current_epoch_total_active_balance = state.get_total_active_balance()?;
let base_reward_per_increment =
BaseRewardPerIncrement::new(current_epoch_total_active_balance, spec)?;
// Contains the set of validators which are either:
//
@@ -257,7 +262,7 @@ impl ParticipationCache {
for (val_index, (((val, curr_epoch_flags), prev_epoch_flags), inactivity_score)) in iter {
let is_active_current_epoch = val.is_active_at(current_epoch);
let is_active_previous_epoch = val.is_active_at(previous_epoch);
let is_eligible = state.is_eligible_validator(val);
let is_eligible = state.is_eligible_validator(previous_epoch, val);
if is_active_current_epoch {
current_epoch_participation.process_active_validator(
@@ -265,6 +270,7 @@ impl ParticipationCache {
val,
curr_epoch_flags,
state,
current_epoch,
RelativeEpoch::Current,
)?;
}
@@ -277,6 +283,7 @@ impl ParticipationCache {
val,
prev_epoch_flags,
state,
current_epoch,
RelativeEpoch::Previous,
)?;
}
@@ -326,7 +333,7 @@ impl ParticipationCache {
if is_eligible || is_active_current_epoch {
let effective_balance = val.effective_balance;
let base_reward =
get_base_reward(effective_balance, current_epoch_total_active_balance, spec)?;
get_base_reward(effective_balance, base_reward_per_increment, spec)?;
validator_info.base_reward = base_reward;
validators.info[val_index] = Some(validator_info);
}

View File

@@ -76,6 +76,7 @@ pub fn get_flag_index_deltas<T: EthSpec>(
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 previous_epoch = state.previous_epoch();
for &index in participation_cache.eligible_validator_indices() {
let validator = participation_cache.get_validator(index)?;
@@ -84,7 +85,7 @@ pub fn get_flag_index_deltas<T: EthSpec>(
let mut delta = Delta::default();
if validator.is_unslashed_participating_index(flag_index)? {
if !state.is_in_inactivity_leak(spec) {
if !state.is_in_inactivity_leak(previous_epoch, spec) {
let reward_numerator = base_reward
.safe_mul(weight)?
.safe_mul(unslashed_participating_increments)?;

View File

@@ -31,7 +31,9 @@ pub fn process_epoch<T: EthSpec>(
validator_statuses.process_attestations(state)?;
// Justification and finalization.
process_justification_and_finalization(state, &validator_statuses.total_balances, spec)?;
let justification_and_finalization_state =
process_justification_and_finalization(state, &validator_statuses.total_balances, spec)?;
justification_and_finalization_state.apply_changes_to_state(state);
// Rewards and Penalties.
process_rewards_and_penalties(state, &mut validator_statuses, spec)?;

View File

@@ -1,21 +1,25 @@
use crate::per_epoch_processing::base::TotalBalances;
use crate::per_epoch_processing::weigh_justification_and_finalization;
use crate::per_epoch_processing::Error;
use crate::per_epoch_processing::{
weigh_justification_and_finalization, JustificationAndFinalizationState,
};
use safe_arith::SafeArith;
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>,
state: &BeaconState<T>,
total_balances: &TotalBalances,
_spec: &ChainSpec,
) -> Result<(), Error> {
) -> Result<JustificationAndFinalizationState<T>, Error> {
let justification_and_finalization_state = JustificationAndFinalizationState::new(state);
if state.current_epoch() <= T::genesis_epoch().safe_add(1)? {
return Ok(());
return Ok(justification_and_finalization_state);
}
weigh_justification_and_finalization(
state,
justification_and_finalization_state,
total_balances.current_epoch(),
total_balances.previous_epoch_target_attesters(),
total_balances.current_epoch_target_attesters(),

View File

@@ -78,6 +78,7 @@ pub fn get_attestation_deltas<T: EthSpec>(
validator_statuses: &ValidatorStatuses,
spec: &ChainSpec,
) -> Result<Vec<AttestationDelta>, Error> {
let previous_epoch = state.previous_epoch();
let finality_delay = state
.previous_epoch()
.safe_sub(state.finalized_checkpoint().epoch)?
@@ -94,7 +95,7 @@ pub fn get_attestation_deltas<T: EthSpec>(
// eligible.
// FIXME(sproul): this is inefficient
let full_validator = state.get_validator(index)?;
if !state.is_eligible_validator(full_validator) {
if !state.is_eligible_validator(previous_epoch, full_validator) {
continue;
}

View File

@@ -278,8 +278,8 @@ impl ValidatorStatuses {
// Loop through the participating validator indices and update the status vec.
for validator_index in attesting_indices {
self.statuses
.get_mut(validator_index)
.ok_or(BeaconStateError::UnknownValidator(validator_index))?
.get_mut(validator_index as usize)
.ok_or(BeaconStateError::UnknownValidator(validator_index as usize))?
.update(&status);
}
}

View File

@@ -127,7 +127,12 @@ impl<T: EthSpec> EpochProcessingSummary<T> {
EpochProcessingSummary::Altair {
participation_cache,
..
} => participation_cache.is_current_epoch_timely_target_attester(val_index),
} => participation_cache
.is_current_epoch_timely_target_attester(val_index)
.or_else(|e| match e {
ParticipationCacheError::InvalidValidatorIndex(_) => Ok(false),
e => Err(e),
}),
}
}
@@ -218,7 +223,12 @@ impl<T: EthSpec> EpochProcessingSummary<T> {
EpochProcessingSummary::Altair {
participation_cache,
..
} => participation_cache.is_previous_epoch_timely_target_attester(val_index),
} => participation_cache
.is_previous_epoch_timely_target_attester(val_index)
.or_else(|e| match e {
ParticipationCacheError::InvalidValidatorIndex(_) => Ok(false),
e => Err(e),
}),
}
}
@@ -244,7 +254,12 @@ impl<T: EthSpec> EpochProcessingSummary<T> {
EpochProcessingSummary::Altair {
participation_cache,
..
} => participation_cache.is_previous_epoch_timely_head_attester(val_index),
} => participation_cache
.is_previous_epoch_timely_head_attester(val_index)
.or_else(|e| match e {
ParticipationCacheError::InvalidValidatorIndex(_) => Ok(false),
e => Err(e),
}),
}
}
@@ -270,7 +285,12 @@ impl<T: EthSpec> EpochProcessingSummary<T> {
EpochProcessingSummary::Altair {
participation_cache,
..
} => participation_cache.is_previous_epoch_timely_source_attester(val_index),
} => participation_cache
.is_previous_epoch_timely_source_attester(val_index)
.or_else(|e| match e {
ParticipationCacheError::InvalidValidatorIndex(_) => Ok(false),
e => Err(e),
}),
}
}

View File

@@ -0,0 +1,115 @@
use types::{BeaconState, BeaconStateError, BitVector, Checkpoint, Epoch, EthSpec, Hash256};
/// This is a subset of the `BeaconState` which is used to compute justification and finality
/// without modifying the `BeaconState`.
///
/// A `JustificationAndFinalizationState` can be created from a `BeaconState` to compute
/// justification/finality changes and then applied to a `BeaconState` to enshrine those changes.
#[must_use = "this value must be applied to a state or explicitly dropped"]
pub struct JustificationAndFinalizationState<T: EthSpec> {
/*
* Immutable fields.
*/
previous_epoch: Epoch,
previous_epoch_target_root: Result<Hash256, BeaconStateError>,
current_epoch: Epoch,
current_epoch_target_root: Result<Hash256, BeaconStateError>,
/*
* Mutable fields.
*/
previous_justified_checkpoint: Checkpoint,
current_justified_checkpoint: Checkpoint,
finalized_checkpoint: Checkpoint,
justification_bits: BitVector<T::JustificationBitsLength>,
}
impl<T: EthSpec> JustificationAndFinalizationState<T> {
pub fn new(state: &BeaconState<T>) -> Self {
let previous_epoch = state.previous_epoch();
let current_epoch = state.current_epoch();
Self {
previous_epoch,
previous_epoch_target_root: state.get_block_root_at_epoch(previous_epoch).copied(),
current_epoch,
current_epoch_target_root: state.get_block_root_at_epoch(current_epoch).copied(),
previous_justified_checkpoint: state.previous_justified_checkpoint(),
current_justified_checkpoint: state.current_justified_checkpoint(),
finalized_checkpoint: state.finalized_checkpoint(),
justification_bits: state.justification_bits().clone(),
}
}
pub fn apply_changes_to_state(self, state: &mut BeaconState<T>) {
let Self {
/*
* Immutable fields do not need to be used.
*/
previous_epoch: _,
previous_epoch_target_root: _,
current_epoch: _,
current_epoch_target_root: _,
/*
* Mutable fields *must* be used.
*/
previous_justified_checkpoint,
current_justified_checkpoint,
finalized_checkpoint,
justification_bits,
} = self;
*state.previous_justified_checkpoint_mut() = previous_justified_checkpoint;
*state.current_justified_checkpoint_mut() = current_justified_checkpoint;
*state.finalized_checkpoint_mut() = finalized_checkpoint;
*state.justification_bits_mut() = justification_bits;
}
pub fn previous_epoch(&self) -> Epoch {
self.previous_epoch
}
pub fn current_epoch(&self) -> Epoch {
self.current_epoch
}
pub fn get_block_root_at_epoch(&self, epoch: Epoch) -> Result<Hash256, BeaconStateError> {
if epoch == self.previous_epoch {
self.previous_epoch_target_root.clone()
} else if epoch == self.current_epoch {
self.current_epoch_target_root.clone()
} else {
Err(BeaconStateError::SlotOutOfBounds)
}
}
pub fn previous_justified_checkpoint(&self) -> Checkpoint {
self.previous_justified_checkpoint
}
pub fn previous_justified_checkpoint_mut(&mut self) -> &mut Checkpoint {
&mut self.previous_justified_checkpoint
}
pub fn current_justified_checkpoint_mut(&mut self) -> &mut Checkpoint {
&mut self.current_justified_checkpoint
}
pub fn current_justified_checkpoint(&self) -> Checkpoint {
self.current_justified_checkpoint
}
pub fn finalized_checkpoint(&self) -> Checkpoint {
self.finalized_checkpoint
}
pub fn finalized_checkpoint_mut(&mut self) -> &mut Checkpoint {
&mut self.finalized_checkpoint
}
pub fn justification_bits(&self) -> &BitVector<T::JustificationBitsLength> {
&self.justification_bits
}
pub fn justification_bits_mut(&mut self) -> &mut BitVector<T::JustificationBitsLength> {
&mut self.justification_bits
}
}

View File

@@ -6,8 +6,8 @@ use bls::Hash256;
use env_logger::{Builder, Env};
use types::Slot;
#[test]
fn runs_without_error() {
#[tokio::test]
async fn runs_without_error() {
Builder::from_env(Env::default().default_filter_or("error")).init();
let harness = BeaconChainHarness::builder(MinimalEthSpec)
@@ -22,15 +22,17 @@ fn runs_without_error() {
(MinimalEthSpec::genesis_epoch() + 4).end_slot(MinimalEthSpec::slots_per_epoch());
let state = harness.get_current_state();
harness.add_attested_blocks_at_slots(
state,
Hash256::zero(),
(1..target_slot.as_u64())
.map(Slot::new)
.collect::<Vec<_>>()
.as_slice(),
(0..8).collect::<Vec<_>>().as_slice(),
);
harness
.add_attested_blocks_at_slots(
state,
Hash256::zero(),
(1..target_slot.as_u64())
.map(Slot::new)
.collect::<Vec<_>>()
.as_slice(),
(0..8).collect::<Vec<_>>().as_slice(),
)
.await;
let mut new_head_state = harness.get_current_state();
process_epoch(&mut new_head_state, &spec).unwrap();
@@ -45,8 +47,8 @@ mod release_tests {
use beacon_chain::test_utils::{AttestationStrategy, BlockStrategy};
use types::{Epoch, ForkName, InconsistentFork, MainnetEthSpec};
#[test]
fn altair_state_on_base_fork() {
#[tokio::test]
async fn altair_state_on_base_fork() {
let mut spec = MainnetEthSpec::default_spec();
let slots_per_epoch = MainnetEthSpec::slots_per_epoch();
// The Altair fork happens at epoch 1.
@@ -61,12 +63,14 @@ mod release_tests {
harness.advance_slot();
harness.extend_chain(
// Build out enough blocks so we get an Altair block at the very end of an epoch.
(slots_per_epoch * 2 - 1) as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
harness
.extend_chain(
// Build out enough blocks so we get an Altair block at the very end of an epoch.
(slots_per_epoch * 2 - 1) as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
)
.await;
harness.get_current_state()
};
@@ -103,8 +107,8 @@ mod release_tests {
);
}
#[test]
fn base_state_on_altair_fork() {
#[tokio::test]
async fn base_state_on_altair_fork() {
let mut spec = MainnetEthSpec::default_spec();
let slots_per_epoch = MainnetEthSpec::slots_per_epoch();
// The Altair fork never happens.
@@ -119,12 +123,14 @@ mod release_tests {
harness.advance_slot();
harness.extend_chain(
// Build out enough blocks so we get a block at the very end of an epoch.
(slots_per_epoch * 2 - 1) as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
harness
.extend_chain(
// Build out enough blocks so we get a block at the very end of an epoch.
(slots_per_epoch * 2 - 1) as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
)
.await;
harness.get_current_state()
};

View File

@@ -1,16 +1,16 @@
use crate::per_epoch_processing::Error;
use crate::per_epoch_processing::{Error, JustificationAndFinalizationState};
use safe_arith::SafeArith;
use std::ops::Range;
use types::{BeaconState, Checkpoint, EthSpec};
use types::{Checkpoint, EthSpec};
/// Update the justified and finalized checkpoints for matching target attestations.
#[allow(clippy::if_same_then_else)] // For readability and consistency with spec.
pub fn weigh_justification_and_finalization<T: EthSpec>(
state: &mut BeaconState<T>,
mut state: JustificationAndFinalizationState<T>,
total_active_balance: u64,
previous_target_balance: u64,
current_target_balance: u64,
) -> Result<(), Error> {
) -> Result<JustificationAndFinalizationState<T>, Error> {
let previous_epoch = state.previous_epoch();
let current_epoch = state.current_epoch();
@@ -24,7 +24,7 @@ pub fn weigh_justification_and_finalization<T: EthSpec>(
if previous_target_balance.safe_mul(3)? >= total_active_balance.safe_mul(2)? {
*state.current_justified_checkpoint_mut() = Checkpoint {
epoch: previous_epoch,
root: *state.get_block_root_at_epoch(previous_epoch)?,
root: state.get_block_root_at_epoch(previous_epoch)?,
};
state.justification_bits_mut().set(1, true)?;
}
@@ -32,7 +32,7 @@ pub fn weigh_justification_and_finalization<T: EthSpec>(
if current_target_balance.safe_mul(3)? >= total_active_balance.safe_mul(2)? {
*state.current_justified_checkpoint_mut() = Checkpoint {
epoch: current_epoch,
root: *state.get_block_root_at_epoch(current_epoch)?,
root: state.get_block_root_at_epoch(current_epoch)?,
};
state.justification_bits_mut().set(0, true)?;
}
@@ -66,5 +66,5 @@ pub fn weigh_justification_and_finalization<T: EthSpec>(
*state.finalized_checkpoint_mut() = old_current_justified_checkpoint;
}
Ok(())
Ok(state)
}