Skip to content

Commit c90ba02

Browse files
authored
Handle execution payload bid (#486)
* Validate execution payload bid * Handle execution payload bid * Handle execution payload bid from gossip * Restructure accepted payload bids * Update payload attestation and payload bid eventstream structure * Store full signed bid * addressed Povi reviews
1 parent 219e4bf commit c90ba02

File tree

15 files changed

+463
-31
lines changed

15 files changed

+463
-31
lines changed

fork_choice_control/src/controller.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use eth2_libp2p::{GossipId, PeerId};
2424
use execution_engine::{ExecutionEngine, PayloadStatusV1};
2525
use fork_choice_store::{
2626
AggregateAndProofOrigin, AttestationItem, AttestationOrigin, AttesterSlashingOrigin,
27-
BlobSidecarOrigin, BlockOrigin, DataColumnSidecarOrigin, PayloadAttestationOrigin,
28-
StateCacheProcessor, Store, StoreConfig,
27+
BlobSidecarOrigin, BlockOrigin, DataColumnSidecarOrigin, ExecutionPayloadBidOrigin,
28+
PayloadAttestationOrigin, StateCacheProcessor, Store, StoreConfig,
2929
};
3030
use futures::channel::{mpsc::Sender as MultiSender, oneshot::Sender as OneshotSender};
3131
use genesis::AnchorCheckpointProvider;
@@ -43,7 +43,9 @@ use types::{
4343
config::Config as ChainConfig,
4444
deneb::containers::BlobSidecar,
4545
fulu::primitives::ColumnIndex,
46-
gloas::containers::{PayloadAttestationMessage, SignedExecutionPayloadEnvelope},
46+
gloas::containers::{
47+
PayloadAttestationMessage, SignedExecutionPayloadBid, SignedExecutionPayloadEnvelope,
48+
},
4749
nonstandard::ValidationOutcome,
4850
phase0::primitives::{ExecutionBlockHash, Slot, SubnetId, H256},
4951
preset::Preset,
@@ -63,8 +65,8 @@ use crate::{
6365
storage::Storage,
6466
tasks::{
6567
AggregateAndProofTask, AttestationTask, AttesterSlashingTask, BlobSidecarTask, BlockTask,
66-
BlockVerifyForGossipTask, DataColumnSidecarTask, PayloadAttestationTask,
67-
StateAtSlotCacheFlushTask,
68+
BlockVerifyForGossipTask, DataColumnSidecarTask, ExecutionPayloadBidTask,
69+
PayloadAttestationTask, StateAtSlotCacheFlushTask,
6870
},
6971
thread_pool::{Spawn, ThreadPool},
7072
unbounded_sink::UnboundedSink,
@@ -362,6 +364,25 @@ where
362364
})
363365
}
364366

367+
pub fn on_gossip_execution_payload_bid(
368+
&self,
369+
payload_bid: Arc<SignedExecutionPayloadBid>,
370+
gossip_id: GossipId,
371+
) {
372+
self.spawn_execution_payload_bid_task(
373+
payload_bid,
374+
ExecutionPayloadBidOrigin::Gossip(gossip_id),
375+
);
376+
}
377+
378+
pub fn on_api_execution_payload_bid(
379+
&self,
380+
payload_bid: Arc<SignedExecutionPayloadBid>,
381+
sender: OneshotSender<Result<ValidationOutcome>>,
382+
) {
383+
self.spawn_execution_payload_bid_task(payload_bid, ExecutionPayloadBidOrigin::Api(sender))
384+
}
385+
365386
pub fn on_notified_fork_choice_update(&self, payload_status: PayloadStatusV1) {
366387
MutatorMessage::NotifiedForkChoiceUpdate {
367388
wait_group: self.owned_wait_group(),
@@ -799,6 +820,20 @@ where
799820
})
800821
}
801822

823+
fn spawn_execution_payload_bid_task(
824+
&self,
825+
payload_bid: Arc<SignedExecutionPayloadBid>,
826+
origin: ExecutionPayloadBidOrigin,
827+
) {
828+
self.spawn(ExecutionPayloadBidTask {
829+
store_snapshot: self.owned_store_snapshot(),
830+
mutator_tx: self.owned_mutator_tx(),
831+
wait_group: self.owned_wait_group(),
832+
payload_bid,
833+
origin,
834+
})
835+
}
836+
802837
fn spawn_block_task(&self, block: Arc<SignedBeaconBlock<P>>, origin: BlockOrigin) {
803838
self.spawn_block_task_with_wait_group(self.owned_wait_group(), block, origin)
804839
}

