From 79549d38d49d54c26773755e285f4e2797c44094 Mon Sep 17 00:00:00 2001 From: hangleang Date: Thu, 14 May 2026 16:21:25 +0700 Subject: [PATCH] Add execution_payload and execution_payload_gossip event --- fork_choice_control/src/events.rs | 116 ++++++++++++++++++++++++++++- fork_choice_control/src/mutator.rs | 27 ++++++- fork_choice_store/src/misc.rs | 5 ++ http_api/src/standard.rs | 2 + 4 files changed, 148 insertions(+), 2 deletions(-) diff --git a/fork_choice_control/src/events.rs b/fork_choice_control/src/events.rs index c455fff03..cf8e3847e 100644 --- a/fork_choice_control/src/events.rs +++ b/fork_choice_control/src/events.rs @@ -25,7 +25,10 @@ use types::{ primitives::{BlobIndex, KzgCommitment, VersionedHash}, }, fulu::primitives::ColumnIndex, - gloas::containers::{PayloadAttestationMessage, SignedExecutionPayloadBid}, + gloas::{ + containers::{PayloadAttestationMessage, SignedExecutionPayloadBid}, + primitives::BuilderIndex, + }, nonstandard::Phase, phase0::{ containers::{Checkpoint, ProposerSlashing, SignedVoluntaryExit}, @@ -52,8 +55,10 @@ pub enum Topic { ChainReorg, ContributionAndProof, DataColumnSidecar, + ExecutionPayload, ExecutionPayloadBid, ExecutionPayloadAvailable, + ExecutionPayloadGossip, FinalizedCheckpoint, Head, PayloadAttestation, @@ -73,8 +78,10 @@ pub enum Event { ChainReorg(ChainReorgEvent), ContributionAndProof(Box>), DataColumnSidecar(DataColumnSidecarEvent

), + ExecutionPayload(ExecutionPayloadEvent), ExecutionPayloadAvailable(ExecutionPayloadAvailableEvent), ExecutionPayloadBid(ExecutionPayloadBidEvent

), + ExecutionPayloadGossip(ExecutionPayloadGossipEvent), FinalizedCheckpoint(FinalizedCheckpointEvent), Head(HeadEvent), PayloadAttestation(PayloadAttestationEvent), @@ -96,8 +103,10 @@ impl Event

{ Self::ChainReorg(_) => Topic::ChainReorg, Self::ContributionAndProof(_) => Topic::ContributionAndProof, Self::DataColumnSidecar(_) => Topic::DataColumnSidecar, + Self::ExecutionPayload(_) => Topic::ExecutionPayload, Self::ExecutionPayloadAvailable(_) => Topic::ExecutionPayloadAvailable, Self::ExecutionPayloadBid(_) => Topic::ExecutionPayloadBid, + Self::ExecutionPayloadGossip(_) => Topic::ExecutionPayloadGossip, Self::FinalizedCheckpoint(_) => Topic::FinalizedCheckpoint, Self::Head(_) => Topic::Head, Self::PayloadAttestation(_) => Topic::PayloadAttestation, @@ -120,8 +129,10 @@ pub struct EventChannels { pub chain_reorgs: Sender>, pub contribution_and_proofs: Sender>, pub data_column_sidecars: Sender>, + pub execution_payloads: Sender>, pub execution_payload_available: Sender>, pub execution_payload_bids: Sender>, + pub execution_payloads_gossip: Sender>, pub finalized_checkpoints: Sender>, pub heads: Sender>, pub payload_attestations: Sender>, @@ -151,8 +162,10 @@ impl EventChannels

{ chain_reorgs: broadcast::channel(max_events).0, contribution_and_proofs: broadcast::channel(max_events).0, data_column_sidecars: broadcast::channel(max_events).0, + execution_payloads: broadcast::channel(max_events).0, execution_payload_available: broadcast::channel(max_events).0, execution_payload_bids: broadcast::channel(max_events).0, + execution_payloads_gossip: broadcast::channel(max_events).0, finalized_checkpoints: broadcast::channel(max_events).0, heads: broadcast::channel(max_events).0, payload_attestations: broadcast::channel(max_events).0, @@ -175,8 +188,10 @@ impl EventChannels

{ Topic::ChainReorg => &self.chain_reorgs, Topic::ContributionAndProof => &self.contribution_and_proofs, Topic::DataColumnSidecar => &self.data_column_sidecars, + Topic::ExecutionPayload => &self.execution_payloads, Topic::ExecutionPayloadAvailable => &self.execution_payload_available, Topic::ExecutionPayloadBid => &self.execution_payload_bids, + Topic::ExecutionPayloadGossip => &self.execution_payloads_gossip, Topic::FinalizedCheckpoint => &self.finalized_checkpoints, Topic::Head => &self.heads, Topic::PayloadAttestation => &self.payload_attestations, @@ -271,6 +286,42 @@ impl EventChannels

{ } } + pub fn send_execution_payload_event( + &self, + slot: Slot, + builder_index: BuilderIndex, + block_hash: ExecutionBlockHash, + block_root: H256, + execution_optimistic: bool, + ) { + if let Err(error) = self.send_execution_payload_event_internal( + slot, + builder_index, + block_hash, + block_root, + execution_optimistic, + ) { + warn_with_peers!("unable to send execution payload event: {error}"); + } + } + + pub fn send_execution_payload_gossip_event( + &self, + slot: Slot, + builder_index: BuilderIndex, + block_hash: ExecutionBlockHash, + block_root: H256, + ) { + if let Err(error) = self.send_execution_payload_gossip_event_internal( + slot, + builder_index, + block_hash, + block_root, + ) { + warn_with_peers!("unable to send execution payload gossip event: {error}"); + } + } + pub fn send_execution_payload_available_event(&self, slot: Slot, block_root: H256) { if let Err(error) = self.send_execution_payload_available_event_internal(slot, block_root) { warn_with_peers!("unable to send execution payload available event: {error}"); @@ -505,6 +556,48 @@ impl EventChannels

{ Ok(()) } + fn send_execution_payload_event_internal( + &self, + slot: Slot, + builder_index: BuilderIndex, + block_hash: ExecutionBlockHash, + block_root: H256, + execution_optimistic: bool, + ) -> Result<()> { + if self.execution_payloads.receiver_count() > 0 { + let event = Event::ExecutionPayload(ExecutionPayloadEvent { + slot, + builder_index, + block_hash, + block_root, + execution_optimistic, + }); + self.execution_payloads.send(event)?; + } + + Ok(()) + } + + fn send_execution_payload_gossip_event_internal( + &self, + slot: Slot, + builder_index: BuilderIndex, + block_hash: ExecutionBlockHash, + block_root: H256, + ) -> Result<()> { + if self.execution_payloads_gossip.receiver_count() > 0 { + let event = Event::ExecutionPayloadGossip(ExecutionPayloadGossipEvent { + slot, + builder_index, + block_hash, + block_root, + }); + self.execution_payloads_gossip.send(event)?; + } + + Ok(()) + } + fn send_execution_payload_available_event_internal( &self, slot: Slot, @@ -713,6 +806,27 @@ impl DataColumnSidecarEvent

{ } } +#[derive(Clone, Copy, Debug, Serialize)] +pub struct ExecutionPayloadEvent { + #[serde(with = "serde_utils::string_or_native")] + pub slot: Slot, + #[serde(with = "serde_utils::string_or_native")] + pub builder_index: BuilderIndex, + pub block_hash: ExecutionBlockHash, + pub block_root: H256, + pub execution_optimistic: bool, +} + +#[derive(Clone, Copy, Debug, Serialize)] +pub struct ExecutionPayloadGossipEvent { + #[serde(with = "serde_utils::string_or_native")] + pub slot: Slot, + #[serde(with = "serde_utils::string_or_native")] + pub builder_index: BuilderIndex, + pub block_hash: ExecutionBlockHash, + pub block_root: H256, +} + #[derive(Clone, Copy, Debug, Serialize)] pub struct ExecutionPayloadAvailableEvent { #[serde(with = "serde_utils::string_or_native")] diff --git a/fork_choice_control/src/mutator.rs b/fork_choice_control/src/mutator.rs index cd11f8583..d94261311 100644 --- a/fork_choice_control/src/mutator.rs +++ b/fork_choice_control/src/mutator.rs @@ -2005,16 +2005,29 @@ where let beacon_block_root = envelope.block_root(); let slot = envelope.slot(); + let builder_index = envelope.builder_index(); + let block_hash = envelope.message.payload.block_hash; + let should_send_gossip_event = origin.should_send_gossip_event(); + let should_generate_event = origin.should_generate_event(); debug_with_peers!( "execution payload envelope accepted (beacon_block_root: {beacon_block_root:?}, slot: {slot})" ); - if origin.should_generate_event() { + if should_generate_event { self.event_channels .send_execution_payload_available_event(slot, beacon_block_root); } + if should_send_gossip_event { + self.event_channels.send_execution_payload_gossip_event( + slot, + builder_index, + block_hash, + beacon_block_root, + ); + } + let (gossip_id, sender) = origin.split(); if let Some(gossip_id) = gossip_id { @@ -2027,6 +2040,18 @@ where ); self.accept_execution_payload_envelope(&wait_group, envelope); + + if should_generate_event { + if let Some(chain_link) = self.store.chain_link(beacon_block_root) { + self.event_channels.send_execution_payload_event( + slot, + builder_index, + block_hash, + beacon_block_root, + chain_link.is_optimistic(), + ); + } + } } Ok(ExecutionPayloadEnvelopeAction::Ignore(publishable)) => { if let Some(metrics) = self.metrics.as_ref() { diff --git a/fork_choice_store/src/misc.rs b/fork_choice_store/src/misc.rs index d4c4b4207..41ed17d4d 100644 --- a/fork_choice_store/src/misc.rs +++ b/fork_choice_store/src/misc.rs @@ -1129,6 +1129,11 @@ impl ExecutionPayloadEnvelopeOrigin { matches!(self, Self::Gossip(_) | Self::Api(_) | Self::Own) } + #[must_use] + pub const fn should_send_gossip_event(&self) -> bool { + matches!(self, Self::Gossip(_) | Self::Api(_)) + } + #[must_use] pub const fn verify_signatures(&self) -> bool { match self { diff --git a/http_api/src/standard.rs b/http_api/src/standard.rs index bf3f6a245..79ed3294e 100644 --- a/http_api/src/standard.rs +++ b/http_api/src/standard.rs @@ -2586,8 +2586,10 @@ pub async fn beacon_events( Event::ChainReorg(data) => ssevent.json_data(data), Event::ContributionAndProof(data) => ssevent.json_data(data), Event::DataColumnSidecar(data) => ssevent.json_data(data), + Event::ExecutionPayload(data) => ssevent.json_data(data), Event::ExecutionPayloadAvailable(data) => ssevent.json_data(data), Event::ExecutionPayloadBid(data) => ssevent.json_data(data), + Event::ExecutionPayloadGossip(data) => ssevent.json_data(data), Event::FinalizedCheckpoint(data) => ssevent.json_data(data), Event::Head(data) => ssevent.json_data(data), Event::PayloadAttestation(data) => ssevent.json_data(data),