From 7a879829568f74e8e586abcee726b36c7ca5b959 Mon Sep 17 00:00:00 2001 From: shane-moore Date: Tue, 19 Aug 2025 13:05:18 -0700 Subject: [PATCH 1/2] add process_builder_pending_payments to epoch processing --- .../src/per_epoch_processing/altair.rs | 6 ++ .../process_builder_pending_payments.rs | 62 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs diff --git a/consensus/state_processing/src/per_epoch_processing/altair.rs b/consensus/state_processing/src/per_epoch_processing/altair.rs index d9e69647304..86a690ee8ce 100644 --- a/consensus/state_processing/src/per_epoch_processing/altair.rs +++ b/consensus/state_processing/src/per_epoch_processing/altair.rs @@ -12,6 +12,7 @@ use crate::per_epoch_processing::{ pub use inactivity_updates::process_inactivity_updates_slow; pub use justification_and_finalization::process_justification_and_finalization; pub use participation_flag_updates::process_participation_flag_updates; +pub use process_builder_pending_payments::process_builder_pending_payments; pub use rewards_and_penalties::process_rewards_and_penalties_slow; pub use sync_committee_updates::process_sync_committee_updates; use types::{BeaconState, ChainSpec, EthSpec, RelativeEpoch}; @@ -19,6 +20,7 @@ use types::{BeaconState, ChainSpec, EthSpec, RelativeEpoch}; pub mod inactivity_updates; pub mod justification_and_finalization; pub mod participation_flag_updates; +pub mod process_builder_pending_payments; pub mod rewards_and_penalties; pub mod sync_committee_updates; @@ -77,6 +79,10 @@ pub fn process_epoch( process_sync_committee_updates(state, spec)?; + if state.fork_name_unchecked().gloas_enabled() { + process_builder_pending_payments(state, spec)?; + } + // Rotate the epoch caches to suit the epoch transition. state.advance_caches()?; update_progressive_balances_on_epoch_transition(state, spec)?; diff --git a/consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs b/consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs new file mode 100644 index 00000000000..d0e0697720a --- /dev/null +++ b/consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs @@ -0,0 +1,62 @@ +use super::Error; +use safe_arith::SafeArith; +use types::{BeaconState, BuilderPendingPayment, ChainSpec, EthSpec, Vector}; + +pub fn process_builder_pending_payments( + state: &mut BeaconState, + spec: &ChainSpec, +) -> Result<(), Error> { + let quorum = get_builder_payment_quorum_threshold(state, spec)?; + + // Collect qualifying payments + let qualifying_payments = state + .builder_pending_payments()? + .iter() + .take(E::slots_per_epoch() as usize) + .filter(|payment| payment.weight > quorum) + .cloned() + .collect::>(); + + // Update `builder_pending_withdrawals` with qualifying `builder_pending_payments` + qualifying_payments + .into_iter() + .try_for_each(|payment| -> Result<(), Error> { + let exit_queue_epoch = + state.compute_exit_epoch_and_update_churn(payment.withdrawal.amount, spec)?; + let withdrawable_epoch = + exit_queue_epoch.safe_add(spec.min_validator_withdrawability_delay)?; + + let mut withdrawal = payment.withdrawal.clone(); + withdrawal.withdrawable_epoch = withdrawable_epoch; + state.builder_pending_withdrawals_mut()?.push(withdrawal)?; + Ok(()) + })?; + + // Move remaining `builder_pending_payments` to start of list and set the rest to default + let new_payments = state + .builder_pending_payments()? + .iter() + .skip(E::slots_per_epoch() as usize) + .cloned() + .chain((0..E::slots_per_epoch() as usize).map(|_| BuilderPendingPayment::default())) + .collect::>(); + + *state.builder_pending_payments_mut()? = Vector::new(new_payments)?; + + Ok(()) +} + +pub fn get_builder_payment_quorum_threshold( + state: &BeaconState, + spec: &ChainSpec, +) -> Result { + let total_active_balance = state.get_total_active_balance()?; + + let quorum = total_active_balance + .safe_div(E::slots_per_epoch())? + .safe_mul(spec.builder_payment_threshold_numerator)?; + + quorum + .safe_div(spec.builder_payment_threshold_denominator) + .map_err(Error::from) +} From 0c81169040461108f6f1cd420a15c1bf12930c9d Mon Sep 17 00:00:00 2001 From: shane-moore Date: Tue, 19 Aug 2025 14:02:30 -0700 Subject: [PATCH 2/2] per_slot_processing modified --- .../src/per_epoch_processing.rs | 1 + .../src/per_epoch_processing/altair.rs | 6 +++--- .../src/per_epoch_processing/gloas.rs | 3 +++ .../process_builder_pending_payments.rs | 18 ++++++++++-------- .../src/per_slot_processing.rs | 19 +++++++++++++++++++ 5 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 consensus/state_processing/src/per_epoch_processing/gloas.rs rename consensus/state_processing/src/per_epoch_processing/{altair => gloas}/process_builder_pending_payments.rs (81%) diff --git a/consensus/state_processing/src/per_epoch_processing.rs b/consensus/state_processing/src/per_epoch_processing.rs index 8de6054bd2e..c393d31fbb0 100644 --- a/consensus/state_processing/src/per_epoch_processing.rs +++ b/consensus/state_processing/src/per_epoch_processing.rs @@ -18,6 +18,7 @@ pub mod capella; pub mod effective_balance_updates; pub mod epoch_processing_summary; pub mod errors; +pub mod gloas; pub mod historical_roots_update; pub mod justification_and_finalization_state; pub mod registry_updates; diff --git a/consensus/state_processing/src/per_epoch_processing/altair.rs b/consensus/state_processing/src/per_epoch_processing/altair.rs index 86a690ee8ce..fa94f5787d6 100644 --- a/consensus/state_processing/src/per_epoch_processing/altair.rs +++ b/consensus/state_processing/src/per_epoch_processing/altair.rs @@ -6,13 +6,13 @@ use crate::epoch_cache::initialize_epoch_cache; use crate::per_epoch_processing::single_pass::{SinglePassConfig, process_epoch_single_pass}; use crate::per_epoch_processing::{ capella::process_historical_summaries_update, + gloas::process_builder_pending_payments, historical_roots_update::process_historical_roots_update, resets::{process_eth1_data_reset, process_randao_mixes_reset, process_slashings_reset}, }; pub use inactivity_updates::process_inactivity_updates_slow; pub use justification_and_finalization::process_justification_and_finalization; pub use participation_flag_updates::process_participation_flag_updates; -pub use process_builder_pending_payments::process_builder_pending_payments; pub use rewards_and_penalties::process_rewards_and_penalties_slow; pub use sync_committee_updates::process_sync_committee_updates; use types::{BeaconState, ChainSpec, EthSpec, RelativeEpoch}; @@ -20,7 +20,6 @@ use types::{BeaconState, ChainSpec, EthSpec, RelativeEpoch}; pub mod inactivity_updates; pub mod justification_and_finalization; pub mod participation_flag_updates; -pub mod process_builder_pending_payments; pub mod rewards_and_penalties; pub mod sync_committee_updates; @@ -79,7 +78,8 @@ pub fn process_epoch( process_sync_committee_updates(state, spec)?; - if state.fork_name_unchecked().gloas_enabled() { + if state.builder_pending_payments().is_ok() { + // Post-Gloas process_builder_pending_payments(state, spec)?; } diff --git a/consensus/state_processing/src/per_epoch_processing/gloas.rs b/consensus/state_processing/src/per_epoch_processing/gloas.rs new file mode 100644 index 00000000000..a9a030ecbe1 --- /dev/null +++ b/consensus/state_processing/src/per_epoch_processing/gloas.rs @@ -0,0 +1,3 @@ +pub use process_builder_pending_payments::process_builder_pending_payments; + +mod process_builder_pending_payments; diff --git a/consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs b/consensus/state_processing/src/per_epoch_processing/gloas/process_builder_pending_payments.rs similarity index 81% rename from consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs rename to consensus/state_processing/src/per_epoch_processing/gloas/process_builder_pending_payments.rs index d0e0697720a..580366bb8cd 100644 --- a/consensus/state_processing/src/per_epoch_processing/altair/process_builder_pending_payments.rs +++ b/consensus/state_processing/src/per_epoch_processing/gloas/process_builder_pending_payments.rs @@ -1,11 +1,13 @@ -use super::Error; +use crate::EpochProcessingError; use safe_arith::SafeArith; use types::{BeaconState, BuilderPendingPayment, ChainSpec, EthSpec, Vector}; +/// TODO(EIP-7732): Add EF consensus-spec tests for `process_builder_pending_payments` +/// Currently blocked by EF consensus-spec-tests for Gloas not yet integrated. pub fn process_builder_pending_payments( state: &mut BeaconState, spec: &ChainSpec, -) -> Result<(), Error> { +) -> Result<(), EpochProcessingError> { let quorum = get_builder_payment_quorum_threshold(state, spec)?; // Collect qualifying payments @@ -18,9 +20,8 @@ pub fn process_builder_pending_payments( .collect::>(); // Update `builder_pending_withdrawals` with qualifying `builder_pending_payments` - qualifying_payments - .into_iter() - .try_for_each(|payment| -> Result<(), Error> { + qualifying_payments.into_iter().try_for_each( + |payment| -> Result<(), EpochProcessingError> { let exit_queue_epoch = state.compute_exit_epoch_and_update_churn(payment.withdrawal.amount, spec)?; let withdrawable_epoch = @@ -30,7 +31,8 @@ pub fn process_builder_pending_payments( withdrawal.withdrawable_epoch = withdrawable_epoch; state.builder_pending_withdrawals_mut()?.push(withdrawal)?; Ok(()) - })?; + }, + )?; // Move remaining `builder_pending_payments` to start of list and set the rest to default let new_payments = state @@ -49,7 +51,7 @@ pub fn process_builder_pending_payments( pub fn get_builder_payment_quorum_threshold( state: &BeaconState, spec: &ChainSpec, -) -> Result { +) -> Result { let total_active_balance = state.get_total_active_balance()?; let quorum = total_active_balance @@ -58,5 +60,5 @@ pub fn get_builder_payment_quorum_threshold( quorum .safe_div(spec.builder_payment_threshold_denominator) - .map_err(Error::from) + .map_err(EpochProcessingError::from) } diff --git a/consensus/state_processing/src/per_slot_processing.rs b/consensus/state_processing/src/per_slot_processing.rs index 8695054e1e7..10ce216c3c5 100644 --- a/consensus/state_processing/src/per_slot_processing.rs +++ b/consensus/state_processing/src/per_slot_processing.rs @@ -13,6 +13,7 @@ pub enum Error { EpochProcessingError(EpochProcessingError), ArithError(ArithError), InconsistentStateFork(InconsistentFork), + BitfieldError(ssz::BitfieldError), } impl From for Error { @@ -21,6 +22,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: ssz::BitfieldError) -> Self { + Self::BitfieldError(e) + } +} + /// Advances a state forward by one slot, performing per-epoch processing if required. /// /// If the root of the supplied `state` is known, then it can be passed as `state_root`. If @@ -49,6 +56,18 @@ pub fn per_slot_processing( state.slot_mut().safe_add_assign(1)?; + // Unset the next payload availability + if state.fork_name_unchecked().gloas_enabled() { + let next_slot_index = state + .slot() + .as_usize() + .safe_add(1)? + .safe_rem(E::slots_per_historical_root())?; + state + .execution_payload_availability_mut()? + .set(next_slot_index, false)?; + } + // Process fork upgrades here. Note that multiple upgrades can potentially run // in sequence if they are scheduled in the same Epoch (common in testnets) if state.slot().safe_rem(E::slots_per_epoch())? == 0 {