fork_choice_control/src/events.rs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use types::{
2424
primitives::{BlobIndex, KzgCommitment, VersionedHash},
2525
},
2626
fulu::primitives::ColumnIndex,
27-
gloas::containers::PayloadAttestationMessage,
27+
gloas::containers::{ExecutionPayloadBid, PayloadAttestationData, PayloadAttestationMessage},
2828
nonstandard::Phase,
2929
phase0::{
3030
containers::{Checkpoint, ProposerSlashing, SignedVoluntaryExit},
@@ -50,6 +50,7 @@ pub enum Topic {
5050
ChainReorg,
5151
ContributionAndProof,
5252
DataColumnSidecar,
53+
ExecutionPayloadBid,
5354
FinalizedCheckpoint,
5455
Head,
5556
PayloadAttestation,
@@ -68,9 +69,10 @@ pub enum Event<P: Preset> {
6869
ChainReorg(ChainReorgEvent),
6970
ContributionAndProof(Box<SignedContributionAndProof<P>>),
7071
DataColumnSidecar(DataColumnSidecarEvent<P>),
72+
ExecutionPayloadBid(ExecutionPayloadBidEvent),
7173
FinalizedCheckpoint(FinalizedCheckpointEvent),
7274
Head(HeadEvent),
73-
PayloadAttestation(Arc<PayloadAttestationMessage>),
75+
PayloadAttestation(PayloadAttestationEvent),
7476
PayloadAttributes(PayloadAttributesEvent),
7577
ProposerSlashing(Box<ProposerSlashing>),
7678
VoluntaryExit(Box<SignedVoluntaryExit>),
@@ -88,6 +90,7 @@ impl<P: Preset> Event<P> {
8890
Self::ChainReorg(_) => Topic::ChainReorg,
8991
Self::ContributionAndProof(_) => Topic::ContributionAndProof,
9092
Self::DataColumnSidecar(_) => Topic::DataColumnSidecar,
93+
Self::ExecutionPayloadBid(_) => Topic::ExecutionPayloadBid,
9194
Self::FinalizedCheckpoint(_) => Topic::FinalizedCheckpoint,
9295
Self::Head(_) => Topic::Head,
9396
Self::PayloadAttestation(_) => Topic::PayloadAttestation,
@@ -109,6 +112,7 @@ pub struct EventChannels<P: Preset> {
109112
pub chain_reorgs: Sender<Event<P>>,
110113
pub contribution_and_proofs: Sender<Event<P>>,
111114
pub data_column_sidecars: Sender<Event<P>>,
115+
pub execution_payload_bids: Sender<Event<P>>,
112116
pub finalized_checkpoints: Sender<Event<P>>,
113117
pub heads: Sender<Event<P>>,
114118
pub payload_attestations: Sender<Event<P>>,
@@ -137,6 +141,7 @@ impl<P: Preset> EventChannels<P> {
137141
chain_reorgs: broadcast::channel(max_events).0,
138142
contribution_and_proofs: broadcast::channel(max_events).0,
139143
data_column_sidecars: broadcast::channel(max_events).0,
144+
execution_payload_bids: broadcast::channel(max_events).0,
140145
finalized_checkpoints: broadcast::channel(max_events).0,
141146
heads: broadcast::channel(max_events).0,
142147
payload_attestations: broadcast::channel(max_events).0,
@@ -158,6 +163,7 @@ impl<P: Preset> EventChannels<P> {
158163
Topic::ChainReorg => &self.chain_reorgs,
159164
Topic::ContributionAndProof => &self.contribution_and_proofs,
160165
Topic::DataColumnSidecar => &self.data_column_sidecars,
166+
Topic::ExecutionPayloadBid => &self.execution_payload_bids,
161167
Topic::FinalizedCheckpoint => &self.finalized_checkpoints,
162168
Topic::Head => &self.heads,
163169
Topic::PayloadAttestation => &self.payload_attestations,
@@ -246,6 +252,12 @@ impl<P: Preset> EventChannels<P> {
246252
}
247253
}
248254

255+
pub fn send_execution_payload_bid_event(&self, payload_bid: ExecutionPayloadBid) {
256+
if let Err(error) = self.send_execution_payload_bid_event_internal(payload_bid) {
257+
warn_with_peers!("unable to send execution payload bid event: {error}");
258+
}
259+
}
260+
249261
pub fn send_finalized_checkpoint_event(
250262
&self,
251263
block_root: H256,
@@ -449,6 +461,19 @@ impl<P: Preset> EventChannels<P> {
449461
Ok(())
450462
}
451463

464+
fn send_execution_payload_bid_event_internal(
465+
&self,
466+
payload_bid: ExecutionPayloadBid,
467+
) -> Result<()> {
468+
if self.execution_payload_bids.receiver_count() > 0 {
469+
let payload_bid_event = ExecutionPayloadBidEvent::new(payload_bid);
470+
let event = Event::ExecutionPayloadBid(payload_bid_event);
471+
self.execution_payload_bids.send(event)?;
472+
}
473+
474+
Ok(())
475+
}
476+
452477
fn send_finalized_checkpoint_event_internal(
453478
&self,
454479
block_root: H256,
@@ -491,7 +516,8 @@ impl<P: Preset> EventChannels<P> {
491516
payload_attestation: Arc<PayloadAttestationMessage>,
492517
) -> Result<()> {
493518
if self.payload_attestations.receiver_count() > 0 {
494-
let event = Event::PayloadAttestation(payload_attestation);
519+
let payload_attestation_event = PayloadAttestationEvent::new(payload_attestation);
520+
let event = Event::PayloadAttestation(payload_attestation_event);
495521
self.payload_attestations.send(event)?;
496522
}
497523

@@ -668,6 +694,30 @@ impl ChainReorgEvent {
668694
}
669695
}
670696

697+
#[derive(Clone, Copy, Debug, Serialize)]
698+
pub struct ExecutionPayloadBidEvent {
699+
#[serde(with = "serde_utils::string_or_native")]
700+
pub slot: Slot,
701+
#[serde(with = "serde_utils::string_or_native")]
702+
pub builder_index: ValidatorIndex,
703+
pub parent_block_root: H256,
704+
pub block_hash: ExecutionBlockHash,
705+
#[serde(with = "serde_utils::string_or_native")]
706+
pub value: Gwei,
707+
}
708+
709+
impl ExecutionPayloadBidEvent {
710+
fn new(payload_bid: ExecutionPayloadBid) -> Self {
711+
Self {
712+
slot: payload_bid.slot,
713+
builder_index: payload_bid.builder_index,
714+
parent_block_root: payload_bid.parent_block_root,
715+
block_hash: payload_bid.block_hash,
716+
value: payload_bid.value,
717+
}
718+
}
719+
}
720+
671721
#[derive(Clone, Copy, Debug, Serialize)]
672722
pub struct FinalizedCheckpointEvent {
673723
pub block: H256,
@@ -710,6 +760,23 @@ impl HeadEvent {
710760
}
711761
}
712762

763+
#[derive(Clone, Copy, Debug, Serialize)]
764+
pub struct PayloadAttestationEvent {
765+
#[serde(with = "serde_utils::string_or_native")]
766+
pub validator_index: ValidatorIndex,
767+
#[serde(flatten)]
768+
pub data: PayloadAttestationData,
769+
}
770+
771+
impl PayloadAttestationEvent {
772+
fn new(payload_attestation: Arc<PayloadAttestationMessage>) -> Self {
773+
Self {
774+
validator_index: payload_attestation.validator_index,
775+
data: payload_attestation.data,
776+
}
777+
}
778+
}
779+
713780
#[derive(Clone, Debug, Serialize)]
714781
pub struct PayloadAttributesEvent {
715782
pub version: Phase,

fork_choice_control/src/messages.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use execution_engine::PayloadStatusV1;
1111
use fork_choice_store::{
1212
AggregateAndProofOrigin, AttestationAction, AttestationItem, AttestationValidationError,
1313
AttesterSlashingOrigin, BlobSidecarAction, BlobSidecarOrigin, BlockAction, BlockOrigin,
14-
ChainLink, DataColumnSidecarAction, DataColumnSidecarOrigin, PayloadAttestationAction,
15-
PayloadAttestationOrigin,
14+
ChainLink, DataColumnSidecarAction, DataColumnSidecarOrigin, ExecutionPayloadBidAction,
15+
ExecutionPayloadBidOrigin, PayloadAttestationAction, PayloadAttestationOrigin,
1616
};
1717
use logging::debug_with_peers;
1818
use serde::Serialize;
@@ -159,6 +159,11 @@ pub enum MutatorMessage<P: Preset, W> {
159159
result: Result<PayloadAttestationAction>,
160160
origin: PayloadAttestationOrigin,
161161
},
162+
PayloadBid {
163+
wait_group: W,
164+
result: Result<ExecutionPayloadBidAction>,
165+
origin: ExecutionPayloadBidOrigin,
166+
},
162167
PreprocessedBeaconState {
163168
state: Arc<BeaconState<P>>,
164169
},

fork_choice_control/src/misc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ pub enum MutatorRejectionReason {
204204
data_column_identifier: DataColumnIdentifier,
205205
},
206206
InvalidPayloadAttestation,
207+
InvalidPayloadBid,
207208
}
208209

209210
#[derive(Clone, Copy, Debug)]

fork_choice_control/src/mutator.rs

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ use fork_choice_store::{
3636
AggregateAndProofAction, ApplyBlockChanges, ApplyTickChanges, AttestationAction,
3737
AttestationItem, AttestationOrigin, AttestationValidationError, AttesterSlashingOrigin,
3838
BlobSidecarAction, BlobSidecarOrigin, BlockAction, BlockOrigin, ChainLink,
39-
DataColumnSidecarAction, DataColumnSidecarOrigin, Error, PayloadAction,
40-
PayloadAttestationAction, PayloadAttestationOrigin, StateCacheProcessor, Store,
41-
ValidAttestation,
39+
DataColumnSidecarAction, DataColumnSidecarOrigin, Error, ExecutionPayloadBidAction,
40+
ExecutionPayloadBidOrigin, PayloadAction, PayloadAttestationAction, PayloadAttestationOrigin,
41+
StateCacheProcessor, Store, ValidAttestation,
4242
};
4343
use futures::channel::{mpsc::Sender as MultiSender, oneshot::Sender as OneshotSender};
4444
use helper_functions::{accessors, misc, predicates, verifier::NullVerifier};
@@ -319,6 +319,11 @@ where
319319
result,
320320
origin,
321321
} => self.handle_payload_attestation(&wait_group, result, origin)?,
322+
MutatorMessage::PayloadBid {
323+
wait_group,
324+
result,
325+
origin,
326+
} => self.handle_payload_bid(&wait_group, result, origin),
322327
MutatorMessage::PreprocessedBeaconState { state } => {
323328
self.prepare_execution_payload_for_next_slot(&state);
324329
}
@@ -1878,6 +1883,59 @@ where
18781883
Ok(())
18791884
}
18801885

1886+
#[expect(clippy::too_many_lines)]
1887+
fn handle_payload_bid(
1888+
&mut self,
1889+
wait_group: &W,
1890+
result: Result<ExecutionPayloadBidAction>,
1891+
origin: ExecutionPayloadBidOrigin,
1892+
) {
1893+
match result {
1894+
Ok(ExecutionPayloadBidAction::Accept(payload_bid)) => {
1895+
trace_with_peers!("payload bid accepted (payload_bid: {payload_bid:?})");
1896+
1897+
self.event_channels
1898+
.send_execution_payload_bid_event(payload_bid.message);
1899+
1900+
let (gossip_id, sender) = origin.split();
1901+
1902+
if let Some(gossip_id) = gossip_id {
1903+
self.send_to_p2p(P2pMessage::Accept(gossip_id));
1904+
}
1905+
1906+
reply_to_http_api(sender, Ok(ValidationOutcome::Accept));
1907+
1908+
self.store_mut().apply_execution_payload_bid(payload_bid);
1909+
1910+
self.update_store_snapshot();
1911+
}
1912+
Ok(ExecutionPayloadBidAction::Ignore(publishable)) => {
1913+
let (gossip_id, sender) = origin.split();
1914+
1915+
if let Some(gossip_id) = gossip_id {
1916+
self.send_to_p2p(P2pMessage::Ignore(gossip_id));
1917+
}
1918+
1919+
reply_to_http_api(sender, Ok(ValidationOutcome::Ignore(publishable)));
1920+
}
1921+
Err(error) => {
1922+
let source = error.to_string();
1923+
warn_with_peers!("payload bid rejected (error: {error:?})",);
1924+
1925+
let (gossip_id, sender) = origin.split();
1926+
1927+
if gossip_id.is_some() {
1928+
self.send_to_p2p(P2pMessage::Reject(
1929+
gossip_id,
1930+
MutatorRejectionReason::InvalidPayloadBid,
1931+
));
1932+
}
1933+
1934+
reply_to_http_api(sender, Err(anyhow!(source)));
1935+
}
1936+
}
1937+
}
1938+
18811939
fn handle_checkpoint_state(
18821940
&mut self,
18831941
wait_group: &W,

0 commit comments

Comments
 (0)