mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-17 10:48:28 +00:00
Implement tree states & hierarchical state DB
This commit is contained in:
@@ -6,17 +6,18 @@ use crate::{
|
||||
use std::marker::PhantomData;
|
||||
use types::{BeaconState, BlindedPayload, ChainSpec, EthSpec, Hash256, SignedBeaconBlock, Slot};
|
||||
|
||||
type PreBlockHook<'a, E, Error> = Box<
|
||||
pub type PreBlockHook<'a, E, Error> = Box<
|
||||
dyn FnMut(&mut BeaconState<E>, &SignedBeaconBlock<E, BlindedPayload<E>>) -> Result<(), Error>
|
||||
+ 'a,
|
||||
>;
|
||||
type PostBlockHook<'a, E, Error> = PreBlockHook<'a, E, Error>;
|
||||
type PreSlotHook<'a, E, Error> = Box<dyn FnMut(&mut BeaconState<E>) -> Result<(), Error> + 'a>;
|
||||
type PostSlotHook<'a, E, Error> = Box<
|
||||
pub type PostBlockHook<'a, E, Error> = PreBlockHook<'a, E, Error>;
|
||||
pub type PreSlotHook<'a, E, Error> =
|
||||
Box<dyn FnMut(Option<Hash256>, &mut BeaconState<E>) -> Result<(), Error> + 'a>;
|
||||
pub type PostSlotHook<'a, E, Error> = Box<
|
||||
dyn FnMut(&mut BeaconState<E>, Option<EpochProcessingSummary<E>>, bool) -> Result<(), Error>
|
||||
+ 'a,
|
||||
>;
|
||||
type StateRootIterDefault<Error> = std::iter::Empty<Result<(Hash256, Slot), Error>>;
|
||||
pub type StateRootIterDefault<Error> = std::iter::Empty<Result<(Hash256, Slot), Error>>;
|
||||
|
||||
/// Efficiently apply blocks to a state while configuring various parameters.
|
||||
///
|
||||
@@ -29,7 +30,6 @@ pub struct BlockReplayer<
|
||||
> {
|
||||
state: BeaconState<Spec>,
|
||||
spec: &'a ChainSpec,
|
||||
state_processing_strategy: StateProcessingStrategy,
|
||||
block_sig_strategy: BlockSignatureStrategy,
|
||||
verify_block_root: Option<VerifyBlockRoot>,
|
||||
pre_block_hook: Option<PreBlockHook<'a, Spec, Error>>,
|
||||
@@ -87,7 +87,6 @@ where
|
||||
Self {
|
||||
state,
|
||||
spec,
|
||||
state_processing_strategy: StateProcessingStrategy::Accurate,
|
||||
block_sig_strategy: BlockSignatureStrategy::VerifyBulk,
|
||||
verify_block_root: Some(VerifyBlockRoot::True),
|
||||
pre_block_hook: None,
|
||||
@@ -105,10 +104,10 @@ where
|
||||
mut self,
|
||||
state_processing_strategy: StateProcessingStrategy,
|
||||
) -> Self {
|
||||
// FIXME(sproul): no-op
|
||||
if state_processing_strategy == StateProcessingStrategy::Inconsistent {
|
||||
self.verify_block_root = None;
|
||||
}
|
||||
self.state_processing_strategy = state_processing_strategy;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -184,11 +183,6 @@ where
|
||||
blocks: &[SignedBeaconBlock<E, BlindedPayload<E>>],
|
||||
i: usize,
|
||||
) -> Result<Option<Hash256>, Error> {
|
||||
// If we don't care about state roots then return immediately.
|
||||
if self.state_processing_strategy == StateProcessingStrategy::Inconsistent {
|
||||
return Ok(Some(Hash256::zero()));
|
||||
}
|
||||
|
||||
// If a state root iterator is configured, use it to find the root.
|
||||
if let Some(ref mut state_root_iter) = self.state_root_iter {
|
||||
let opt_root = state_root_iter
|
||||
@@ -230,11 +224,12 @@ where
|
||||
}
|
||||
|
||||
while self.state.slot() < block.slot() {
|
||||
let state_root = self.get_state_root(self.state.slot(), &blocks, i)?;
|
||||
|
||||
if let Some(ref mut pre_slot_hook) = self.pre_slot_hook {
|
||||
pre_slot_hook(&mut self.state)?;
|
||||
pre_slot_hook(state_root, &mut self.state)?;
|
||||
}
|
||||
|
||||
let state_root = self.get_state_root(self.state.slot(), &blocks, i)?;
|
||||
let summary = per_slot_processing(&mut self.state, state_root, self.spec)
|
||||
.map_err(BlockReplayError::from)?;
|
||||
|
||||
@@ -248,15 +243,11 @@ where
|
||||
pre_block_hook(&mut self.state, block)?;
|
||||
}
|
||||
|
||||
let verify_block_root = self.verify_block_root.unwrap_or_else(|| {
|
||||
// If no explicit policy is set, verify only the first 1 or 2 block roots if using
|
||||
// accurate state roots. Inaccurate state roots require block root verification to
|
||||
// be off.
|
||||
if i <= 1 && self.state_processing_strategy == StateProcessingStrategy::Accurate {
|
||||
VerifyBlockRoot::True
|
||||
} else {
|
||||
VerifyBlockRoot::False
|
||||
}
|
||||
// If no explicit policy is set, verify only the first 1 or 2 block roots.
|
||||
let verify_block_root = self.verify_block_root.unwrap_or(if i <= 1 {
|
||||
VerifyBlockRoot::True
|
||||
} else {
|
||||
VerifyBlockRoot::False
|
||||
});
|
||||
// Proposer index was already checked when this block was originally processed, we
|
||||
// can omit recomputing it during replay.
|
||||
@@ -266,7 +257,7 @@ where
|
||||
&mut self.state,
|
||||
block,
|
||||
self.block_sig_strategy,
|
||||
self.state_processing_strategy,
|
||||
StateProcessingStrategy::Accurate,
|
||||
verify_block_root,
|
||||
&mut ctxt,
|
||||
self.spec,
|
||||
@@ -280,11 +271,12 @@ where
|
||||
|
||||
if let Some(target_slot) = target_slot {
|
||||
while self.state.slot() < target_slot {
|
||||
let state_root = self.get_state_root(self.state.slot(), &blocks, blocks.len())?;
|
||||
|
||||
if let Some(ref mut pre_slot_hook) = self.pre_slot_hook {
|
||||
pre_slot_hook(&mut self.state)?;
|
||||
pre_slot_hook(state_root, &mut self.state)?;
|
||||
}
|
||||
|
||||
let state_root = self.get_state_root(self.state.slot(), &blocks, blocks.len())?;
|
||||
let summary = per_slot_processing(&mut self.state, state_root, self.spec)
|
||||
.map_err(BlockReplayError::from)?;
|
||||
|
||||
|
||||
@@ -24,14 +24,12 @@ impl BaseRewardPerIncrement {
|
||||
/// shown to be a significant optimisation.
|
||||
///
|
||||
/// Spec v1.1.0
|
||||
pub fn get_base_reward<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
index: usize,
|
||||
pub fn get_base_reward(
|
||||
validator_effective_balance: u64,
|
||||
base_reward_per_increment: BaseRewardPerIncrement,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, Error> {
|
||||
state
|
||||
.get_effective_balance(index)?
|
||||
validator_effective_balance
|
||||
.safe_div(spec.effective_balance_increment)?
|
||||
.safe_mul(base_reward_per_increment.as_u64())
|
||||
.map_err(Into::into)
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
use integer_sqrt::IntegerSquareRoot;
|
||||
use safe_arith::SafeArith;
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use types::*;
|
||||
|
||||
/// Returns the base reward for some validator.
|
||||
pub fn get_base_reward<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
index: usize,
|
||||
// Should be == get_total_active_balance(state, spec)
|
||||
total_active_balance: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, BeaconStateError> {
|
||||
state
|
||||
.get_effective_balance(index)?
|
||||
.safe_mul(spec.base_reward_factor)?
|
||||
.safe_div(total_active_balance.integer_sqrt())?
|
||||
.safe_div(spec.base_rewards_per_epoch)
|
||||
.map_err(Into::into)
|
||||
/// This type exists to avoid confusing `total_active_balance` with `sqrt_total_active_balance`,
|
||||
/// since they are used in close proximity and the same type (`u64`).
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SqrtTotalActiveBalance(u64);
|
||||
|
||||
impl SqrtTotalActiveBalance {
|
||||
pub fn new(total_active_balance: u64) -> Self {
|
||||
Self(total_active_balance.integer_sqrt())
|
||||
}
|
||||
|
||||
pub fn as_u64(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the base reward for some validator.
|
||||
pub fn get_base_reward(
|
||||
validator_effective_balance: u64,
|
||||
sqrt_total_active_balance: SqrtTotalActiveBalance,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, ArithError> {
|
||||
validator_effective_balance
|
||||
.safe_mul(spec.base_reward_factor)?
|
||||
.safe_div(sqrt_total_active_balance.as_u64())?
|
||||
.safe_div(spec.base_rewards_per_epoch)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ pub fn initiate_validator_exit<T: EthSpec>(
|
||||
index: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Return if the validator already initiated exit
|
||||
if state.get_validator(index)?.exit_epoch != spec.far_future_epoch {
|
||||
return Ok(());
|
||||
}
|
||||
// We do things in a slightly different order to the spec here. Instead of immediately checking
|
||||
// whether the validator has already exited, we instead prepare the exit cache and compute the
|
||||
// cheap-to-calculate values from that. *Then* we look up the validator a single time in the
|
||||
// validator tree (expensive), make the check and mutate as appropriate.
|
||||
|
||||
// Ensure the exit cache is built.
|
||||
state.build_exit_cache(spec)?;
|
||||
@@ -28,12 +28,21 @@ pub fn initiate_validator_exit<T: EthSpec>(
|
||||
exit_queue_epoch.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
let validator = state.get_validator_cow(index)?;
|
||||
|
||||
// Return if the validator already initiated exit
|
||||
if validator.exit_epoch() != spec.far_future_epoch {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let validator = validator.to_mut();
|
||||
validator.mutable.exit_epoch = exit_queue_epoch;
|
||||
validator.mutable.withdrawable_epoch =
|
||||
exit_queue_epoch.safe_add(spec.min_validator_withdrawability_delay)?;
|
||||
|
||||
state
|
||||
.exit_cache_mut()
|
||||
.record_validator_exit(exit_queue_epoch)?;
|
||||
state.get_validator_mut(index)?.exit_epoch = exit_queue_epoch;
|
||||
state.get_validator_mut(index)?.withdrawable_epoch =
|
||||
exit_queue_epoch.safe_add(spec.min_validator_withdrawability_delay)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@ pub fn increase_balance<E: EthSpec>(
|
||||
index: usize,
|
||||
delta: u64,
|
||||
) -> Result<(), BeaconStateError> {
|
||||
state.get_balance_mut(index)?.safe_add_assign(delta)?;
|
||||
Ok(())
|
||||
increase_balance_directly(state.get_balance_mut(index)?, delta)
|
||||
}
|
||||
|
||||
/// Decrease the balance of a validator, saturating upon overflow, as per the spec.
|
||||
@@ -34,7 +33,17 @@ pub fn decrease_balance<E: EthSpec>(
|
||||
index: usize,
|
||||
delta: u64,
|
||||
) -> Result<(), BeaconStateError> {
|
||||
let balance = state.get_balance_mut(index)?;
|
||||
decrease_balance_directly(state.get_balance_mut(index)?, delta)
|
||||
}
|
||||
|
||||
/// Increase the balance of a validator, erroring upon overflow, as per the spec.
|
||||
pub fn increase_balance_directly(balance: &mut u64, delta: u64) -> Result<(), BeaconStateError> {
|
||||
balance.safe_add_assign(delta)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decrease the balance of a validator, saturating upon overflow, as per the spec.
|
||||
pub fn decrease_balance_directly(balance: &mut u64, delta: u64) -> Result<(), BeaconStateError> {
|
||||
*balance = balance.saturating_sub(delta);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ pub fn slash_validator<T: EthSpec>(
|
||||
initiate_validator_exit(state, slashed_index, spec)?;
|
||||
|
||||
let validator = state.get_validator_mut(slashed_index)?;
|
||||
validator.slashed = true;
|
||||
validator.withdrawable_epoch = cmp::max(
|
||||
validator.withdrawable_epoch,
|
||||
validator.mutable.slashed = true;
|
||||
validator.mutable.withdrawable_epoch = cmp::max(
|
||||
validator.withdrawable_epoch(),
|
||||
epoch.safe_add(T::EpochsPerSlashingsVector::to_u64())?,
|
||||
);
|
||||
let validator_effective_balance = validator.effective_balance;
|
||||
let validator_effective_balance = validator.effective_balance();
|
||||
state.set_slashings(
|
||||
epoch,
|
||||
state
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use crate::common::get_indexed_attestation;
|
||||
use crate::per_block_processing::errors::{AttestationInvalid, BlockOperationError};
|
||||
use crate::{EpochCache, EpochCacheError};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{hash_map::Entry, HashMap};
|
||||
use std::marker::PhantomData;
|
||||
use tree_hash::TreeHash;
|
||||
@@ -8,7 +10,7 @@ use types::{
|
||||
ChainSpec, Epoch, EthSpec, Hash256, IndexedAttestation, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConsensusContext<T: EthSpec> {
|
||||
/// Slot to act as an identifier/safeguard
|
||||
slot: Slot,
|
||||
@@ -16,6 +18,8 @@ pub struct ConsensusContext<T: EthSpec> {
|
||||
proposer_index: Option<u64>,
|
||||
/// Block root of the block at `slot`.
|
||||
current_block_root: Option<Hash256>,
|
||||
/// Epoch cache of values that are useful for block processing that are static over an epoch.
|
||||
epoch_cache: Option<EpochCache>,
|
||||
/// Cache of indexed attestations constructed during block processing.
|
||||
indexed_attestations:
|
||||
HashMap<(AttestationData, BitList<T::MaxValidatorsPerCommittee>), IndexedAttestation<T>>,
|
||||
@@ -25,6 +29,7 @@ pub struct ConsensusContext<T: EthSpec> {
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ContextError {
|
||||
BeaconState(BeaconStateError),
|
||||
EpochCache(EpochCacheError),
|
||||
SlotMismatch { slot: Slot, expected: Slot },
|
||||
EpochMismatch { epoch: Epoch, expected: Epoch },
|
||||
}
|
||||
@@ -35,12 +40,19 @@ impl From<BeaconStateError> for ContextError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EpochCacheError> for ContextError {
|
||||
fn from(e: EpochCacheError) -> Self {
|
||||
Self::EpochCache(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> ConsensusContext<T> {
|
||||
pub fn new(slot: Slot) -> Self {
|
||||
Self {
|
||||
slot,
|
||||
proposer_index: None,
|
||||
current_block_root: None,
|
||||
epoch_cache: None,
|
||||
indexed_attestations: HashMap::new(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
@@ -133,6 +145,31 @@ impl<T: EthSpec> ConsensusContext<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_epoch_cache(mut self, epoch_cache: EpochCache) -> Self {
|
||||
self.epoch_cache = Some(epoch_cache);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_base_reward(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
validator_index: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, ContextError> {
|
||||
self.check_slot(state.slot())?;
|
||||
|
||||
// Build epoch cache if not already built.
|
||||
let epoch_cache = if let Some(ref cache) = self.epoch_cache {
|
||||
Cow::Borrowed(cache)
|
||||
} else {
|
||||
let cache = EpochCache::new(state, spec)?;
|
||||
self.epoch_cache = Some(cache.clone());
|
||||
Cow::Owned(cache)
|
||||
};
|
||||
|
||||
Ok(epoch_cache.get_base_reward(validator_index)?)
|
||||
}
|
||||
|
||||
pub fn get_indexed_attestation(
|
||||
&mut self,
|
||||
state: &BeaconState<T>,
|
||||
|
||||
137
consensus/state_processing/src/epoch_cache.rs
Normal file
137
consensus/state_processing/src/epoch_cache.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use crate::common::{
|
||||
altair::{self, BaseRewardPerIncrement},
|
||||
base::{self, SqrtTotalActiveBalance},
|
||||
};
|
||||
use safe_arith::ArithError;
|
||||
use std::sync::Arc;
|
||||
use types::{BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256, Slot};
|
||||
|
||||
/// Cache of values which are uniquely determined at the start of an epoch.
|
||||
///
|
||||
/// The values are fixed with respect to the last block of the _prior_ epoch, which we refer
|
||||
/// to as the "decision block". This cache is very similar to the `BeaconProposerCache` in that
|
||||
/// beacon proposers are determined at exactly the same time as the values in this cache, so
|
||||
/// the keys for the two caches are identical.
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct EpochCache {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
struct Inner {
|
||||
/// Unique identifier for this cache, which can be used to check its validity before use
|
||||
/// with any `BeaconState`.
|
||||
key: EpochCacheKey,
|
||||
/// Base reward for every validator in this epoch.
|
||||
base_rewards: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub struct EpochCacheKey {
|
||||
pub epoch: Epoch,
|
||||
pub decision_block_root: Hash256,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum EpochCacheError {
|
||||
IncorrectEpoch { cache: Epoch, state: Epoch },
|
||||
IncorrectDecisionBlock { cache: Hash256, state: Hash256 },
|
||||
ValidatorIndexOutOfBounds { validator_index: usize },
|
||||
InvalidSlot { slot: Slot },
|
||||
Arith(ArithError),
|
||||
BeaconState(BeaconStateError),
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for EpochCacheError {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
Self::BeaconState(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for EpochCacheError {
|
||||
fn from(e: ArithError) -> Self {
|
||||
Self::Arith(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl EpochCache {
|
||||
pub fn new<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, EpochCacheError> {
|
||||
let epoch = state.current_epoch();
|
||||
let decision_block_root = state
|
||||
.proposer_shuffling_decision_root(Hash256::zero())
|
||||
.map_err(EpochCacheError::BeaconState)?;
|
||||
|
||||
// The cache should never be constructed at slot 0 because it should only be used for
|
||||
// block processing (which implies slot > 0) or epoch processing (which implies slot >= 32).
|
||||
/* FIXME(sproul): EF tests like this
|
||||
if decision_block_root.is_zero() {
|
||||
return Err(EpochCacheError::InvalidSlot { slot: state.slot() });
|
||||
}
|
||||
*/
|
||||
|
||||
// Compute base rewards.
|
||||
let total_active_balance = state.get_total_active_balance()?;
|
||||
let sqrt_total_active_balance = SqrtTotalActiveBalance::new(total_active_balance);
|
||||
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
|
||||
|
||||
let mut base_rewards = Vec::with_capacity(state.validators().len());
|
||||
|
||||
for validator in state.validators().iter() {
|
||||
let effective_balance = validator.effective_balance();
|
||||
|
||||
let base_reward = if spec
|
||||
.altair_fork_epoch
|
||||
.map_or(false, |altair_epoch| epoch < altair_epoch)
|
||||
{
|
||||
base::get_base_reward(effective_balance, sqrt_total_active_balance, spec)?
|
||||
} else {
|
||||
altair::get_base_reward(effective_balance, base_reward_per_increment, spec)?
|
||||
};
|
||||
base_rewards.push(base_reward);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
inner: Arc::new(Inner {
|
||||
key: EpochCacheKey {
|
||||
epoch,
|
||||
decision_block_root,
|
||||
},
|
||||
base_rewards,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn check_validity<E: EthSpec>(
|
||||
&self,
|
||||
state: &BeaconState<E>,
|
||||
) -> Result<(), EpochCacheError> {
|
||||
if self.inner.key.epoch != state.current_epoch() {
|
||||
return Err(EpochCacheError::IncorrectEpoch {
|
||||
cache: self.inner.key.epoch,
|
||||
state: state.current_epoch(),
|
||||
});
|
||||
}
|
||||
let state_decision_root = state
|
||||
.proposer_shuffling_decision_root(Hash256::zero())
|
||||
.map_err(EpochCacheError::BeaconState)?;
|
||||
if self.inner.key.decision_block_root != state_decision_root {
|
||||
return Err(EpochCacheError::IncorrectDecisionBlock {
|
||||
cache: self.inner.key.decision_block_root,
|
||||
state: state_decision_root,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_base_reward(&self, validator_index: usize) -> Result<u64, EpochCacheError> {
|
||||
self.inner
|
||||
.base_rewards
|
||||
.get(validator_index)
|
||||
.copied()
|
||||
.ok_or(EpochCacheError::ValidatorIndexOutOfBounds { validator_index })
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
|
||||
let mut state = BeaconState::new(genesis_time, eth1_data, spec);
|
||||
|
||||
// Seed RANDAO with Eth1 entropy
|
||||
state.fill_randao_mixes_with(eth1_block_hash);
|
||||
state.fill_randao_mixes_with(eth1_block_hash)?;
|
||||
|
||||
let mut deposit_tree = DepositDataTree::create(&[], 0, DEPOSIT_TREE_DEPTH);
|
||||
|
||||
@@ -116,18 +116,20 @@ pub fn process_activations<T: EthSpec>(
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
let (validators, balances) = state.validators_and_balances_mut();
|
||||
for (index, validator) in validators.iter_mut().enumerate() {
|
||||
let mut validators_iter = validators.iter_cow();
|
||||
while let Some((index, validator)) = validators_iter.next_cow() {
|
||||
let validator = validator.to_mut();
|
||||
let balance = balances
|
||||
.get(index)
|
||||
.copied()
|
||||
.ok_or(Error::BalancesOutOfBounds(index))?;
|
||||
validator.effective_balance = std::cmp::min(
|
||||
validator.mutable.effective_balance = std::cmp::min(
|
||||
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
if validator.effective_balance == spec.max_effective_balance {
|
||||
validator.activation_eligibility_epoch = T::genesis_epoch();
|
||||
validator.activation_epoch = T::genesis_epoch();
|
||||
if validator.effective_balance() == spec.max_effective_balance {
|
||||
validator.mutable.activation_eligibility_epoch = T::genesis_epoch();
|
||||
validator.mutable.activation_epoch = T::genesis_epoch();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -19,6 +19,7 @@ mod metrics;
|
||||
pub mod block_replayer;
|
||||
pub mod common;
|
||||
pub mod consensus_context;
|
||||
pub mod epoch_cache;
|
||||
pub mod genesis;
|
||||
pub mod per_block_processing;
|
||||
pub mod per_epoch_processing;
|
||||
@@ -29,6 +30,7 @@ pub mod verify_operation;
|
||||
|
||||
pub use block_replayer::{BlockReplayError, BlockReplayer, StateProcessingStrategy};
|
||||
pub use consensus_context::{ConsensusContext, ContextError};
|
||||
pub use epoch_cache::{EpochCache, EpochCacheError, EpochCacheKey};
|
||||
pub use genesis::{
|
||||
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
|
||||
process_activations,
|
||||
|
||||
@@ -23,4 +23,11 @@ lazy_static! {
|
||||
"beacon_participation_prev_epoch_active_gwei_total",
|
||||
"Total effective balance (gwei) of validators active in the previous epoch"
|
||||
);
|
||||
/*
|
||||
* Processing metrics
|
||||
*/
|
||||
pub static ref PROCESS_EPOCH_TIME: Result<Histogram> = try_create_histogram(
|
||||
"beacon_state_processing_process_epoch",
|
||||
"Time required for process_epoch",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ pub fn process_block_header<T: EthSpec>(
|
||||
|
||||
// Verify proposer is not slashed
|
||||
verify!(
|
||||
!state.get_validator(proposer_index as usize)?.slashed,
|
||||
!state.get_validator(proposer_index as usize)?.slashed(),
|
||||
HeaderInvalid::ProposerSlashed(proposer_index)
|
||||
);
|
||||
|
||||
|
||||
@@ -47,17 +47,20 @@ pub fn process_sync_aggregate<T: EthSpec>(
|
||||
// Apply participant and proposer rewards
|
||||
let committee_indices = state.get_sync_committee_indices(¤t_sync_committee)?;
|
||||
|
||||
let mut total_proposer_reward = 0;
|
||||
for (participant_index, participation_bit) in committee_indices
|
||||
.into_iter()
|
||||
.zip(aggregate.sync_committee_bits.iter())
|
||||
{
|
||||
// FIXME(sproul): double-check this for Capella, proposer shouldn't have 0 effective balance
|
||||
if participation_bit {
|
||||
increase_balance(state, participant_index, participant_reward)?;
|
||||
increase_balance(state, proposer_index as usize, proposer_reward)?;
|
||||
total_proposer_reward.safe_add_assign(proposer_reward)?;
|
||||
} else {
|
||||
decrease_balance(state, participant_index, participant_reward)?;
|
||||
}
|
||||
}
|
||||
increase_balance(state, proposer_index as usize, total_proposer_reward)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::signature_sets::Error as SignatureSetError;
|
||||
use crate::ContextError;
|
||||
use crate::{ContextError, EpochCacheError};
|
||||
use merkle_proof::MerkleTreeError;
|
||||
use safe_arith::ArithError;
|
||||
use ssz::DecodeError;
|
||||
@@ -78,6 +78,8 @@ pub enum BlockProcessingError {
|
||||
},
|
||||
ExecutionInvalid,
|
||||
ConsensusContext(ContextError),
|
||||
MilhouseError(milhouse::Error),
|
||||
EpochCacheError(EpochCacheError),
|
||||
WithdrawalsRootMismatch {
|
||||
expected: Hash256,
|
||||
found: Hash256,
|
||||
@@ -127,6 +129,18 @@ impl From<ContextError> for BlockProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EpochCacheError> for BlockProcessingError {
|
||||
fn from(e: EpochCacheError) -> Self {
|
||||
BlockProcessingError::EpochCacheError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<milhouse::Error> for BlockProcessingError {
|
||||
fn from(e: milhouse::Error) -> Self {
|
||||
Self::MilhouseError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
|
||||
match e {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use super::*;
|
||||
use crate::common::{
|
||||
altair::{get_base_reward, BaseRewardPerIncrement},
|
||||
get_attestation_participation_flag_indices, increase_balance, initiate_validator_exit,
|
||||
slash_validator,
|
||||
};
|
||||
use crate::per_block_processing::errors::{BlockProcessingError, IntoWithIndex};
|
||||
use crate::VerifySignatures;
|
||||
use safe_arith::SafeArith;
|
||||
use std::sync::Arc;
|
||||
use types::consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR};
|
||||
|
||||
pub fn process_operations<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
@@ -126,7 +126,7 @@ pub mod altair {
|
||||
|
||||
let proposer_index = ctxt.get_proposer_index(state, spec)?;
|
||||
|
||||
let attesting_indices = &verify_attestation_for_block_inclusion(
|
||||
let attesting_indices = verify_attestation_for_block_inclusion(
|
||||
state,
|
||||
attestation,
|
||||
ctxt,
|
||||
@@ -134,7 +134,8 @@ pub mod altair {
|
||||
spec,
|
||||
)
|
||||
.map_err(|e| e.into_with_index(att_index))?
|
||||
.attesting_indices;
|
||||
.attesting_indices
|
||||
.clone();
|
||||
|
||||
// Matching roots, participation flag indices
|
||||
let data = &attestation.data;
|
||||
@@ -143,10 +144,8 @@ pub mod altair {
|
||||
get_attestation_participation_flag_indices(state, data, inclusion_delay, spec)?;
|
||||
|
||||
// Update epoch participation flags.
|
||||
let total_active_balance = state.get_total_active_balance()?;
|
||||
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
|
||||
let mut proposer_reward_numerator = 0;
|
||||
for index in attesting_indices {
|
||||
for index in &attesting_indices {
|
||||
let index = *index as usize;
|
||||
|
||||
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
|
||||
@@ -160,8 +159,7 @@ pub mod altair {
|
||||
{
|
||||
validator_participation.add_flag(flag_index)?;
|
||||
proposer_reward_numerator.safe_add_assign(
|
||||
get_base_reward(state, index, base_reward_per_increment, spec)?
|
||||
.safe_mul(weight)?,
|
||||
ctxt.get_base_reward(state, index, spec)?.safe_mul(weight)?,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -392,17 +390,19 @@ pub fn process_deposit<T: EthSpec>(
|
||||
|
||||
// Create a new validator.
|
||||
let validator = Validator {
|
||||
pubkey: deposit.data.pubkey,
|
||||
withdrawal_credentials: deposit.data.withdrawal_credentials,
|
||||
activation_eligibility_epoch: spec.far_future_epoch,
|
||||
activation_epoch: spec.far_future_epoch,
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
effective_balance: std::cmp::min(
|
||||
amount.safe_sub(amount.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
),
|
||||
slashed: false,
|
||||
pubkey: Arc::new(deposit.data.pubkey),
|
||||
mutable: ValidatorMutable {
|
||||
withdrawal_credentials: deposit.data.withdrawal_credentials,
|
||||
activation_eligibility_epoch: spec.far_future_epoch,
|
||||
activation_epoch: spec.far_future_epoch,
|
||||
exit_epoch: spec.far_future_epoch,
|
||||
withdrawable_epoch: spec.far_future_epoch,
|
||||
effective_balance: std::cmp::min(
|
||||
amount.safe_sub(amount.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
),
|
||||
slashed: false,
|
||||
},
|
||||
};
|
||||
state.validators_mut().push(validator)?;
|
||||
state.balances_mut().push(deposit.data.amount)?;
|
||||
|
||||
@@ -64,7 +64,7 @@ where
|
||||
.validators()
|
||||
.get(validator_index)
|
||||
.and_then(|v| {
|
||||
let pk: Option<PublicKey> = v.pubkey.decompress().ok();
|
||||
let pk: Option<PublicKey> = v.pubkey().decompress().ok();
|
||||
pk
|
||||
})
|
||||
.map(Cow::Owned)
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn verify_bls_to_execution_change<T: EthSpec>(
|
||||
|
||||
verify!(
|
||||
validator
|
||||
.withdrawal_credentials
|
||||
.withdrawal_credentials()
|
||||
.as_bytes()
|
||||
.first()
|
||||
.map(|byte| *byte == spec.bls_withdrawal_prefix_byte)
|
||||
@@ -41,7 +41,7 @@ pub fn verify_bls_to_execution_change<T: EthSpec>(
|
||||
// future.
|
||||
let pubkey_hash = hash(address_change.from_bls_pubkey.as_serialized());
|
||||
verify!(
|
||||
validator.withdrawal_credentials.as_bytes().get(1..) == pubkey_hash.get(1..),
|
||||
validator.withdrawal_credentials().as_bytes().get(1..) == pubkey_hash.get(1..),
|
||||
Invalid::WithdrawalCredentialsMismatch
|
||||
);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn verify_exit<T: EthSpec>(
|
||||
|
||||
// Verify that the validator has not yet exited.
|
||||
verify!(
|
||||
validator.exit_epoch == spec.far_future_epoch,
|
||||
validator.exit_epoch() == spec.far_future_epoch,
|
||||
ExitInvalid::AlreadyExited(exit.validator_index)
|
||||
);
|
||||
|
||||
@@ -56,7 +56,7 @@ pub fn verify_exit<T: EthSpec>(
|
||||
|
||||
// Verify the validator has been active long enough.
|
||||
let earliest_exit_epoch = validator
|
||||
.activation_epoch
|
||||
.activation_epoch()
|
||||
.safe_add(spec.shard_committee_period)?;
|
||||
verify!(
|
||||
current_epoch >= earliest_exit_epoch,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![deny(clippy::wildcard_imports)]
|
||||
|
||||
use crate::metrics;
|
||||
pub use epoch_processing_summary::EpochProcessingSummary;
|
||||
use errors::EpochProcessingError as Error;
|
||||
pub use justification_and_finalization_state::JustificationAndFinalizationState;
|
||||
@@ -32,6 +33,8 @@ pub fn process_epoch<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<EpochProcessingSummary<T>, Error> {
|
||||
let _timer = metrics::start_timer(&metrics::PROCESS_EPOCH_TIME);
|
||||
|
||||
// Verify that the `BeaconState` instantiation matches the fork at `state.slot()`.
|
||||
state
|
||||
.fork_name(spec)
|
||||
|
||||
@@ -29,7 +29,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
state.build_committee_cache(RelativeEpoch::Next, spec)?;
|
||||
|
||||
// Pre-compute participating indices and total balances.
|
||||
let participation_cache = ParticipationCache::new(state, spec)?;
|
||||
let mut participation_cache = ParticipationCache::new(state, spec)?;
|
||||
let sync_committee = state.current_sync_committee()?.clone();
|
||||
|
||||
// Justification and finalization.
|
||||
@@ -37,7 +37,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
process_justification_and_finalization(state, &participation_cache)?;
|
||||
justification_and_finalization_state.apply_changes_to_state(state);
|
||||
|
||||
process_inactivity_updates(state, &participation_cache, spec)?;
|
||||
process_inactivity_updates(state, &mut participation_cache, spec)?;
|
||||
|
||||
// Rewards and Penalties.
|
||||
process_rewards_and_penalties(state, &participation_cache, spec)?;
|
||||
@@ -48,6 +48,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
// Slashings.
|
||||
process_slashings(
|
||||
state,
|
||||
Some(participation_cache.process_slashings_indices()),
|
||||
participation_cache.current_epoch_total_active_balance(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use super::ParticipationCache;
|
||||
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;
|
||||
@@ -11,7 +9,7 @@ use types::eth_spec::EthSpec;
|
||||
|
||||
pub fn process_inactivity_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
participation_cache: &ParticipationCache,
|
||||
participation_cache: &mut ParticipationCache,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), EpochProcessingError> {
|
||||
let previous_epoch = state.previous_epoch();
|
||||
@@ -20,24 +18,50 @@ pub fn process_inactivity_updates<T: EthSpec>(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let unslashed_indices = participation_cache
|
||||
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, state.previous_epoch())?;
|
||||
// Fast path: inactivity scores have already been pre-computed.
|
||||
if let Some(inactivity_score_updates) = participation_cache.inactivity_score_updates.take() {
|
||||
// We need to flush the existing inactivity scores in case tree hashing hasn't happened in
|
||||
// a long time (e.g. during state reconstruction).
|
||||
// FIXME(sproul): re-think this
|
||||
state.inactivity_scores_mut()?.apply_updates()?;
|
||||
state
|
||||
.inactivity_scores_mut()?
|
||||
.bulk_update(inactivity_score_updates)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let is_in_inactivity_leak = state.is_in_inactivity_leak(previous_epoch, spec);
|
||||
|
||||
let mut inactivity_scores = state.inactivity_scores_mut()?.iter_cow();
|
||||
|
||||
while let Some((index, inactivity_score)) = inactivity_scores.next_cow() {
|
||||
let validator = match participation_cache.get_validator(index) {
|
||||
Ok(val) if val.is_eligible => val,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let inactivity_score_mut;
|
||||
|
||||
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))?;
|
||||
if validator.is_unslashed_participating_index(TIMELY_TARGET_FLAG_INDEX)? {
|
||||
// Avoid mutating when the inactivity score is 0 and can't go any lower -- the common
|
||||
// case.
|
||||
if *inactivity_score == 0 {
|
||||
continue;
|
||||
}
|
||||
inactivity_score_mut = inactivity_score.to_mut();
|
||||
inactivity_score_mut.safe_sub_assign(1)?;
|
||||
} else {
|
||||
state
|
||||
.get_inactivity_score_mut(index)?
|
||||
.safe_add_assign(spec.inactivity_score_bias)?;
|
||||
inactivity_score_mut = inactivity_score.to_mut();
|
||||
inactivity_score_mut.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))?;
|
||||
if !is_in_inactivity_leak {
|
||||
inactivity_score_mut.safe_sub_assign(min(
|
||||
spec.inactivity_score_recovery_rate,
|
||||
*inactivity_score_mut,
|
||||
))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -4,7 +4,6 @@ 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.
|
||||
@@ -18,15 +17,9 @@ pub fn process_justification_and_finalization<T: EthSpec>(
|
||||
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()?;
|
||||
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(
|
||||
justification_and_finalization_state,
|
||||
total_active_balance,
|
||||
|
||||
@@ -11,19 +11,40 @@
|
||||
//! 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, BaseRewardPerIncrement};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use types::milhouse::update_map::{MaxMap, UpdateMap};
|
||||
use types::{
|
||||
consts::altair::{
|
||||
NUM_FLAG_INDICES, TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX,
|
||||
TIMELY_TARGET_FLAG_INDEX,
|
||||
},
|
||||
BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, ParticipationFlags, RelativeEpoch,
|
||||
Unsigned, Validator,
|
||||
};
|
||||
use vec_map::VecMap;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
InvalidFlagIndex(usize),
|
||||
NoUnslashedParticipatingIndices,
|
||||
MissingValidator(usize),
|
||||
BeaconState(BeaconStateError),
|
||||
Arith(ArithError),
|
||||
InvalidValidatorIndex(usize),
|
||||
InconsistentTotalActiveBalance { cached: u64, computed: u64 },
|
||||
}
|
||||
|
||||
impl From<BeaconStateError> for Error {
|
||||
fn from(e: BeaconStateError) -> Self {
|
||||
Self::BeaconState(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithError> for Error {
|
||||
fn from(e: ArithError) -> Self {
|
||||
Self::Arith(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// A balance which will never be below the specified `minimum`.
|
||||
@@ -55,16 +76,6 @@ impl Balance {
|
||||
/// Caches the participation values for one epoch (either the previous or current).
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct SingleEpochParticipationCache {
|
||||
/// Maps an active validator index to their participation flags.
|
||||
///
|
||||
/// To reiterate, only active and unslashed validator indices are stored in this map.
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// It would be ideal to maintain a reference to the `BeaconState` here rather than copying the
|
||||
/// `ParticipationFlags`, however that would cause us to run into mutable reference limitations
|
||||
/// upstream.
|
||||
unslashed_participating_indices: Vec<Option<ParticipationFlags>>,
|
||||
/// Stores the sum of the balances for all validators in `self.unslashed_participating_indices`
|
||||
/// for all flags in `NUM_FLAG_INDICES`.
|
||||
///
|
||||
@@ -76,12 +87,10 @@ struct SingleEpochParticipationCache {
|
||||
}
|
||||
|
||||
impl SingleEpochParticipationCache {
|
||||
fn new<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSpec) -> Self {
|
||||
let num_validators = state.validators().len();
|
||||
fn new(spec: &ChainSpec) -> Self {
|
||||
let zero_balance = Balance::zero(spec.effective_balance_increment);
|
||||
|
||||
Self {
|
||||
unslashed_participating_indices: vec![None; num_validators],
|
||||
total_flag_balances: [zero_balance; NUM_FLAG_INDICES],
|
||||
total_active_balance: zero_balance,
|
||||
}
|
||||
@@ -95,76 +104,41 @@ impl SingleEpochParticipationCache {
|
||||
.ok_or(Error::InvalidFlagIndex(flag_index))
|
||||
}
|
||||
|
||||
/// Returns `true` if `val_index` is active, unslashed and has `flag_index` set.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// May return an error if `flag_index` is out-of-bounds.
|
||||
fn has_flag(&self, val_index: usize, flag_index: usize) -> Result<bool, Error> {
|
||||
let participation_flags = self
|
||||
.unslashed_participating_indices
|
||||
.get(val_index)
|
||||
.ok_or(Error::InvalidValidatorIndex(val_index))?;
|
||||
if let Some(participation_flags) = participation_flags {
|
||||
participation_flags
|
||||
.has_flag(flag_index)
|
||||
.map_err(|_| Error::InvalidFlagIndex(flag_index))
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Process an **active** validator, reading from the `state` with respect to the
|
||||
/// Process an **active** validator, reading from the `epoch_participation` with respect to the
|
||||
/// `relative_epoch`.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// - The provided `state` **must** be Altair. An error will be returned otherwise.
|
||||
/// - An error will be returned if the `val_index` validator is inactive at the given
|
||||
/// `relative_epoch`.
|
||||
fn process_active_validator<T: EthSpec>(
|
||||
fn process_active_validator(
|
||||
&mut self,
|
||||
val_index: usize,
|
||||
state: &BeaconState<T>,
|
||||
validator: &Validator,
|
||||
epoch_participation: &ParticipationFlags,
|
||||
current_epoch: Epoch,
|
||||
relative_epoch: RelativeEpoch,
|
||||
) -> Result<(), BeaconStateError> {
|
||||
let val_balance = state.get_effective_balance(val_index)?;
|
||||
let validator = state.get_validator(val_index)?;
|
||||
|
||||
// Sanity check to ensure the validator is active.
|
||||
let epoch = relative_epoch.into_epoch(current_epoch);
|
||||
if !validator.is_active_at(epoch) {
|
||||
return Err(BeaconStateError::ValidatorIsInactive { val_index });
|
||||
}
|
||||
|
||||
let epoch_participation = match relative_epoch {
|
||||
RelativeEpoch::Current => state.current_epoch_participation(),
|
||||
RelativeEpoch::Previous => state.previous_epoch_participation(),
|
||||
_ => Err(BeaconStateError::EpochOutOfBounds),
|
||||
}?
|
||||
.get(val_index)
|
||||
.ok_or(BeaconStateError::ParticipationOutOfBounds(val_index))?;
|
||||
|
||||
// All active validators increase the total active balance.
|
||||
self.total_active_balance.safe_add_assign(val_balance)?;
|
||||
self.total_active_balance
|
||||
.safe_add_assign(validator.effective_balance())?;
|
||||
|
||||
// Only unslashed validators may proceed.
|
||||
if validator.slashed {
|
||||
if validator.slashed() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Add their `ParticipationFlags` to the map.
|
||||
*self
|
||||
.unslashed_participating_indices
|
||||
.get_mut(val_index)
|
||||
.ok_or(BeaconStateError::UnknownValidator(val_index))? = Some(*epoch_participation);
|
||||
|
||||
// Iterate through all the flags and increment the total flag balances for whichever flags
|
||||
// are set for `val_index`.
|
||||
for (flag, balance) in self.total_flag_balances.iter_mut().enumerate() {
|
||||
if epoch_participation.has_flag(flag)? {
|
||||
balance.safe_add_assign(val_balance)?;
|
||||
balance.safe_add_assign(validator.effective_balance())?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +146,43 @@ impl SingleEpochParticipationCache {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ValidatorInfo {
|
||||
pub effective_balance: u64,
|
||||
pub base_reward: u64,
|
||||
pub is_eligible: bool,
|
||||
pub is_slashed: bool,
|
||||
pub is_active_current_epoch: bool,
|
||||
pub is_active_previous_epoch: bool,
|
||||
pub previous_epoch_participation: ParticipationFlags,
|
||||
}
|
||||
|
||||
impl ValidatorInfo {
|
||||
#[inline]
|
||||
pub fn is_unslashed_participating_index(&self, flag_index: usize) -> Result<bool, Error> {
|
||||
Ok(self.is_active_previous_epoch
|
||||
&& !self.is_slashed
|
||||
&& self
|
||||
.previous_epoch_participation
|
||||
.has_flag(flag_index)
|
||||
.map_err(|_| Error::InvalidFlagIndex(flag_index))?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Single `HashMap` for validator info relevant to `process_epoch`.
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct ValidatorInfoCache {
|
||||
info: Vec<Option<ValidatorInfo>>,
|
||||
}
|
||||
|
||||
impl ValidatorInfoCache {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
info: vec![None; capacity],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Maintains a cache to be used during `altair::process_epoch`.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ParticipationCache {
|
||||
@@ -181,8 +192,15 @@ pub struct ParticipationCache {
|
||||
previous_epoch: Epoch,
|
||||
/// Caches information about active validators pertaining to `self.previous_epoch`.
|
||||
previous_epoch_participation: SingleEpochParticipationCache,
|
||||
/// Caches validator information relevant to `process_epoch`.
|
||||
validators: ValidatorInfoCache,
|
||||
/// Caches the result of the `get_eligible_validator_indices` function.
|
||||
eligible_indices: Vec<usize>,
|
||||
/// Caches the indices and effective balances of validators that need to be processed by
|
||||
/// `process_slashings`.
|
||||
process_slashings_indices: Vec<(usize, u64)>,
|
||||
/// Updates to the inactivity scores if we are definitely not in an inactivity leak.
|
||||
pub inactivity_score_updates: Option<MaxMap<VecMap<u64>>>,
|
||||
}
|
||||
|
||||
impl ParticipationCache {
|
||||
@@ -191,17 +209,21 @@ impl ParticipationCache {
|
||||
/// ## Errors
|
||||
///
|
||||
/// - The provided `state` **must** be an Altair state. An error will be returned otherwise.
|
||||
pub fn new<T: EthSpec>(
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Self, BeaconStateError> {
|
||||
pub fn new<T: EthSpec>(state: &BeaconState<T>, spec: &ChainSpec) -> Result<Self, Error> {
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
// Both the current/previous epoch participations are set to a capacity that is slightly
|
||||
// larger than required. The difference will be due slashed-but-active validators.
|
||||
let mut current_epoch_participation = SingleEpochParticipationCache::new(state, spec);
|
||||
let mut previous_epoch_participation = SingleEpochParticipationCache::new(state, spec);
|
||||
let mut current_epoch_participation = SingleEpochParticipationCache::new(spec);
|
||||
let mut previous_epoch_participation = SingleEpochParticipationCache::new(spec);
|
||||
|
||||
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:
|
||||
//
|
||||
// - Active in the previous epoch.
|
||||
@@ -211,6 +233,16 @@ impl ParticipationCache {
|
||||
// reallocations.
|
||||
let mut eligible_indices = Vec::with_capacity(state.validators().len());
|
||||
|
||||
let mut process_slashings_indices = vec![];
|
||||
|
||||
// Fast path for inactivity scores update when we are definitely not in an inactivity leak.
|
||||
// This breaks the dependence of `process_inactivity_updates` on the finalization
|
||||
// re-calculation.
|
||||
let definitely_not_in_inactivity_leak =
|
||||
state.finalized_checkpoint().epoch + spec.min_epochs_to_inactivity_penalty + 1
|
||||
>= state.current_epoch();
|
||||
let mut inactivity_score_updates = MaxMap::default();
|
||||
|
||||
// Iterate through all validators, updating:
|
||||
//
|
||||
// 1. Validator participation for current and previous epochs.
|
||||
@@ -218,30 +250,100 @@ impl ParticipationCache {
|
||||
//
|
||||
// Care is taken to ensure that the ordering of `eligible_indices` is the same as the
|
||||
// `get_eligible_validator_indices` function in the spec.
|
||||
for (val_index, val) in state.validators().iter().enumerate() {
|
||||
if val.is_active_at(current_epoch) {
|
||||
let iter = state
|
||||
.validators()
|
||||
.iter()
|
||||
.zip(state.current_epoch_participation()?)
|
||||
.zip(state.previous_epoch_participation()?)
|
||||
.zip(state.inactivity_scores()?)
|
||||
.enumerate();
|
||||
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(previous_epoch, val);
|
||||
|
||||
if is_active_current_epoch {
|
||||
current_epoch_participation.process_active_validator(
|
||||
val_index,
|
||||
state,
|
||||
val,
|
||||
curr_epoch_flags,
|
||||
current_epoch,
|
||||
RelativeEpoch::Current,
|
||||
)?;
|
||||
}
|
||||
|
||||
if val.is_active_at(previous_epoch) {
|
||||
if is_active_previous_epoch {
|
||||
assert!(is_eligible);
|
||||
|
||||
previous_epoch_participation.process_active_validator(
|
||||
val_index,
|
||||
state,
|
||||
val,
|
||||
prev_epoch_flags,
|
||||
current_epoch,
|
||||
RelativeEpoch::Previous,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Note: a validator might still be "eligible" whilst returning `false` to
|
||||
// `Validator::is_active_at`.
|
||||
if state.is_eligible_validator(previous_epoch, val_index)? {
|
||||
eligible_indices.push(val_index)
|
||||
if val.slashed()
|
||||
&& current_epoch.safe_add(T::EpochsPerSlashingsVector::to_u64().safe_div(2)?)?
|
||||
== val.withdrawable_epoch()
|
||||
{
|
||||
process_slashings_indices.push((val_index, val.effective_balance()));
|
||||
}
|
||||
|
||||
// Note: a validator might still be "eligible" whilst returning `false` to
|
||||
// `Validator::is_active_at`. It's also possible for a validator to be active
|
||||
// in the current epoch without being eligible (if it was just activated).
|
||||
if is_eligible {
|
||||
eligible_indices.push(val_index);
|
||||
}
|
||||
|
||||
let mut validator_info = ValidatorInfo {
|
||||
effective_balance: val.effective_balance(),
|
||||
base_reward: 0, // not read
|
||||
is_eligible,
|
||||
is_slashed: val.slashed(),
|
||||
is_active_current_epoch,
|
||||
is_active_previous_epoch,
|
||||
previous_epoch_participation: *prev_epoch_flags,
|
||||
};
|
||||
|
||||
// Calculate inactivity updates.
|
||||
if is_eligible && definitely_not_in_inactivity_leak {
|
||||
let mut new_inactivity_score =
|
||||
if validator_info.is_unslashed_participating_index(TIMELY_TARGET_FLAG_INDEX)? {
|
||||
inactivity_score.saturating_sub(1)
|
||||
} else {
|
||||
inactivity_score.safe_add(spec.inactivity_score_bias)?
|
||||
};
|
||||
|
||||
// Decrease the score of all validators for forgiveness when not during a leak
|
||||
new_inactivity_score =
|
||||
new_inactivity_score.saturating_sub(spec.inactivity_score_recovery_rate);
|
||||
|
||||
if new_inactivity_score != *inactivity_score {
|
||||
inactivity_score_updates.insert(val_index, new_inactivity_score);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::indexing_slicing)]
|
||||
if is_eligible || is_active_current_epoch {
|
||||
let effective_balance = val.effective_balance();
|
||||
let base_reward =
|
||||
get_base_reward(effective_balance, base_reward_per_increment, spec)?;
|
||||
validator_info.base_reward = base_reward;
|
||||
validators.info[val_index] = Some(validator_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check total active balance.
|
||||
if current_epoch_participation.total_active_balance.get()
|
||||
!= current_epoch_total_active_balance
|
||||
{
|
||||
return Err(Error::InconsistentTotalActiveBalance {
|
||||
cached: current_epoch_total_active_balance,
|
||||
computed: current_epoch_participation.total_active_balance.get(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
@@ -249,7 +351,11 @@ impl ParticipationCache {
|
||||
current_epoch_participation,
|
||||
previous_epoch,
|
||||
previous_epoch_participation,
|
||||
validators,
|
||||
eligible_indices,
|
||||
process_slashings_indices,
|
||||
inactivity_score_updates: definitely_not_in_inactivity_leak
|
||||
.then_some(inactivity_score_updates),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -258,24 +364,8 @@ impl ParticipationCache {
|
||||
&self.eligible_indices
|
||||
}
|
||||
|
||||
/// Equivalent to the `get_unslashed_participating_indices` function in the specification.
|
||||
pub fn get_unslashed_participating_indices(
|
||||
&self,
|
||||
flag_index: usize,
|
||||
epoch: Epoch,
|
||||
) -> Result<UnslashedParticipatingIndices, BeaconStateError> {
|
||||
let participation = if epoch == self.current_epoch {
|
||||
&self.current_epoch_participation
|
||||
} else if epoch == self.previous_epoch {
|
||||
&self.previous_epoch_participation
|
||||
} else {
|
||||
return Err(BeaconStateError::EpochOutOfBounds);
|
||||
};
|
||||
|
||||
Ok(UnslashedParticipatingIndices {
|
||||
participation,
|
||||
flag_index,
|
||||
})
|
||||
pub fn process_slashings_indices(&mut self) -> Vec<(usize, u64)> {
|
||||
std::mem::take(&mut self.process_slashings_indices)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -296,51 +386,63 @@ impl ParticipationCache {
|
||||
}
|
||||
|
||||
pub fn previous_epoch_target_attesting_balance(&self) -> Result<u64, Error> {
|
||||
self.previous_epoch_participation
|
||||
.total_flag_balance(TIMELY_TARGET_FLAG_INDEX)
|
||||
self.previous_epoch_flag_attesting_balance(TIMELY_TARGET_FLAG_INDEX)
|
||||
}
|
||||
|
||||
pub fn previous_epoch_source_attesting_balance(&self) -> Result<u64, Error> {
|
||||
self.previous_epoch_participation
|
||||
.total_flag_balance(TIMELY_SOURCE_FLAG_INDEX)
|
||||
self.previous_epoch_flag_attesting_balance(TIMELY_SOURCE_FLAG_INDEX)
|
||||
}
|
||||
|
||||
pub fn previous_epoch_head_attesting_balance(&self) -> Result<u64, Error> {
|
||||
self.previous_epoch_flag_attesting_balance(TIMELY_HEAD_FLAG_INDEX)
|
||||
}
|
||||
|
||||
pub fn previous_epoch_flag_attesting_balance(&self, flag_index: usize) -> Result<u64, Error> {
|
||||
self.previous_epoch_participation
|
||||
.total_flag_balance(TIMELY_HEAD_FLAG_INDEX)
|
||||
.total_flag_balance(flag_index)
|
||||
}
|
||||
|
||||
/*
|
||||
* Active/Unslashed
|
||||
*/
|
||||
|
||||
/// Returns `None` for an unknown `val_index`.
|
||||
pub fn is_active_unslashed_in_previous_epoch(&self, val_index: usize) -> Option<bool> {
|
||||
self.previous_epoch_participation
|
||||
.unslashed_participating_indices
|
||||
.get(val_index)
|
||||
.map(|flags| flags.is_some())
|
||||
pub fn is_active_unslashed_in_previous_epoch(&self, val_index: usize) -> bool {
|
||||
self.get_validator(val_index).map_or(false, |validator| {
|
||||
validator.is_active_previous_epoch && !validator.is_slashed
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `None` for an unknown `val_index`.
|
||||
pub fn is_active_unslashed_in_current_epoch(&self, val_index: usize) -> Option<bool> {
|
||||
self.current_epoch_participation
|
||||
.unslashed_participating_indices
|
||||
pub fn is_active_unslashed_in_current_epoch(&self, val_index: usize) -> bool {
|
||||
self.get_validator(val_index).map_or(false, |validator| {
|
||||
validator.is_active_current_epoch && !validator.is_slashed
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_validator(&self, val_index: usize) -> Result<&ValidatorInfo, Error> {
|
||||
self.validators
|
||||
.info
|
||||
.get(val_index)
|
||||
.map(|flags| flags.is_some())
|
||||
.ok_or(Error::MissingValidator(val_index))?
|
||||
.as_ref()
|
||||
.ok_or(Error::MissingValidator(val_index))
|
||||
}
|
||||
|
||||
/*
|
||||
* Flags
|
||||
*/
|
||||
|
||||
/// Always returns false for a slashed validator.
|
||||
pub fn is_previous_epoch_timely_source_attester(
|
||||
&self,
|
||||
val_index: usize,
|
||||
) -> Result<bool, Error> {
|
||||
self.previous_epoch_participation
|
||||
.has_flag(val_index, TIMELY_SOURCE_FLAG_INDEX)
|
||||
self.get_validator(val_index)
|
||||
.map_or(Ok(false), |validator| {
|
||||
Ok(!validator.is_slashed
|
||||
&& validator
|
||||
.previous_epoch_participation
|
||||
.has_flag(TIMELY_SOURCE_FLAG_INDEX)
|
||||
.map_err(|_| Error::InvalidFlagIndex(TIMELY_SOURCE_FLAG_INDEX))?)
|
||||
})
|
||||
}
|
||||
|
||||
/// Always returns false for a slashed validator.
|
||||
@@ -348,63 +450,35 @@ impl ParticipationCache {
|
||||
&self,
|
||||
val_index: usize,
|
||||
) -> Result<bool, Error> {
|
||||
self.previous_epoch_participation
|
||||
.has_flag(val_index, TIMELY_TARGET_FLAG_INDEX)
|
||||
self.get_validator(val_index)
|
||||
.map_or(Ok(false), |validator| {
|
||||
Ok(!validator.is_slashed
|
||||
&& validator
|
||||
.previous_epoch_participation
|
||||
.has_flag(TIMELY_TARGET_FLAG_INDEX)
|
||||
.map_err(|_| Error::InvalidFlagIndex(TIMELY_TARGET_FLAG_INDEX))?)
|
||||
})
|
||||
}
|
||||
|
||||
/// Always returns false for a slashed validator.
|
||||
pub fn is_previous_epoch_timely_head_attester(&self, val_index: usize) -> Result<bool, Error> {
|
||||
self.previous_epoch_participation
|
||||
.has_flag(val_index, TIMELY_HEAD_FLAG_INDEX)
|
||||
self.get_validator(val_index)
|
||||
.map_or(Ok(false), |validator| {
|
||||
Ok(!validator.is_slashed
|
||||
&& validator
|
||||
.previous_epoch_participation
|
||||
.has_flag(TIMELY_HEAD_FLAG_INDEX)
|
||||
.map_err(|_| Error::InvalidFlagIndex(TIMELY_TARGET_FLAG_INDEX))?)
|
||||
})
|
||||
}
|
||||
|
||||
/// Always returns false for a slashed validator.
|
||||
pub fn is_current_epoch_timely_source_attester(&self, val_index: usize) -> Result<bool, Error> {
|
||||
self.current_epoch_participation
|
||||
.has_flag(val_index, TIMELY_SOURCE_FLAG_INDEX)
|
||||
}
|
||||
|
||||
/// Always returns false for a slashed validator.
|
||||
pub fn is_current_epoch_timely_target_attester(&self, val_index: usize) -> Result<bool, Error> {
|
||||
self.current_epoch_participation
|
||||
.has_flag(val_index, TIMELY_TARGET_FLAG_INDEX)
|
||||
}
|
||||
|
||||
/// Always returns false for a slashed validator.
|
||||
pub fn is_current_epoch_timely_head_attester(&self, val_index: usize) -> Result<bool, Error> {
|
||||
self.current_epoch_participation
|
||||
.has_flag(val_index, TIMELY_HEAD_FLAG_INDEX)
|
||||
}
|
||||
}
|
||||
|
||||
/// Imitates the return value of the `get_unslashed_participating_indices` in the
|
||||
/// specification.
|
||||
///
|
||||
/// This struct exists to help make the Lighthouse code read more like the specification.
|
||||
pub struct UnslashedParticipatingIndices<'a> {
|
||||
participation: &'a SingleEpochParticipationCache,
|
||||
flag_index: usize,
|
||||
}
|
||||
|
||||
impl<'a> UnslashedParticipatingIndices<'a> {
|
||||
/// Returns `Ok(true)` if the given `val_index` is both:
|
||||
///
|
||||
/// - An active validator.
|
||||
/// - Has `self.flag_index` set.
|
||||
pub fn contains(&self, val_index: usize) -> Result<bool, Error> {
|
||||
self.participation.has_flag(val_index, self.flag_index)
|
||||
}
|
||||
|
||||
/// Returns the sum of all balances of validators which have `self.flag_index` set.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// Respects the `EFFECTIVE_BALANCE_INCREMENT` minimum.
|
||||
pub fn total_balance(&self) -> Result<u64, Error> {
|
||||
self.participation
|
||||
.total_flag_balances
|
||||
.get(self.flag_index)
|
||||
.ok_or(Error::InvalidFlagIndex(self.flag_index))
|
||||
.map(Balance::get)
|
||||
pub fn is_current_epoch_timely_target_attester(
|
||||
&self,
|
||||
_val_index: usize,
|
||||
) -> Result<bool, Error> {
|
||||
// FIXME(sproul): decide whether it's worth storing the current epoch participation flags
|
||||
// *just* for this call. Perhaps the validator API could source it from the state directly.
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
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;
|
||||
use types::VList;
|
||||
|
||||
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()
|
||||
])?;
|
||||
*state.current_epoch_participation_mut()? =
|
||||
VList::repeat(ParticipationFlags::default(), state.validators().len())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4,12 +4,9 @@ use types::consts::altair::{
|
||||
PARTICIPATION_FLAG_WEIGHTS, TIMELY_HEAD_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX,
|
||||
WEIGHT_DENOMINATOR,
|
||||
};
|
||||
use types::{BeaconState, ChainSpec, EthSpec};
|
||||
use types::{BeaconState, BeaconStateError, ChainSpec, EthSpec};
|
||||
|
||||
use crate::common::{
|
||||
altair::{get_base_reward, BaseRewardPerIncrement},
|
||||
decrease_balance, increase_balance,
|
||||
};
|
||||
use crate::common::{decrease_balance_directly, increase_balance_directly};
|
||||
use crate::per_epoch_processing::{Delta, Error};
|
||||
|
||||
/// Apply attester and proposer rewards.
|
||||
@@ -43,9 +40,20 @@ pub fn process_rewards_and_penalties<T: EthSpec>(
|
||||
|
||||
// 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)?;
|
||||
let mut balances = state.balances_mut().iter_cow();
|
||||
|
||||
while let Some((i, balance)) = balances.next_cow() {
|
||||
let delta = deltas
|
||||
.get(i)
|
||||
.ok_or(BeaconStateError::BalancesOutOfBounds(i))?;
|
||||
|
||||
if delta.rewards == 0 && delta.penalties == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let balance = balance.to_mut();
|
||||
increase_balance_directly(balance, delta.rewards)?;
|
||||
decrease_balance_directly(balance, delta.penalties)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -62,21 +70,21 @@ pub fn get_flag_index_deltas<T: EthSpec>(
|
||||
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_balance =
|
||||
participation_cache.previous_epoch_flag_attesting_balance(flag_index)?;
|
||||
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)?;
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
for &index in participation_cache.eligible_validator_indices() {
|
||||
let base_reward = get_base_reward(state, index, base_reward_per_increment, spec)?;
|
||||
let validator = participation_cache.get_validator(index)?;
|
||||
let base_reward = validator.base_reward;
|
||||
|
||||
let mut delta = Delta::default();
|
||||
|
||||
if unslashed_participating_indices.contains(index)? {
|
||||
if validator.is_unslashed_participating_index(flag_index)? {
|
||||
if !state.is_in_inactivity_leak(previous_epoch, spec) {
|
||||
let reward_numerator = base_reward
|
||||
.safe_mul(weight)?
|
||||
@@ -110,15 +118,12 @@ pub fn get_inactivity_penalty_deltas<T: EthSpec>(
|
||||
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 validator = participation_cache.get_validator(index)?;
|
||||
let mut delta = Delta::default();
|
||||
|
||||
if !matching_target_indices.contains(index)? {
|
||||
let penalty_numerator = state
|
||||
.get_validator(index)?
|
||||
if !validator.is_unslashed_participating_index(TIMELY_TARGET_FLAG_INDEX)? {
|
||||
let penalty_numerator = validator
|
||||
.effective_balance
|
||||
.safe_mul(state.get_inactivity_score(index)?)?;
|
||||
let penalty_denominator = spec
|
||||
|
||||
@@ -44,6 +44,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
// Slashings.
|
||||
process_slashings(
|
||||
state,
|
||||
None,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::common::{base::get_base_reward, decrease_balance, increase_balance};
|
||||
use crate::common::{
|
||||
base::{get_base_reward, SqrtTotalActiveBalance},
|
||||
decrease_balance, increase_balance,
|
||||
};
|
||||
use crate::per_epoch_processing::{
|
||||
base::{TotalBalances, ValidatorStatus, ValidatorStatuses},
|
||||
Delta, Error,
|
||||
@@ -78,7 +81,6 @@ 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)?
|
||||
@@ -87,17 +89,22 @@ pub fn get_attestation_deltas<T: EthSpec>(
|
||||
let mut deltas = vec![AttestationDelta::default(); state.validators().len()];
|
||||
|
||||
let total_balances = &validator_statuses.total_balances;
|
||||
let sqrt_total_active_balance = SqrtTotalActiveBalance::new(total_balances.current_epoch());
|
||||
|
||||
for (index, validator) in validator_statuses.statuses.iter().enumerate() {
|
||||
// Ignore ineligible validators. All sub-functions of the spec do this except for
|
||||
// `get_inclusion_delay_deltas`. It's safe to do so here because any validator that is in
|
||||
// the unslashed indices of the matching source attestations is active, and therefore
|
||||
// eligible.
|
||||
if !state.is_eligible_validator(previous_epoch, index)? {
|
||||
if !validator.is_eligible {
|
||||
continue;
|
||||
}
|
||||
|
||||
let base_reward = get_base_reward(state, index, total_balances.current_epoch(), spec)?;
|
||||
let base_reward = get_base_reward(
|
||||
validator.current_epoch_effective_balance,
|
||||
sqrt_total_active_balance,
|
||||
spec,
|
||||
)?;
|
||||
|
||||
let source_delta =
|
||||
get_source_delta(validator, base_reward, total_balances, finality_delay, spec)?;
|
||||
|
||||
@@ -53,6 +53,8 @@ impl InclusionInfo {
|
||||
pub struct ValidatorStatus {
|
||||
/// True if the validator has been slashed, ever.
|
||||
pub is_slashed: bool,
|
||||
/// True if the validator is eligible.
|
||||
pub is_eligible: bool,
|
||||
/// True if the validator can withdraw in the current epoch.
|
||||
pub is_withdrawable_in_current_epoch: bool,
|
||||
/// True if the validator was active in the state's _current_ epoch.
|
||||
@@ -92,6 +94,7 @@ impl ValidatorStatus {
|
||||
// Update all the bool fields, only updating `self` if `other` is true (never setting
|
||||
// `self` to false).
|
||||
set_self_if_other_is_true!(self, other, is_slashed);
|
||||
set_self_if_other_is_true!(self, other, is_eligible);
|
||||
set_self_if_other_is_true!(self, other, is_withdrawable_in_current_epoch);
|
||||
set_self_if_other_is_true!(self, other, is_active_in_current_epoch);
|
||||
set_self_if_other_is_true!(self, other, is_active_in_previous_epoch);
|
||||
@@ -195,24 +198,27 @@ impl ValidatorStatuses {
|
||||
let mut statuses = Vec::with_capacity(state.validators().len());
|
||||
let mut total_balances = TotalBalances::new(spec);
|
||||
|
||||
for (i, validator) in state.validators().iter().enumerate() {
|
||||
let effective_balance = state.get_effective_balance(i)?;
|
||||
let current_epoch = state.current_epoch();
|
||||
let previous_epoch = state.previous_epoch();
|
||||
|
||||
for validator in state.validators().iter() {
|
||||
let effective_balance = validator.effective_balance();
|
||||
let mut status = ValidatorStatus {
|
||||
is_slashed: validator.slashed,
|
||||
is_withdrawable_in_current_epoch: validator
|
||||
.is_withdrawable_at(state.current_epoch()),
|
||||
is_slashed: validator.slashed(),
|
||||
is_eligible: state.is_eligible_validator(previous_epoch, validator),
|
||||
is_withdrawable_in_current_epoch: validator.is_withdrawable_at(current_epoch),
|
||||
current_epoch_effective_balance: effective_balance,
|
||||
..ValidatorStatus::default()
|
||||
};
|
||||
|
||||
if validator.is_active_at(state.current_epoch()) {
|
||||
if validator.is_active_at(current_epoch) {
|
||||
status.is_active_in_current_epoch = true;
|
||||
total_balances
|
||||
.current_epoch
|
||||
.safe_add_assign(effective_balance)?;
|
||||
}
|
||||
|
||||
if validator.is_active_at(state.previous_epoch()) {
|
||||
if validator.is_active_at(previous_epoch) {
|
||||
status.is_active_in_previous_epoch = true;
|
||||
total_balances
|
||||
.previous_epoch
|
||||
@@ -285,10 +291,10 @@ impl ValidatorStatuses {
|
||||
}
|
||||
|
||||
// Compute the total balances
|
||||
for (index, v) in self.statuses.iter().enumerate() {
|
||||
for v in self.statuses.iter() {
|
||||
// According to the spec, we only count unslashed validators towards the totals.
|
||||
if !v.is_slashed {
|
||||
let validator_balance = state.get_effective_balance(index)?;
|
||||
let validator_balance = v.current_epoch_effective_balance;
|
||||
|
||||
if v.is_current_epoch_attester {
|
||||
self.total_balances
|
||||
|
||||
@@ -25,7 +25,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
state.build_committee_cache(RelativeEpoch::Next, spec)?;
|
||||
|
||||
// Pre-compute participating indices and total balances.
|
||||
let participation_cache = ParticipationCache::new(state, spec)?;
|
||||
let mut participation_cache = ParticipationCache::new(state, spec)?;
|
||||
let sync_committee = state.current_sync_committee()?.clone();
|
||||
|
||||
// Justification and finalization.
|
||||
@@ -33,7 +33,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
process_justification_and_finalization(state, &participation_cache)?;
|
||||
justification_and_finalization_state.apply_changes_to_state(state);
|
||||
|
||||
process_inactivity_updates(state, &participation_cache, spec)?;
|
||||
process_inactivity_updates(state, &mut participation_cache, spec)?;
|
||||
|
||||
// Rewards and Penalties.
|
||||
process_rewards_and_penalties(state, &participation_cache, spec)?;
|
||||
@@ -44,6 +44,7 @@ pub fn process_epoch<T: EthSpec>(
|
||||
// Slashings.
|
||||
process_slashings(
|
||||
state,
|
||||
Some(participation_cache.process_slashings_indices()),
|
||||
participation_cache.current_epoch_total_active_balance(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
@@ -13,6 +13,9 @@ pub fn process_historical_summaries_update<T: EthSpec>(
|
||||
.safe_rem((T::slots_per_historical_root() as u64).safe_div(T::slots_per_epoch())?)?
|
||||
== 0
|
||||
{
|
||||
// We need to flush any pending mutations before hashing.
|
||||
state.block_roots_mut().apply_updates()?;
|
||||
state.state_roots_mut().apply_updates()?;
|
||||
let summary = HistoricalSummary::new(state);
|
||||
return state
|
||||
.historical_summaries_mut()?
|
||||
|
||||
@@ -8,26 +8,47 @@ pub fn process_effective_balance_updates<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), EpochProcessingError> {
|
||||
// Compute new total active balance for the next epoch as a side-effect of iterating the
|
||||
// effective balances.
|
||||
let next_epoch = state.next_epoch()?;
|
||||
let mut new_total_active_balance = 0;
|
||||
|
||||
let hysteresis_increment = spec
|
||||
.effective_balance_increment
|
||||
.safe_div(spec.hysteresis_quotient)?;
|
||||
let downward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_downward_multiplier)?;
|
||||
let upward_threshold = hysteresis_increment.safe_mul(spec.hysteresis_upward_multiplier)?;
|
||||
let (validators, balances) = state.validators_and_balances_mut();
|
||||
for (index, validator) in validators.iter_mut().enumerate() {
|
||||
let mut validators_iter = validators.iter_cow();
|
||||
|
||||
while let Some((index, validator)) = validators_iter.next_cow() {
|
||||
let balance = balances
|
||||
.get(index)
|
||||
.copied()
|
||||
.ok_or(BeaconStateError::BalancesOutOfBounds(index))?;
|
||||
|
||||
if balance.safe_add(downward_threshold)? < validator.effective_balance
|
||||
|| validator.effective_balance.safe_add(upward_threshold)? < balance
|
||||
let new_effective_balance = if balance.safe_add(downward_threshold)?
|
||||
< validator.effective_balance()
|
||||
|| validator.effective_balance().safe_add(upward_threshold)? < balance
|
||||
{
|
||||
validator.effective_balance = std::cmp::min(
|
||||
std::cmp::min(
|
||||
balance.safe_sub(balance.safe_rem(spec.effective_balance_increment)?)?,
|
||||
spec.max_effective_balance,
|
||||
);
|
||||
)
|
||||
} else {
|
||||
validator.effective_balance()
|
||||
};
|
||||
|
||||
if validator.is_active_at(next_epoch) {
|
||||
new_total_active_balance.safe_add_assign(new_effective_balance)?;
|
||||
}
|
||||
|
||||
if new_effective_balance != validator.effective_balance() {
|
||||
validator.to_mut().mutable.effective_balance = new_effective_balance;
|
||||
}
|
||||
}
|
||||
|
||||
state.set_total_active_balance(next_epoch, new_total_active_balance);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -101,9 +101,7 @@ impl<T: EthSpec> EpochProcessingSummary<T> {
|
||||
EpochProcessingSummary::Altair {
|
||||
participation_cache,
|
||||
..
|
||||
} => participation_cache
|
||||
.is_active_unslashed_in_current_epoch(val_index)
|
||||
.unwrap_or(false),
|
||||
} => participation_cache.is_active_unslashed_in_current_epoch(val_index),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +202,7 @@ impl<T: EthSpec> EpochProcessingSummary<T> {
|
||||
EpochProcessingSummary::Altair {
|
||||
participation_cache,
|
||||
..
|
||||
} => participation_cache
|
||||
.is_active_unslashed_in_previous_epoch(val_index)
|
||||
.unwrap_or(false),
|
||||
} => participation_cache.is_active_unslashed_in_previous_epoch(val_index),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::per_epoch_processing::altair::participation_cache::Error as ParticipationCacheError;
|
||||
use types::{BeaconStateError, InconsistentFork};
|
||||
use types::{milhouse, BeaconStateError, InconsistentFork};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EpochProcessingError {
|
||||
@@ -25,6 +25,7 @@ pub enum EpochProcessingError {
|
||||
InvalidJustificationBit(ssz_types::Error),
|
||||
InvalidFlagIndex(usize),
|
||||
ParticipationCache(ParticipationCacheError),
|
||||
MilhouseError(milhouse::Error),
|
||||
}
|
||||
|
||||
impl From<InclusionError> for EpochProcessingError {
|
||||
@@ -57,6 +58,12 @@ impl From<ParticipationCacheError> for EpochProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<milhouse::Error> for EpochProcessingError {
|
||||
fn from(e: milhouse::Error) -> Self {
|
||||
Self::MilhouseError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InclusionError {
|
||||
/// The validator did not participate in an attestation in this period.
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn process_historical_roots_update<T: EthSpec>(
|
||||
.safe_rem(T::SlotsPerHistoricalRoot::to_u64().safe_div(T::slots_per_epoch())?)?
|
||||
== 0
|
||||
{
|
||||
let historical_batch = state.historical_batch();
|
||||
let historical_batch = state.historical_batch()?;
|
||||
state
|
||||
.historical_roots_mut()
|
||||
.push(historical_batch.tree_hash_root())?;
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
let current_epoch = state.current_epoch();
|
||||
let is_ejectable = |validator: &Validator| {
|
||||
validator.is_active_at(current_epoch)
|
||||
&& validator.effective_balance <= spec.ejection_balance
|
||||
&& validator.effective_balance() <= spec.ejection_balance
|
||||
};
|
||||
let indices_to_update: Vec<_> = state
|
||||
.validators()
|
||||
@@ -32,7 +32,7 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
for index in indices_to_update {
|
||||
let validator = state.get_validator_mut(index)?;
|
||||
if validator.is_eligible_for_activation_queue(spec) {
|
||||
validator.activation_eligibility_epoch = current_epoch.safe_add(1)?;
|
||||
validator.mutable.activation_eligibility_epoch = current_epoch.safe_add(1)?;
|
||||
}
|
||||
if is_ejectable(validator) {
|
||||
initiate_validator_exit(state, index, spec)?;
|
||||
@@ -45,7 +45,7 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, validator)| validator.is_eligible_for_activation(state, spec))
|
||||
.sorted_by_key(|(index, validator)| (validator.activation_eligibility_epoch, *index))
|
||||
.sorted_by_key(|(index, validator)| (validator.activation_eligibility_epoch(), *index))
|
||||
.map(|(index, _)| index)
|
||||
.collect_vec();
|
||||
|
||||
@@ -53,7 +53,7 @@ pub fn process_registry_updates<T: EthSpec>(
|
||||
let churn_limit = state.get_churn_limit(spec)? as usize;
|
||||
let delayed_activation_epoch = state.compute_activation_exit_epoch(current_epoch, spec)?;
|
||||
for index in activation_queue.into_iter().take(churn_limit) {
|
||||
state.get_validator_mut(index)?.activation_epoch = delayed_activation_epoch;
|
||||
state.get_validator_mut(index)?.mutable.activation_epoch = delayed_activation_epoch;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use super::errors::EpochProcessingError;
|
||||
use core::result::Result;
|
||||
use core::result::Result::Ok;
|
||||
use safe_arith::SafeArith;
|
||||
use types::beacon_state::BeaconState;
|
||||
use types::eth_spec::EthSpec;
|
||||
use types::{Unsigned, VariableList};
|
||||
use types::{Unsigned, VList};
|
||||
|
||||
pub fn process_eth1_data_reset<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
@@ -15,7 +13,7 @@ pub fn process_eth1_data_reset<T: EthSpec>(
|
||||
.safe_rem(T::SlotsPerEth1VotingPeriod::to_u64())?
|
||||
== 0
|
||||
{
|
||||
*state.eth1_data_votes_mut() = VariableList::empty();
|
||||
*state.eth1_data_votes_mut() = VList::empty();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::common::decrease_balance;
|
||||
use crate::per_epoch_processing::Error;
|
||||
use safe_arith::{SafeArith, SafeArithIter};
|
||||
use types::{BeaconState, BeaconStateError, ChainSpec, EthSpec, Unsigned};
|
||||
use types::{BeaconState, ChainSpec, EthSpec, Unsigned};
|
||||
|
||||
/// Process slashings.
|
||||
pub fn process_slashings<T: EthSpec>(
|
||||
state: &mut BeaconState<T>,
|
||||
indices: Option<Vec<(usize, u64)>>,
|
||||
total_balance: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
@@ -16,27 +18,30 @@ pub fn process_slashings<T: EthSpec>(
|
||||
total_balance,
|
||||
);
|
||||
|
||||
let (validators, balances) = state.validators_and_balances_mut();
|
||||
for (index, validator) in validators.iter().enumerate() {
|
||||
if validator.slashed
|
||||
&& epoch.safe_add(T::EpochsPerSlashingsVector::to_u64().safe_div(2)?)?
|
||||
== validator.withdrawable_epoch
|
||||
{
|
||||
let increment = spec.effective_balance_increment;
|
||||
let penalty_numerator = validator
|
||||
.effective_balance
|
||||
.safe_div(increment)?
|
||||
.safe_mul(adjusted_total_slashing_balance)?;
|
||||
let penalty = penalty_numerator
|
||||
.safe_div(total_balance)?
|
||||
.safe_mul(increment)?;
|
||||
let target_withdrawable_epoch =
|
||||
epoch.safe_add(T::EpochsPerSlashingsVector::to_u64().safe_div(2)?)?;
|
||||
let indices = indices.unwrap_or_else(|| {
|
||||
state
|
||||
.validators()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, validator)| {
|
||||
validator.slashed() && target_withdrawable_epoch == validator.withdrawable_epoch()
|
||||
})
|
||||
.map(|(index, validator)| (index, validator.effective_balance()))
|
||||
.collect()
|
||||
});
|
||||
|
||||
// Equivalent to `decrease_balance(state, index, penalty)`, but avoids borrowing `state`.
|
||||
let balance = balances
|
||||
.get_mut(index)
|
||||
.ok_or(BeaconStateError::BalancesOutOfBounds(index))?;
|
||||
*balance = balance.saturating_sub(penalty);
|
||||
}
|
||||
for (index, validator_effective_balance) in indices {
|
||||
let increment = spec.effective_balance_increment;
|
||||
let penalty_numerator = validator_effective_balance
|
||||
.safe_div(increment)?
|
||||
.safe_mul(adjusted_total_slashing_balance)?;
|
||||
let penalty = penalty_numerator
|
||||
.safe_div(total_balance)?
|
||||
.safe_mul(increment)?;
|
||||
|
||||
decrease_balance(state, index, penalty)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -59,6 +59,11 @@ pub fn per_slot_processing<T: EthSpec>(
|
||||
if spec.capella_fork_epoch == Some(state.current_epoch()) {
|
||||
upgrade_to_capella(state, spec)?;
|
||||
}
|
||||
|
||||
// Additionally build all caches so that all valid states that are advanced always have
|
||||
// committee caches built, and we don't have to worry about initialising them at higher
|
||||
// layers.
|
||||
state.build_all_caches(spec)?;
|
||||
}
|
||||
|
||||
Ok(summary)
|
||||
|
||||
@@ -3,13 +3,13 @@ use std::mem;
|
||||
use std::sync::Arc;
|
||||
use types::{
|
||||
BeaconState, BeaconStateAltair, BeaconStateError as Error, ChainSpec, EthSpec, Fork,
|
||||
ParticipationFlags, PendingAttestation, RelativeEpoch, SyncCommittee, VariableList,
|
||||
ParticipationFlags, PendingAttestation, RelativeEpoch, SyncCommittee, VList,
|
||||
};
|
||||
|
||||
/// Translate the participation information from the epoch prior to the fork into Altair's format.
|
||||
pub fn translate_participation<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
pending_attestations: &VariableList<PendingAttestation<E>, E::MaxPendingAttestations>,
|
||||
pending_attestations: &VList<PendingAttestation<E>, E::MaxPendingAttestations>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error> {
|
||||
// Previous epoch committee cache is required for `get_attesting_indices`.
|
||||
@@ -50,8 +50,8 @@ pub fn upgrade_to_altair<E: EthSpec>(
|
||||
let pre = pre_state.as_base_mut()?;
|
||||
|
||||
let default_epoch_participation =
|
||||
VariableList::new(vec![ParticipationFlags::default(); pre.validators.len()])?;
|
||||
let inactivity_scores = VariableList::new(vec![0; pre.validators.len()])?;
|
||||
VList::new(vec![ParticipationFlags::default(); pre.validators.len()])?;
|
||||
let inactivity_scores = VList::new(vec![0; pre.validators.len()])?;
|
||||
|
||||
let temp_sync_committee = Arc::new(SyncCommittee::temporary()?);
|
||||
|
||||
@@ -104,7 +104,6 @@ pub fn upgrade_to_altair<E: EthSpec>(
|
||||
committee_caches: mem::take(&mut pre.committee_caches),
|
||||
pubkey_cache: mem::take(&mut pre.pubkey_cache),
|
||||
exit_cache: mem::take(&mut pre.exit_cache),
|
||||
tree_hash_cache: mem::take(&mut pre.tree_hash_cache),
|
||||
});
|
||||
|
||||
// Fill in previous epoch participation from the pre state's pending attestations.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use ssz_types::VariableList;
|
||||
use std::mem;
|
||||
use types::{BeaconState, BeaconStateCapella, BeaconStateError as Error, ChainSpec, EthSpec, Fork};
|
||||
use types::{
|
||||
BeaconState, BeaconStateCapella, BeaconStateError as Error, ChainSpec, EthSpec, Fork, VList,
|
||||
};
|
||||
|
||||
/// Transform a `Merge` state into an `Capella` state.
|
||||
pub fn upgrade_to_capella<E: EthSpec>(
|
||||
@@ -59,13 +60,12 @@ pub fn upgrade_to_capella<E: EthSpec>(
|
||||
// Capella
|
||||
next_withdrawal_index: 0,
|
||||
next_withdrawal_validator_index: 0,
|
||||
historical_summaries: VariableList::default(),
|
||||
historical_summaries: VList::default(),
|
||||
// Caches
|
||||
total_active_balance: pre.total_active_balance,
|
||||
committee_caches: mem::take(&mut pre.committee_caches),
|
||||
pubkey_cache: mem::take(&mut pre.pubkey_cache),
|
||||
exit_cache: mem::take(&mut pre.exit_cache),
|
||||
tree_hash_cache: mem::take(&mut pre.tree_hash_cache),
|
||||
});
|
||||
|
||||
*pre_state = post;
|
||||
|
||||
@@ -63,7 +63,6 @@ pub fn upgrade_to_bellatrix<E: EthSpec>(
|
||||
committee_caches: mem::take(&mut pre.committee_caches),
|
||||
pubkey_cache: mem::take(&mut pre.pubkey_cache),
|
||||
exit_cache: mem::take(&mut pre.exit_cache),
|
||||
tree_hash_cache: mem::take(&mut pre.tree_hash_cache),
|
||||
});
|
||||
|
||||
*pre_state = post;
|
||||
|
||||
Reference in New Issue
Block a user