mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-07 00:42:42 +00:00
Merge branch 'electra-epoch-proc' of https://github.com/sigp/lighthouse into electra-engine-api
This commit is contained in:
@@ -8,12 +8,12 @@ pub fn initiate_validator_exit<E: EthSpec>(
|
|||||||
index: usize,
|
index: usize,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// We do things in a slightly different order to the spec here. Instead of immediately checking
|
let validator = state.get_validator_cow(index)?;
|
||||||
// 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
|
// Return if the validator already initiated exit
|
||||||
// validator tree (expensive), make the check and mutate as appropriate. Compared to the spec
|
if validator.exit_epoch != spec.far_future_epoch {
|
||||||
// ordering, this saves us from looking up the validator in the validator registry multiple
|
return Ok(());
|
||||||
// times.
|
}
|
||||||
|
|
||||||
// Ensure the exit cache is built.
|
// Ensure the exit cache is built.
|
||||||
state.build_exit_cache(spec)?;
|
state.build_exit_cache(spec)?;
|
||||||
@@ -36,14 +36,7 @@ pub fn initiate_validator_exit<E: EthSpec>(
|
|||||||
exit_queue_epoch
|
exit_queue_epoch
|
||||||
};
|
};
|
||||||
|
|
||||||
let validator = state.get_validator_cow(index)?;
|
let validator = state.get_validator_mut(index)?;
|
||||||
|
|
||||||
// Return if the validator already initiated exit
|
|
||||||
if validator.exit_epoch != spec.far_future_epoch {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let validator = validator.into_mut()?;
|
|
||||||
validator.exit_epoch = exit_queue_epoch;
|
validator.exit_epoch = exit_queue_epoch;
|
||||||
validator.withdrawable_epoch =
|
validator.withdrawable_epoch =
|
||||||
exit_queue_epoch.safe_add(spec.min_validator_withdrawability_delay)?;
|
exit_queue_epoch.safe_add(spec.min_validator_withdrawability_delay)?;
|
||||||
|
|||||||
@@ -86,9 +86,10 @@ impl PreEpochCache {
|
|||||||
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
|
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
|
||||||
|
|
||||||
let effective_balance_increment = spec.effective_balance_increment;
|
let effective_balance_increment = spec.effective_balance_increment;
|
||||||
let max_effective_balance_eth = spec
|
let max_effective_balance =
|
||||||
.max_effective_balance
|
spec.max_effective_balance_for_fork(spec.fork_name_at_epoch(epoch));
|
||||||
.safe_div(effective_balance_increment)?;
|
let max_effective_balance_eth =
|
||||||
|
max_effective_balance.safe_div(effective_balance_increment)?;
|
||||||
|
|
||||||
let mut base_rewards = Vec::with_capacity(max_effective_balance_eth.safe_add(1)? as usize);
|
let mut base_rewards = Vec::with_capacity(max_effective_balance_eth.safe_add(1)? as usize);
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ pub enum EpochProcessingError {
|
|||||||
MilhouseError(milhouse::Error),
|
MilhouseError(milhouse::Error),
|
||||||
EpochCache(EpochCacheError),
|
EpochCache(EpochCacheError),
|
||||||
SinglePassMissingActivationQueue,
|
SinglePassMissingActivationQueue,
|
||||||
|
MissingEarliestExitEpoch,
|
||||||
|
MissingExitBalanceToConsume,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InclusionError> for EpochProcessingError {
|
impl From<InclusionError> for EpochProcessingError {
|
||||||
|
|||||||
@@ -167,6 +167,9 @@ pub fn process_epoch_single_pass<E: EthSpec>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut earliest_exit_epoch = state.earliest_exit_epoch().ok();
|
||||||
|
let mut exit_balance_to_consume = state.exit_balance_to_consume().ok();
|
||||||
|
|
||||||
// Split the state into several disjoint mutable borrows.
|
// Split the state into several disjoint mutable borrows.
|
||||||
let (
|
let (
|
||||||
validators,
|
validators,
|
||||||
@@ -286,6 +289,8 @@ pub fn process_epoch_single_pass<E: EthSpec>(
|
|||||||
exit_cache,
|
exit_cache,
|
||||||
activation_queue_refs,
|
activation_queue_refs,
|
||||||
state_ctxt,
|
state_ctxt,
|
||||||
|
earliest_exit_epoch.as_mut(),
|
||||||
|
exit_balance_to_consume.as_mut(),
|
||||||
spec,
|
spec,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -320,6 +325,17 @@ pub fn process_epoch_single_pass<E: EthSpec>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.registry_updates && fork_name >= ForkName::Electra {
|
||||||
|
if let Ok(earliest_exit_epoch_state) = state.earliest_exit_epoch_mut() {
|
||||||
|
*earliest_exit_epoch_state =
|
||||||
|
earliest_exit_epoch.ok_or(Error::MissingEarliestExitEpoch)?;
|
||||||
|
}
|
||||||
|
if let Ok(exit_balance_to_consume_state) = state.exit_balance_to_consume_mut() {
|
||||||
|
*exit_balance_to_consume_state =
|
||||||
|
exit_balance_to_consume.ok_or(Error::MissingExitBalanceToConsume)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Finish processing pending balance deposits if relevant.
|
// Finish processing pending balance deposits if relevant.
|
||||||
//
|
//
|
||||||
// This *could* be reordered after `process_pending_consolidations` which pushes only to the end
|
// This *could* be reordered after `process_pending_consolidations` which pushes only to the end
|
||||||
@@ -527,12 +543,15 @@ impl RewardsAndPenaltiesContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn process_single_registry_update(
|
fn process_single_registry_update(
|
||||||
validator: &mut Cow<Validator>,
|
validator: &mut Cow<Validator>,
|
||||||
validator_info: &ValidatorInfo,
|
validator_info: &ValidatorInfo,
|
||||||
exit_cache: &mut ExitCache,
|
exit_cache: &mut ExitCache,
|
||||||
activation_queues: Option<(&BTreeSet<usize>, &mut ActivationQueue)>,
|
activation_queues: Option<(&BTreeSet<usize>, &mut ActivationQueue)>,
|
||||||
state_ctxt: &StateContext,
|
state_ctxt: &StateContext,
|
||||||
|
earliest_exit_epoch: Option<&mut Epoch>,
|
||||||
|
exit_balance_to_consume: Option<&mut u64>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if state_ctxt.fork_name < ForkName::Electra {
|
if state_ctxt.fork_name < ForkName::Electra {
|
||||||
@@ -548,7 +567,14 @@ fn process_single_registry_update(
|
|||||||
spec,
|
spec,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
process_single_registry_update_post_electra(validator, exit_cache, state_ctxt, spec)
|
process_single_registry_update_post_electra(
|
||||||
|
validator,
|
||||||
|
exit_cache,
|
||||||
|
state_ctxt,
|
||||||
|
earliest_exit_epoch.ok_or(Error::MissingEarliestExitEpoch)?,
|
||||||
|
exit_balance_to_consume.ok_or(Error::MissingExitBalanceToConsume)?,
|
||||||
|
spec,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +595,7 @@ fn process_single_registry_update_pre_electra(
|
|||||||
|
|
||||||
if validator.is_active_at(current_epoch) && validator.effective_balance <= spec.ejection_balance
|
if validator.is_active_at(current_epoch) && validator.effective_balance <= spec.ejection_balance
|
||||||
{
|
{
|
||||||
initiate_validator_exit(validator, exit_cache, state_ctxt, spec)?;
|
initiate_validator_exit(validator, exit_cache, state_ctxt, None, None, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if activation_queue.contains(&validator_info.index) {
|
if activation_queue.contains(&validator_info.index) {
|
||||||
@@ -592,6 +618,8 @@ fn process_single_registry_update_post_electra(
|
|||||||
validator: &mut Cow<Validator>,
|
validator: &mut Cow<Validator>,
|
||||||
exit_cache: &mut ExitCache,
|
exit_cache: &mut ExitCache,
|
||||||
state_ctxt: &StateContext,
|
state_ctxt: &StateContext,
|
||||||
|
earliest_exit_epoch: &mut Epoch,
|
||||||
|
exit_balance_to_consume: &mut u64,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let current_epoch = state_ctxt.current_epoch;
|
let current_epoch = state_ctxt.current_epoch;
|
||||||
@@ -602,8 +630,14 @@ fn process_single_registry_update_post_electra(
|
|||||||
|
|
||||||
if validator.is_active_at(current_epoch) && validator.effective_balance <= spec.ejection_balance
|
if validator.is_active_at(current_epoch) && validator.effective_balance <= spec.ejection_balance
|
||||||
{
|
{
|
||||||
// TODO(electra): make sure initiate_validator_exit is updated
|
initiate_validator_exit(
|
||||||
initiate_validator_exit(validator, exit_cache, state_ctxt, spec)?;
|
validator,
|
||||||
|
exit_cache,
|
||||||
|
state_ctxt,
|
||||||
|
Some(earliest_exit_epoch),
|
||||||
|
Some(exit_balance_to_consume),
|
||||||
|
spec,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if validator.is_eligible_for_activation_with_finalized_checkpoint(
|
if validator.is_eligible_for_activation_with_finalized_checkpoint(
|
||||||
@@ -621,6 +655,8 @@ fn initiate_validator_exit(
|
|||||||
validator: &mut Cow<Validator>,
|
validator: &mut Cow<Validator>,
|
||||||
exit_cache: &mut ExitCache,
|
exit_cache: &mut ExitCache,
|
||||||
state_ctxt: &StateContext,
|
state_ctxt: &StateContext,
|
||||||
|
earliest_exit_epoch: Option<&mut Epoch>,
|
||||||
|
exit_balance_to_consume: Option<&mut u64>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Return if the validator already initiated exit
|
// Return if the validator already initiated exit
|
||||||
@@ -628,16 +664,27 @@ fn initiate_validator_exit(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute exit queue epoch
|
let exit_queue_epoch = if state_ctxt.fork_name >= ForkName::Electra {
|
||||||
let delayed_epoch = spec.compute_activation_exit_epoch(state_ctxt.current_epoch)?;
|
compute_exit_epoch_and_update_churn(
|
||||||
let mut exit_queue_epoch = exit_cache
|
validator,
|
||||||
.max_epoch()?
|
state_ctxt,
|
||||||
.map_or(delayed_epoch, |epoch| max(epoch, delayed_epoch));
|
earliest_exit_epoch.ok_or(Error::MissingEarliestExitEpoch)?,
|
||||||
let exit_queue_churn = exit_cache.get_churn_at(exit_queue_epoch)?;
|
exit_balance_to_consume.ok_or(Error::MissingExitBalanceToConsume)?,
|
||||||
|
spec,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
// Compute exit queue epoch
|
||||||
|
let delayed_epoch = spec.compute_activation_exit_epoch(state_ctxt.current_epoch)?;
|
||||||
|
let mut exit_queue_epoch = exit_cache
|
||||||
|
.max_epoch()?
|
||||||
|
.map_or(delayed_epoch, |epoch| max(epoch, delayed_epoch));
|
||||||
|
let exit_queue_churn = exit_cache.get_churn_at(exit_queue_epoch)?;
|
||||||
|
|
||||||
if exit_queue_churn >= state_ctxt.churn_limit {
|
if exit_queue_churn >= state_ctxt.churn_limit {
|
||||||
exit_queue_epoch.safe_add_assign(1)?;
|
exit_queue_epoch.safe_add_assign(1)?;
|
||||||
}
|
}
|
||||||
|
exit_queue_epoch
|
||||||
|
};
|
||||||
|
|
||||||
let validator = validator.make_mut()?;
|
let validator = validator.make_mut()?;
|
||||||
validator.exit_epoch = exit_queue_epoch;
|
validator.exit_epoch = exit_queue_epoch;
|
||||||
@@ -648,6 +695,64 @@ fn initiate_validator_exit(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_exit_epoch_and_update_churn(
|
||||||
|
validator: &mut Cow<Validator>,
|
||||||
|
state_ctxt: &StateContext,
|
||||||
|
earliest_exit_epoch_state: &mut Epoch,
|
||||||
|
exit_balance_to_consume_state: &mut u64,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<Epoch, Error> {
|
||||||
|
let exit_balance = validator.effective_balance;
|
||||||
|
let mut earliest_exit_epoch = std::cmp::max(
|
||||||
|
*earliest_exit_epoch_state,
|
||||||
|
spec.compute_activation_exit_epoch(state_ctxt.current_epoch)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
let per_epoch_churn = get_activation_exit_churn_limit(state_ctxt, spec)?;
|
||||||
|
// New epoch for exits
|
||||||
|
let mut exit_balance_to_consume = if *earliest_exit_epoch_state < earliest_exit_epoch {
|
||||||
|
per_epoch_churn
|
||||||
|
} else {
|
||||||
|
*exit_balance_to_consume_state
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exit doesn't fit in the current earliest epoch
|
||||||
|
if exit_balance > exit_balance_to_consume {
|
||||||
|
let balance_to_process = exit_balance.safe_sub(exit_balance_to_consume)?;
|
||||||
|
let additional_epochs = balance_to_process
|
||||||
|
.safe_sub(1)?
|
||||||
|
.safe_div(per_epoch_churn)?
|
||||||
|
.safe_add(1)?;
|
||||||
|
earliest_exit_epoch.safe_add_assign(additional_epochs)?;
|
||||||
|
exit_balance_to_consume.safe_add_assign(additional_epochs.safe_mul(per_epoch_churn)?)?;
|
||||||
|
}
|
||||||
|
// Consume the balance and update state variables
|
||||||
|
*exit_balance_to_consume_state = exit_balance_to_consume.safe_sub(exit_balance)?;
|
||||||
|
*earliest_exit_epoch_state = earliest_exit_epoch;
|
||||||
|
|
||||||
|
Ok(earliest_exit_epoch)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_activation_exit_churn_limit(
|
||||||
|
state_ctxt: &StateContext,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> Result<u64, Error> {
|
||||||
|
Ok(std::cmp::min(
|
||||||
|
spec.max_per_epoch_activation_exit_churn_limit,
|
||||||
|
get_balance_churn_limit(state_ctxt, spec)?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_balance_churn_limit(state_ctxt: &StateContext, spec: &ChainSpec) -> Result<u64, Error> {
|
||||||
|
let total_active_balance = state_ctxt.total_active_balance;
|
||||||
|
let churn = std::cmp::max(
|
||||||
|
spec.min_per_epoch_churn_limit_electra,
|
||||||
|
total_active_balance.safe_div(spec.churn_limit_quotient)?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(churn.safe_sub(churn.safe_rem(spec.effective_balance_increment)?)?)
|
||||||
|
}
|
||||||
|
|
||||||
impl SlashingsContext {
|
impl SlashingsContext {
|
||||||
fn new<E: EthSpec>(
|
fn new<E: EthSpec>(
|
||||||
state: &BeaconState<E>,
|
state: &BeaconState<E>,
|
||||||
|
|||||||
@@ -400,6 +400,14 @@ impl ChainSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn max_effective_balance_for_fork(&self, fork_name: ForkName) -> u64 {
|
||||||
|
if fork_name >= ForkName::Electra {
|
||||||
|
self.max_effective_balance_electra
|
||||||
|
} else {
|
||||||
|
self.max_effective_balance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a full `Fork` struct for a given epoch.
|
/// Returns a full `Fork` struct for a given epoch.
|
||||||
pub fn fork_at_epoch(&self, epoch: Epoch) -> Fork {
|
pub fn fork_at_epoch(&self, epoch: Epoch) -> Fork {
|
||||||
let current_fork_name = self.fork_name_at_epoch(epoch);
|
let current_fork_name = self.fork_name_at_epoch(epoch);
|
||||||
|
|||||||
Reference in New Issue
Block a user