Merge remote-tracking branch 'origin/gloas-filter-conflicting-voluntairy-exits' into glamsterdam-devnet-0

This commit is contained in:
Eitan Seri-Levi
2026-04-28 10:39:26 +02:00
4 changed files with 73 additions and 22 deletions

View File

@@ -1,4 +1,4 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::Arc; use std::sync::Arc;
@@ -138,6 +138,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
graffiti_settings: GraffitiSettings, graffiti_settings: GraffitiSettings,
verification: ProduceBlockVerification, verification: ProduceBlockVerification,
) -> Result<BlockProductionResult<T::EthSpec>, BlockProductionError> { ) -> Result<BlockProductionResult<T::EthSpec>, 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) // Part 1/3 (blocking)
// //
// Perform the state advance and block-packing functions. // Perform the state advance and block-packing functions.
@@ -146,6 +156,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.graffiti_calculator .graffiti_calculator
.get_graffiti(graffiti_settings) .get_graffiti(graffiti_settings)
.await; .await;
let parent_execution_requests_ref = parent_execution_requests.clone();
let (partial_beacon_block, state) = self let (partial_beacon_block, state) = self
.task_executor .task_executor
.spawn_blocking_handle( .spawn_blocking_handle(
@@ -156,6 +167,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
produce_at_slot, produce_at_slot,
randao_reveal, randao_reveal,
graffiti, graffiti,
&parent_execution_requests_ref,
) )
}, },
"produce_partial_beacon_block_gloas", "produce_partial_beacon_block_gloas",
@@ -164,16 +176,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.await .await
.map_err(BlockProductionError::TokioJoin)??; .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) // Part 2/3 (async)
// //
// Produce the execution payload bid. // Produce the execution payload bid.
@@ -224,6 +226,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
produce_at_slot: Slot, produce_at_slot: Slot,
randao_reveal: Signature, randao_reveal: Signature,
graffiti: Graffiti, graffiti: Graffiti,
parent_execution_requests: &ExecutionRequests<T::EthSpec>,
) -> Result<(PartialBeaconBlock<T::EthSpec>, BeaconState<T::EthSpec>), BlockProductionError> ) -> Result<(PartialBeaconBlock<T::EthSpec>, BeaconState<T::EthSpec>), BlockProductionError>
{ {
// It is invalid to try to produce a block using a state from a future slot. // It is invalid to try to produce a block using a state from a future slot.
@@ -258,6 +261,31 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let (mut proposer_slashings, mut attester_slashings, mut voluntary_exits) = let (mut proposer_slashings, mut attester_slashings, mut voluntary_exits) =
self.op_pool.get_slashings_and_exits(&state, &self.spec); self.op_pool.get_slashings_and_exits(&state, &self.spec);
// Filter out voluntary exits that conflict with parent execution requests.
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); drop(slashings_and_exits_span);
let eth1_data = state.eth1_data().clone(); let eth1_data = state.eth1_data().clone();

View File

@@ -360,7 +360,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
let block_info = if current_slot > head_slot { let block_info = if current_slot > head_slot {
" … empty".to_string() " … empty".to_string()
} else { } else {
head_root.to_string() head_root.short().to_string()
}; };
let block_hash = match beacon_chain.canonical_head.head_execution_status() { let block_hash = match beacon_chain.canonical_head.head_execution_status() {
@@ -393,7 +393,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
info!( info!(
peers = peer_count_pretty(connected_peer_count), peers = peer_count_pretty(connected_peer_count),
exec_hash = block_hash, exec_hash = block_hash,
finalized_root = %finalized_checkpoint.root, finalized_root = %finalized_checkpoint.root.short(),
finalized_epoch = %finalized_checkpoint.epoch, finalized_epoch = %finalized_checkpoint.epoch,
epoch = %current_epoch, epoch = %current_epoch,
block = block_info, block = block_info,
@@ -404,7 +404,7 @@ pub fn spawn_notifier<T: BeaconChainTypes>(
metrics::set_gauge(&metrics::IS_SYNCED, 0); metrics::set_gauge(&metrics::IS_SYNCED, 0);
info!( info!(
peers = peer_count_pretty(connected_peer_count), peers = peer_count_pretty(connected_peer_count),
finalized_root = %finalized_checkpoint.root, finalized_root = %finalized_checkpoint.root.short(),
finalized_epoch = %finalized_checkpoint.epoch, finalized_epoch = %finalized_checkpoint.epoch,
%head_slot, %head_slot,
%current_slot, %current_slot,

View File

@@ -5,7 +5,10 @@ use rand::RngCore;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ssz::{Decode, DecodeError, Encode}; 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))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)] #[derive(Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq, Hash)]
@@ -20,13 +23,7 @@ impl fmt::Debug for ExecutionBlockHash {
impl fmt::Display for ExecutionBlockHash { impl fmt::Display for ExecutionBlockHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let hash = format!("{}", self.0); self.0.short().fmt(f)
write!(
f,
"{}…{}",
&hash[..6],
&hash[hash.len().saturating_sub(4)..]
)
} }
} }

View File

@@ -49,3 +49,29 @@ pub type Hash64 = alloy_primitives::B64;
pub type Address = alloy_primitives::Address; pub type Address = alloy_primitives::Address;
pub type VersionedHash = Hash256; pub type VersionedHash = Hash256;
pub type MerkleProof = Vec<Hash256>; pub type MerkleProof = Vec<Hash256>;
/// 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]
)
}
}