Add get_builders_sweep_withdrawals and fix Gloas withdrawal tests.

This commit is contained in:
Jimmy Chen
2026-01-20 17:07:27 +11:00
parent 5c8acea305
commit 8b4c037fb4
5 changed files with 87 additions and 7 deletions

View File

@@ -659,6 +659,70 @@ pub fn get_pending_partial_withdrawals<E: EthSpec>(
Ok(Some(processed_count))
}
/// Get withdrawals from the builders sweep.
///
/// This function iterates through builders starting from `next_withdrawal_builder_index`
/// and adds withdrawals for builders whose withdrawable_epoch has been reached and have balance.
///
/// https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/#new-get_builders_sweep_withdrawals
pub fn get_builders_sweep_withdrawals<E: EthSpec>(
state: &BeaconState<E>,
withdrawal_index: &mut u64,
withdrawals: &mut Vec<Withdrawal>,
) -> Result<Option<u64>, BlockProcessingError> {
let Ok(builders) = state.builders() else {
// Pre-Gloas, nothing to do.
return Ok(None);
};
if builders.is_empty() {
return Ok(Some(0));
}
let epoch = state.current_epoch();
let builders_limit = std::cmp::min(builders.len(), E::max_builders_per_withdrawals_sweep());
// Reserve one slot for validator sweep withdrawals
let withdrawals_limit = E::max_withdrawals_per_payload().saturating_sub(1);
block_verify!(
withdrawals.len() <= withdrawals_limit,
BlockProcessingError::WithdrawalsLimitExceeded {
limit: withdrawals_limit,
prior_withdrawals: withdrawals.len()
}
);
let mut processed_count: u64 = 0;
let mut builder_index = state.next_withdrawal_builder_index()?;
for _ in 0..builders_limit {
if withdrawals.len() >= withdrawals_limit {
break;
}
let builder = builders
.get(builder_index as usize)
.ok_or(BeaconStateError::UnknownBuilder(builder_index))?;
if builder.withdrawable_epoch <= epoch && builder.balance > 0 {
withdrawals.push(Withdrawal {
index: *withdrawal_index,
validator_index: convert_builder_index_to_validator_index(builder_index),
address: builder.execution_address,
amount: builder.balance,
});
withdrawal_index.safe_add_assign(1)?;
}
builder_index = builder_index
.safe_add(1)?
.safe_rem(builders.len() as u64)?;
processed_count.safe_add_assign(1)?;
}
Ok(Some(processed_count))
}
/// Get withdrawals from the validator sweep.
///
/// This function iterates through validators starting from `next_withdrawal_validator_index`
@@ -737,7 +801,7 @@ pub fn get_validators_sweep_withdrawals<E: EthSpec>(
pub fn get_expected_withdrawals<E: EthSpec>(
state: &BeaconState<E>,
spec: &ChainSpec,
) -> Result<(Withdrawals<E>, Option<usize>, Option<usize>, u64), BlockProcessingError> {
) -> Result<(Withdrawals<E>, Option<usize>, Option<usize>, Option<u64>, u64), BlockProcessingError> {
let mut withdrawal_index = state.next_withdrawal_index()?;
let mut withdrawals = Vec::<Withdrawal>::with_capacity(E::max_withdrawals_per_payload());
@@ -751,6 +815,11 @@ pub fn get_expected_withdrawals<E: EthSpec>(
let processed_partial_withdrawals_count =
get_pending_partial_withdrawals(state, &mut withdrawal_index, &mut withdrawals, spec)?;
// [New in Gloas:EIP7732]
// Get builders sweep withdrawals
let processed_builders_sweep_count =
get_builders_sweep_withdrawals(state, &mut withdrawal_index, &mut withdrawals)?;
// Get validators sweep withdrawals
let processed_validators_sweep_count =
get_validators_sweep_withdrawals(state, &mut withdrawal_index, &mut withdrawals, spec)?;
@@ -761,6 +830,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
.map_err(BlockProcessingError::SszTypesError)?,
processed_builder_withdrawals_count,
processed_partial_withdrawals_count,
processed_builders_sweep_count,
processed_validators_sweep_count,
))
}

View File

@@ -82,7 +82,7 @@ pub mod capella {
) -> Result<(), BlockProcessingError> {
// check if capella enabled because this function will run on the merge block where the fork is technically still Bellatrix
if state.fork_name_unchecked().capella_enabled() {
let (expected_withdrawals, _, partial_withdrawals_count, _) =
let (expected_withdrawals, _, partial_withdrawals_count, _, _) =
get_expected_withdrawals(state, spec)?;
let expected_root = expected_withdrawals.tree_hash_root();
@@ -127,8 +127,14 @@ pub mod gloas {
return Ok(());
}
let (expected_withdrawals, builder_withdrawals_count, partial_withdrawals_count, _) =
get_expected_withdrawals(state, spec)?;
// TODO(EIP-7732): Use processed_builders_sweep_count to call update_next_withdrawal_builder_index
let (
expected_withdrawals,
builder_withdrawals_count,
partial_withdrawals_count,
_processed_builders_sweep_count,
_,
) = get_expected_withdrawals(state, spec)?;
for withdrawal in expected_withdrawals.iter() {
decrease_balance(
@@ -158,6 +164,9 @@ pub mod gloas {
*state.builder_pending_withdrawals_mut()? = List::new(updated_builder_withdrawals)?;
}
// [New in Gloas:EIP7732] update_payload_expected_withdrawals
*state.payload_expected_withdrawals_mut()? = List::new(expected_withdrawals.to_vec())?;
process_withdrawals_common(state, expected_withdrawals, partial_withdrawals_count, spec)?;
Ok(())