Implement tree states & hierarchical state DB

This commit is contained in:
Michael Sproul
2023-06-19 10:14:47 +10:00
parent 2bb62b7f7d
commit 23db089a7a
193 changed files with 6093 additions and 5925 deletions

View File

@@ -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)?;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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>,

View 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 })
}
}

View File

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

View File

@@ -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,

View File

@@ -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",
);
}

View File

@@ -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)
);

View File

@@ -47,17 +47,20 @@ pub fn process_sync_aggregate<T: EthSpec>(
// Apply participant and proposer rewards
let committee_indices = state.get_sync_committee_indices(&current_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(())
}

View File

@@ -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 {

View File

@@ -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)?;

View File

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

View File

@@ -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
);

View File

@@ -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,

View File

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

View File

@@ -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,
)?;

View File

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

View File

@@ -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,

View File

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

View File

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

View File

@@ -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

View File

@@ -44,6 +44,7 @@ pub fn process_epoch<T: EthSpec>(
// Slashings.
process_slashings(
state,
None,
validator_statuses.total_balances.current_epoch(),
spec,
)?;

View File

@@ -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)?;

View File

@@ -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

View File

@@ -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,
)?;

View File

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

View File

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

View File

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

View File

@@ -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.

View File

@@ -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())?;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;