mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-04 13:24:39 +00:00
process withdrawals updates
This commit is contained in:
@@ -4435,6 +4435,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let proposal_epoch = proposal_slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
if head_state.current_epoch() == proposal_epoch {
|
||||
return get_expected_withdrawals(&unadvanced_state, &self.spec)
|
||||
.map(|(withdrawals, _)| withdrawals)
|
||||
.map_err(Error::PrepareProposerFailed);
|
||||
}
|
||||
|
||||
@@ -4452,7 +4453,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
proposal_epoch.start_slot(T::EthSpec::slots_per_epoch()),
|
||||
&self.spec,
|
||||
)?;
|
||||
get_expected_withdrawals(&advanced_state, &self.spec).map_err(Error::PrepareProposerFailed)
|
||||
get_expected_withdrawals(&advanced_state, &self.spec)
|
||||
.map(|(withdrawals, _)| withdrawals)
|
||||
.map_err(Error::PrepareProposerFailed)
|
||||
}
|
||||
|
||||
/// Determine whether a fork choice update to the execution layer should be overridden.
|
||||
|
||||
@@ -413,7 +413,7 @@ pub fn get_execution_payload<T: BeaconChainTypes>(
|
||||
state.latest_execution_payload_header()?.block_hash();
|
||||
let withdrawals = match state {
|
||||
&BeaconState::Capella(_) | &BeaconState::Deneb(_) | &BeaconState::Electra(_) => {
|
||||
Some(get_expected_withdrawals(state, spec)?.into())
|
||||
Some(get_expected_withdrawals(state, spec)?.0.into())
|
||||
}
|
||||
&BeaconState::Bellatrix(_) => None,
|
||||
// These shouldn't happen but they're here to make the pattern irrefutable
|
||||
|
||||
@@ -33,7 +33,7 @@ pub fn get_next_withdrawals<T: BeaconChainTypes>(
|
||||
}
|
||||
|
||||
match get_expected_withdrawals(&state, &chain.spec) {
|
||||
Ok(withdrawals) => Ok(withdrawals),
|
||||
Ok((withdrawals, _)) => Ok(withdrawals),
|
||||
Err(e) => Err(warp_utils::reject::custom_server_error(format!(
|
||||
"failed to get expected withdrawal: {:?}",
|
||||
e
|
||||
|
||||
@@ -610,6 +610,7 @@ pub async fn proposer_boost_re_org_test(
|
||||
assert_eq!(state_b.slot(), slot_b);
|
||||
let pre_advance_withdrawals = get_expected_withdrawals(&state_b, &harness.chain.spec)
|
||||
.unwrap()
|
||||
.0
|
||||
.to_vec();
|
||||
complete_state_advance(&mut state_b, None, slot_c, &harness.chain.spec).unwrap();
|
||||
|
||||
@@ -696,6 +697,7 @@ pub async fn proposer_boost_re_org_test(
|
||||
get_expected_withdrawals(&state_b, &harness.chain.spec)
|
||||
}
|
||||
.unwrap()
|
||||
.0
|
||||
.to_vec();
|
||||
let payload_attribs_withdrawals = payload_attribs.withdrawals().unwrap();
|
||||
assert_eq!(expected_withdrawals, *payload_attribs_withdrawals);
|
||||
|
||||
@@ -5448,7 +5448,9 @@ impl ApiTester {
|
||||
&self.chain.spec,
|
||||
);
|
||||
}
|
||||
let expected_withdrawals = get_expected_withdrawals(&state, &self.chain.spec).unwrap();
|
||||
let expected_withdrawals = get_expected_withdrawals(&state, &self.chain.spec)
|
||||
.unwrap()
|
||||
.0;
|
||||
|
||||
// fetch expected withdrawals from the client
|
||||
let result = self.client.get_expected_withdrawals(&state_id).await;
|
||||
|
||||
@@ -503,13 +503,61 @@ pub fn compute_timestamp_at_slot<E: EthSpec>(
|
||||
pub fn get_expected_withdrawals<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Withdrawals<E>, BlockProcessingError> {
|
||||
) -> Result<(Withdrawals<E>, Option<usize>), BlockProcessingError> {
|
||||
let epoch = state.current_epoch();
|
||||
let mut withdrawal_index = state.next_withdrawal_index()?;
|
||||
let mut validator_index = state.next_withdrawal_validator_index()?;
|
||||
let mut withdrawals = vec![];
|
||||
let fork_name = state.fork_name_unchecked();
|
||||
|
||||
// [New in Electra:EIP7251]
|
||||
// Consume pending partial withdrawals
|
||||
let partial_withdrawals_count =
|
||||
if let Ok(partial_withdrawals) = state.pending_partial_withdrawals() {
|
||||
for withdrawal in partial_withdrawals {
|
||||
if withdrawal.withdrawable_epoch > epoch
|
||||
|| withdrawals.len() == spec.max_pending_partials_per_withdrawals_sweep as usize
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
let withdrawal_balance = state
|
||||
.balances()
|
||||
.get(withdrawal.index as usize)
|
||||
.copied()
|
||||
.ok_or(BeaconStateError::BalancesOutOfBounds(
|
||||
withdrawal.index as usize,
|
||||
))?;
|
||||
let validator = state.validators().get(withdrawal.index as usize).ok_or(
|
||||
BeaconStateError::UnknownValidator(withdrawal.index as usize),
|
||||
)?;
|
||||
|
||||
let has_sufficient_effective_balance =
|
||||
validator.effective_balance >= spec.min_activation_balance;
|
||||
let has_excess_balance = withdrawal_balance > spec.min_activation_balance;
|
||||
|
||||
if validator.exit_epoch == spec.far_future_epoch
|
||||
&& has_sufficient_effective_balance
|
||||
&& has_excess_balance
|
||||
{
|
||||
let withdrawable_balance = std::cmp::min(
|
||||
withdrawal_balance.safe_sub(spec.min_activation_balance)?,
|
||||
withdrawal.amount,
|
||||
);
|
||||
withdrawals.push(Withdrawal {
|
||||
index: withdrawal_index,
|
||||
validator_index: withdrawal.index,
|
||||
address: Address::from_slice(&validator.withdrawal_credentials[12..]),
|
||||
amount: withdrawable_balance,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
}
|
||||
Some(withdrawals.len())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let bound = std::cmp::min(
|
||||
state.validators().len() as u64,
|
||||
spec.max_validators_per_withdrawals_sweep,
|
||||
@@ -536,7 +584,10 @@ pub fn get_expected_withdrawals<E: EthSpec>(
|
||||
address: validator
|
||||
.get_eth1_withdrawal_address(spec)
|
||||
.ok_or(BlockProcessingError::WithdrawalCredentialsInvalid)?,
|
||||
amount: balance.safe_sub(spec.max_effective_balance)?,
|
||||
amount: balance.safe_sub(
|
||||
validator
|
||||
.get_validator_max_effective_balance(spec, state.fork_name_unchecked()),
|
||||
)?,
|
||||
});
|
||||
withdrawal_index.safe_add_assign(1)?;
|
||||
}
|
||||
@@ -548,7 +599,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
|
||||
.safe_rem(state.validators().len() as u64)?;
|
||||
}
|
||||
|
||||
Ok(withdrawals.into())
|
||||
Ok((withdrawals.into(), partial_withdrawals_count))
|
||||
}
|
||||
|
||||
/// Apply withdrawals to the state.
|
||||
@@ -558,9 +609,9 @@ pub fn process_withdrawals<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
match state {
|
||||
BeaconState::Bellatrix(_) => Ok(()),
|
||||
BeaconState::Capella(_) | BeaconState::Deneb(_) | BeaconState::Electra(_) => {
|
||||
let expected_withdrawals = get_expected_withdrawals(state, spec)?;
|
||||
let (expected_withdrawals, partial_withdrawals_count) =
|
||||
get_expected_withdrawals(state, spec)?;
|
||||
let expected_root = expected_withdrawals.tree_hash_root();
|
||||
let withdrawals_root = payload.withdrawals_root()?;
|
||||
|
||||
@@ -579,6 +630,19 @@ pub fn process_withdrawals<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
)?;
|
||||
}
|
||||
|
||||
// Update pending partial withdrawals [New in Electra:EIP7251]
|
||||
if let Some(partial_withdrawals_count) = partial_withdrawals_count {
|
||||
let new_partial_withdrawals = state
|
||||
.pending_partial_withdrawals()?
|
||||
.to_vec()
|
||||
.get(partial_withdrawals_count..)
|
||||
.ok_or(BeaconStateError::PartialWithdrawalCountInvalid(
|
||||
partial_withdrawals_count,
|
||||
))?
|
||||
.to_vec();
|
||||
*state.pending_partial_withdrawals_mut()? = List::new(new_partial_withdrawals)?;
|
||||
}
|
||||
|
||||
// Update the next withdrawal index if this block contained withdrawals
|
||||
if let Some(latest_withdrawal) = expected_withdrawals.last() {
|
||||
*state.next_withdrawal_index_mut()? = latest_withdrawal.index.safe_add(1)?;
|
||||
@@ -606,6 +670,6 @@ pub fn process_withdrawals<E: EthSpec, Payload: AbstractExecPayload<E>>(
|
||||
Ok(())
|
||||
}
|
||||
// these shouldn't even be encountered but they're here for completeness
|
||||
BeaconState::Base(_) | BeaconState::Altair(_) => Ok(()),
|
||||
BeaconState::Base(_) | BeaconState::Altair(_) | BeaconState::Bellatrix(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ pub enum Error {
|
||||
IndexNotSupported(usize),
|
||||
InvalidFlagIndex(usize),
|
||||
MerkleTreeError(merkle_proof::MerkleTreeError),
|
||||
PartialWithdrawalCountInvalid(usize),
|
||||
}
|
||||
|
||||
/// Control whether an epoch-indexed field can be indexed at the next epoch or not.
|
||||
@@ -2097,11 +2098,12 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
&self,
|
||||
validator_index: usize,
|
||||
spec: &ChainSpec,
|
||||
current_fork: ForkName,
|
||||
) -> Result<u64, Error> {
|
||||
let max_effective_balance = self
|
||||
.validators()
|
||||
.get(validator_index)
|
||||
.map(|validator| validator.get_validator_max_effective_balance(spec))
|
||||
.map(|validator| validator.get_validator_max_effective_balance(spec, current_fork))
|
||||
.ok_or(Error::UnknownValidator(validator_index))?;
|
||||
Ok(std::cmp::min(
|
||||
*self
|
||||
|
||||
@@ -203,7 +203,7 @@ impl Validator {
|
||||
current_fork: ForkName,
|
||||
) -> bool {
|
||||
if current_fork >= ForkName::Electra {
|
||||
self.is_partially_withdrawable_validator_electra(balance, spec)
|
||||
self.is_partially_withdrawable_validator_electra(balance, spec, current_fork)
|
||||
} else {
|
||||
self.is_partially_withdrawable_validator_capella(balance, spec)
|
||||
}
|
||||
@@ -223,8 +223,9 @@ impl Validator {
|
||||
&self,
|
||||
balance: u64,
|
||||
spec: &ChainSpec,
|
||||
current_fork: ForkName,
|
||||
) -> bool {
|
||||
let max_effective_balance = self.get_validator_max_effective_balance(spec);
|
||||
let max_effective_balance = self.get_validator_max_effective_balance(spec, current_fork);
|
||||
let has_max_effective_balance = self.effective_balance == max_effective_balance;
|
||||
let has_excess_balance = balance > max_effective_balance;
|
||||
self.has_execution_withdrawal_credential(spec)
|
||||
@@ -239,12 +240,20 @@ impl Validator {
|
||||
}
|
||||
|
||||
/// Returns the max effective balance for a validator in gwei.
|
||||
pub fn get_validator_max_effective_balance(&self, spec: &ChainSpec) -> u64 {
|
||||
pub fn get_validator_max_effective_balance(
|
||||
&self,
|
||||
spec: &ChainSpec,
|
||||
current_fork: ForkName,
|
||||
) -> u64 {
|
||||
if current_fork >= ForkName::Electra {
|
||||
if self.has_compounding_withdrawal_credential(spec) {
|
||||
spec.max_effective_balance_electra
|
||||
} else {
|
||||
spec.min_activation_balance
|
||||
}
|
||||
} else {
|
||||
spec.max_effective_balance
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user