From 90122b76629a50aeb4b23c4447d94bbace73527f Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Mon, 19 Jan 2026 18:53:15 +1100 Subject: [PATCH] Refactor `process_builder_withdrawals` --- beacon_node/beacon_chain/tests/store_tests.rs | 1 + .../src/per_block_processing.rs | 97 +++++++++---------- consensus/types/src/core/consts.rs | 1 + 3 files changed, 49 insertions(+), 50 deletions(-) diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index 314090d9e0..f2e9896b9d 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -1364,6 +1364,7 @@ async fn proposer_shuffling_root_consistency_at_fork_boundary() { } #[tokio::test] +#[allow(clippy::large_stack_frames)] async fn proposer_shuffling_changing_with_lookahead() { let initial_blocks = E::slots_per_epoch() * 4 - 1; diff --git a/consensus/state_processing/src/per_block_processing.rs b/consensus/state_processing/src/per_block_processing.rs index cb0bc5046f..d6872a14f1 100644 --- a/consensus/state_processing/src/per_block_processing.rs +++ b/consensus/state_processing/src/per_block_processing.rs @@ -10,6 +10,7 @@ use signature_sets::{ use std::borrow::Cow; use tree_hash::TreeHash; use typenum::Unsigned; +use types::consts::gloas::BUILDER_INDEX_FLAG; use types::*; pub use self::verify_attester_slashing::{ @@ -526,6 +527,48 @@ pub fn compute_timestamp_at_slot( .and_then(|since_genesis| state.genesis_time().safe_add(since_genesis)) } +pub fn convert_builder_index_to_validator_index(builder_index: BuilderIndex) -> u64 { + builder_index | BUILDER_INDEX_FLAG +} + +pub fn convert_validator_index_to_builder_index(validator_index: u64) -> BuilderIndex { + validator_index & !BUILDER_INDEX_FLAG +} + +pub fn get_builder_withdrawals( + state: &BeaconState, + withdrawal_index: &mut u64, + withdrawals: &mut Vec, +) -> Result { + let Ok(builder_pending_withdrawals) = state.builder_pending_withdrawals() else { + // Pre-Gloas, nothing to do. + return Ok(0); + }; + + let withdrawals_limit = E::max_withdrawals_per_payload(); + + let mut processed_count = 0; + for withdrawal in builder_pending_withdrawals { + let has_reached_limit = withdrawals.len() == withdrawals_limit; + + if has_reached_limit { + break; + } + + let builder_index = withdrawal.builder_index; + + withdrawals.push(Withdrawal { + index: *withdrawal_index, + validator_index: convert_builder_index_to_validator_index(builder_index), + address: withdrawal.fee_recipient, + amount: withdrawal.amount, + }); + withdrawal_index.safe_add_assign(1)?; + processed_count.safe_add_assign(1)?; + } + Ok(processed_count) +} + /// Compute the next batch of withdrawals which should be included in a block. /// /// https://ethereum.github.io/consensus-specs/specs/gloas/beacon-chain/#modified-get_expected_withdrawals @@ -536,61 +579,15 @@ pub fn get_expected_withdrawals( ) -> Result<(Withdrawals, Option, Option), 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::::with_capacity(E::max_withdrawals_per_payload()); let fork_name = state.fork_name_unchecked(); + let mut validator_index = state.next_withdrawal_validator_index()?; + // [New in Gloas:EIP7732] // Sweep for builder payments let processed_builder_withdrawals_count = - if let Ok(builder_pending_withdrawals) = state.builder_pending_withdrawals() { - let mut processed_builder_withdrawals_count = 0; - for withdrawal in builder_pending_withdrawals { - if withdrawal.withdrawable_epoch > epoch - || withdrawals.len().safe_add(1)? == E::max_withdrawals_per_payload() - { - break; - } - - if process_withdrawals::is_builder_payment_withdrawable(state, withdrawal)? { - let total_withdrawn = withdrawals - .iter() - .filter_map(|w| { - (w.validator_index == withdrawal.builder_index).then_some(w.amount) - }) - .safe_sum()?; - let balance = state - .get_balance(withdrawal.builder_index as usize)? - .safe_sub(total_withdrawn)?; - let builder = state.get_validator(withdrawal.builder_index as usize)?; - - let withdrawable_balance = if builder.slashed { - std::cmp::min(balance, withdrawal.amount) - } else if balance > spec.min_activation_balance { - std::cmp::min( - balance.safe_sub(spec.min_activation_balance)?, - withdrawal.amount, - ) - } else { - 0 - }; - - if withdrawable_balance > 0 { - withdrawals.push(Withdrawal { - index: withdrawal_index, - validator_index: withdrawal.builder_index, - address: withdrawal.fee_recipient, - amount: withdrawable_balance, - }); - withdrawal_index.safe_add_assign(1)?; - } - } - processed_builder_withdrawals_count.safe_add_assign(1)?; - } - Some(processed_builder_withdrawals_count) - } else { - None - }; + get_builder_withdrawals(state, &mut withdrawal_index, &mut withdrawals)?; // [New in Electra:EIP7251] // Consume pending partial withdrawals @@ -696,7 +693,7 @@ pub fn get_expected_withdrawals( withdrawals .try_into() .map_err(BlockProcessingError::SszTypesError)?, - processed_builder_withdrawals_count, + Some(processed_builder_withdrawals_count as usize), processed_partial_withdrawals_count, )) } diff --git a/consensus/types/src/core/consts.rs b/consensus/types/src/core/consts.rs index 6876159c0f..546875e054 100644 --- a/consensus/types/src/core/consts.rs +++ b/consensus/types/src/core/consts.rs @@ -27,4 +27,5 @@ pub mod deneb { } pub mod gloas { pub const BUILDER_INDEX_SELF_BUILD: u64 = u64::MAX; + pub const BUILDER_INDEX_FLAG: u64 = 1 << 40; }