From 4d99d1a3b52ddc5451fe657bf2c945cc146d0539 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Mon, 27 Apr 2026 13:55:19 +0200 Subject: [PATCH 1/3] filter voluntary exits that conflict w/ parent envelope execution requests --- .../src/block_production/gloas.rs | 54 +++++++++++++++---- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/beacon_node/beacon_chain/src/block_production/gloas.rs b/beacon_node/beacon_chain/src/block_production/gloas.rs index 9b3fc2806e..a055cd898d 100644 --- a/beacon_node/beacon_chain/src/block_production/gloas.rs +++ b/beacon_node/beacon_chain/src/block_production/gloas.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::marker::PhantomData; use std::sync::Arc; @@ -136,6 +136,16 @@ impl BeaconChain { graffiti_settings: GraffitiSettings, verification: ProduceBlockVerification, ) -> Result, BlockProductionError> { + // Extract the parent's execution requests from the envelope (if parent was full). + let parent_execution_requests = if parent_payload_status == PayloadStatus::Full { + parent_envelope + .as_ref() + .map(|env| env.message.execution_requests.clone()) + .ok_or(BlockProductionError::MissingParentExecutionPayload)? + } else { + ExecutionRequests::default() + }; + // Part 1/3 (blocking) // // Perform the state advance and block-packing functions. @@ -144,6 +154,7 @@ impl BeaconChain { .graffiti_calculator .get_graffiti(graffiti_settings) .await; + let parent_execution_requests_ref = parent_execution_requests.clone(); let (partial_beacon_block, state) = self .task_executor .spawn_blocking_handle( @@ -154,6 +165,7 @@ impl BeaconChain { produce_at_slot, randao_reveal, graffiti, + &parent_execution_requests_ref, ) }, "produce_partial_beacon_block_gloas", @@ -162,16 +174,6 @@ impl BeaconChain { .await .map_err(BlockProductionError::TokioJoin)??; - // Extract the parent's execution requests from the envelope (if parent was full). - let parent_execution_requests = if parent_payload_status == PayloadStatus::Full { - parent_envelope - .as_ref() - .map(|env| env.message.execution_requests.clone()) - .ok_or(BlockProductionError::MissingParentExecutionPayload)? - } else { - ExecutionRequests::default() - }; - // Part 2/3 (async) // // Produce the execution payload bid. @@ -222,6 +224,7 @@ impl BeaconChain { produce_at_slot: Slot, randao_reveal: Signature, graffiti: Graffiti, + parent_execution_requests: &ExecutionRequests, ) -> Result<(PartialBeaconBlock, BeaconState), BlockProductionError> { // It is invalid to try to produce a block using a state from a future slot. @@ -256,6 +259,35 @@ impl BeaconChain { let (mut proposer_slashings, mut attester_slashings, mut voluntary_exits) = self.op_pool.get_slashings_and_exits(&state, &self.spec); + // Filter out voluntary exits that conflict with parent execution requests. + // Parent withdrawal/consolidation requests are processed before voluntary + // exits during block processing (`process_parent_execution_payload` runs + // before `process_operations`) and can initiate validator exits, causing the + // voluntary exit assertion failures. + let mut exited_pubkeys = HashSet::with_capacity( + parent_execution_requests.withdrawals.len() + + parent_execution_requests.consolidations.len(), + ); + for req in &parent_execution_requests.withdrawals { + if req.amount == self.spec.full_exit_request_amount { + exited_pubkeys.insert(req.validator_pubkey); + } + } + for req in &parent_execution_requests.consolidations { + if req.source_pubkey != req.target_pubkey { + exited_pubkeys.insert(req.source_pubkey); + } + } + if !exited_pubkeys.is_empty() { + voluntary_exits.retain(|exit| { + state + .validators() + .get(exit.message.validator_index as usize) + .map(|v| !exited_pubkeys.contains(&v.pubkey)) + .unwrap_or(false) + }); + } + drop(slashings_and_exits_span); let eth1_data = state.eth1_data().clone(); From 4699bbc06983666a87f2db0679a2aa24ad52dc76 Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Mon, 27 Apr 2026 13:55:31 +0200 Subject: [PATCH 2/3] fmt --- beacon_node/beacon_chain/src/block_production/gloas.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/beacon_node/beacon_chain/src/block_production/gloas.rs b/beacon_node/beacon_chain/src/block_production/gloas.rs index a055cd898d..85853a09b7 100644 --- a/beacon_node/beacon_chain/src/block_production/gloas.rs +++ b/beacon_node/beacon_chain/src/block_production/gloas.rs @@ -260,10 +260,6 @@ impl BeaconChain { self.op_pool.get_slashings_and_exits(&state, &self.spec); // Filter out voluntary exits that conflict with parent execution requests. - // Parent withdrawal/consolidation requests are processed before voluntary - // exits during block processing (`process_parent_execution_payload` runs - // before `process_operations`) and can initiate validator exits, causing the - // voluntary exit assertion failures. let mut exited_pubkeys = HashSet::with_capacity( parent_execution_requests.withdrawals.len() + parent_execution_requests.consolidations.len(), From 949c027dfd37408784216b2c4e5e727e6ad571fe Mon Sep 17 00:00:00 2001 From: Mac L Date: Tue, 28 Apr 2026 11:01:13 +0400 Subject: [PATCH 3/3] Add method to `Hash256` to display shortened hashes (#9118) #6689 Inspired by the initial implementation of #9108, credit to @chong-he. This adds an extension trait to `Hash256` and add a `short` method to provide smaller formatted hashes for logging. Co-Authored-By: Mac L --- beacon_node/client/src/notifier.rs | 6 ++--- .../types/src/core/execution_block_hash.rs | 13 ++++------ consensus/types/src/core/mod.rs | 26 +++++++++++++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/beacon_node/client/src/notifier.rs b/beacon_node/client/src/notifier.rs index 0d73a6bf7a..bdb4228765 100644 --- a/beacon_node/client/src/notifier.rs +++ b/beacon_node/client/src/notifier.rs @@ -360,7 +360,7 @@ pub fn spawn_notifier( let block_info = if current_slot > head_slot { " … empty".to_string() } else { - head_root.to_string() + head_root.short().to_string() }; let block_hash = match beacon_chain.canonical_head.head_execution_status() { @@ -393,7 +393,7 @@ pub fn spawn_notifier( info!( peers = peer_count_pretty(connected_peer_count), exec_hash = block_hash, - finalized_root = %finalized_checkpoint.root, + finalized_root = %finalized_checkpoint.root.short(), finalized_epoch = %finalized_checkpoint.epoch, epoch = %current_epoch, block = block_info, @@ -404,7 +404,7 @@ pub fn spawn_notifier( metrics::set_gauge(&metrics::IS_SYNCED, 0); info!( peers = peer_count_pretty(connected_peer_count), - finalized_root = %finalized_checkpoint.root, + finalized_root = %finalized_checkpoint.root.short(), finalized_epoch = %finalized_checkpoint.epoch, %head_slot, %current_slot, diff --git a/consensus/types/src/core/execution_block_hash.rs b/consensus/types/src/core/execution_block_hash.rs index cbacf7cf74..71e63727ee 100644 --- a/consensus/types/src/core/execution_block_hash.rs +++ b/consensus/types/src/core/execution_block_hash.rs @@ -5,7 +5,10 @@ use rand::RngCore; use serde::{Deserialize, Serialize}; use ssz::{Decode, DecodeError, Encode}; -use crate::{core::Hash256, test_utils::TestRandom}; +use crate::{ + core::{Hash256, Hash256Ext}, + test_utils::TestRandom, +}; #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)] @@ -20,13 +23,7 @@ impl fmt::Debug for ExecutionBlockHash { impl fmt::Display for ExecutionBlockHash { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let hash = format!("{}", self.0); - write!( - f, - "{}…{}", - &hash[..6], - &hash[hash.len().saturating_sub(4)..] - ) + self.0.short().fmt(f) } } diff --git a/consensus/types/src/core/mod.rs b/consensus/types/src/core/mod.rs index 4e583fbc67..f722ac5191 100644 --- a/consensus/types/src/core/mod.rs +++ b/consensus/types/src/core/mod.rs @@ -49,3 +49,29 @@ pub type Hash64 = alloy_primitives::B64; pub type Address = alloy_primitives::Address; pub type VersionedHash = Hash256; pub type MerkleProof = Vec; + +/// Extension trait for `Hash256` to allow us to implement additional methods on it. +pub trait Hash256Ext { + fn short(&self) -> ShortenedHash<'_>; +} + +impl Hash256Ext for Hash256 { + fn short(&self) -> ShortenedHash<'_> { + ShortenedHash(self) + } +} + +pub struct ShortenedHash<'a>(&'a Hash256); + +impl<'a> std::fmt::Display for ShortenedHash<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let hash: &[u8; 32] = self.0.as_ref(); + write!( + f, + // Format as hex, padded to 2 digits per byte. + // This outputs a consistent "0x1234...abcd" format. + "0x{:02x}{:02x}…{:02x}{:02x}", + hash[0], hash[1], hash[30], hash[31] + ) + } +}