Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions crates/relay/src/api/proposer/get_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ use tracing::{Instrument, error, info, warn};
use super::ProposerApi;
use crate::{
api::{Api, proposer::error::ProposerApiError},
auctioneer::{GetPayloadResultData, PayloadBidData},
beacon::types::BroadcastValidation,
auctioneer::{Event, GetPayloadResultData, PayloadBidData},
beacon::{error::BeaconClientError, types::BroadcastValidation},
database::SavePayloadParams,
gossip::{BroadcastGetPayloadParams, BroadcastPayloadParams},
};
Expand Down Expand Up @@ -337,6 +337,25 @@ impl<A: Api> ProposerApi<A> {
)
.await
{
if matches!(err, BeaconClientError::BlockValidationFailed(..)) {
let builder_pubkey = bid.builder_pubkey;
let reason = format!("Block validation failed: {err}");

let _ = self_clone.auctioneer_handle.send_event(Event::BuilderDemotion {
slot,
builder_pubkey,
block_hash,
reason,
});

warn!(
%builder_pubkey,
%block_hash,
slot = %slot,
"BuilderDemotion event sent due to BlockValidationFailed"
);
}

error!(%err, "error publishing block");
failed_publishing = true;
};
Expand Down
32 changes: 32 additions & 0 deletions crates/relay/src/auctioneer/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,38 @@ impl Context {
};
self.payloads.insert(block_hash, payload);
}

pub fn handle_builder_demotion(
&mut self,
slot: Slot,
builder_pubkey: BlsPublicKeyBytes,
block_hash: B256,
reason: String,
) {
if self.cache.demote_builder(&builder_pubkey) {
warn!(%builder_pubkey, %block_hash, "builder demoted due to block validation failure");

let db = self.db.clone();
let failsafe = self.sim_manager.failsafe_triggered.clone();
let slot_u64 = slot.as_u64();

spawn_tracked!(async move {
if let Err(err) =
db.db_demote_builder(slot_u64, &builder_pubkey, &block_hash, reason).await
{
failsafe.store(true, Ordering::Relaxed);
error!(
%builder_pubkey,
%err,
%block_hash,
"failed to persist builder demotion in DB — pausing optimistic submissions"
);
}
});
} else {
warn!(%reason, %builder_pubkey, %block_hash, "builder already demoted, skipping demotion");
}
}
}

impl Deref for Context {
Expand Down
5 changes: 5 additions & 0 deletions crates/relay/src/auctioneer/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ impl AuctioneerHandle {
trace!("sending to worker");
self.auctioneer.try_send(Event::GossipPayload(req)).map_err(|_| ChannelFull)
}

pub fn send_event(&self, event: Event) -> Result<(), ChannelFull> {
trace!("sending event to auctioneer: {:?}", event.as_str());
self.auctioneer.try_send(event).map_err(|_| ChannelFull)
}
}

pub struct ChannelFull;
Expand Down
8 changes: 8 additions & 0 deletions crates/relay/src/auctioneer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,14 @@ impl State {
warn!(curr =% bid_slot, gossip_slot = payload.slot, "received early or late gossip payload");
}
}

// Builder demotion event
(
State::Slot { .. } | State::Sorting(_) | State::Broadcasting { .. },
Event::BuilderDemotion { slot, builder_pubkey, block_hash, reason },
) => {
ctx.handle_builder_demotion(slot, builder_pubkey, block_hash, reason);
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions crates/relay/src/auctioneer/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ pub enum Event {
is_synced: bool,
},
MergeResult(BlockMergeResult),
BuilderDemotion {
slot: Slot,
builder_pubkey: BlsPublicKeyBytes,
block_hash: B256,
reason: String,
},
}

impl Event {
Expand All @@ -304,6 +310,7 @@ impl Event {
Event::SimResult(_) => "SimResult",
Event::SimulatorSync { .. } => "SimulatorSync",
Event::MergeResult(_) => "MergeResult",
Event::BuilderDemotion { .. } => "BuilderDemotion",
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions crates/relay/src/beacon/beacon_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ impl BeaconClient {
if body.contains("duplicate block") {
return Ok(200);
}
if body.contains("ExecutionPayloadError(RejectedByExecutionEngine") &&
(body.contains("block hash mismatch") || body.contains("validation_error"))
{
return Err(BeaconClientError::BlockValidationFailed(body));
}
Ok(code)
}
_ => {
Expand Down
3 changes: 3 additions & 0 deletions crates/relay/src/beacon/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub enum BeaconClientError {

#[error("block integration failed")]
BlockIntegrationFailed,

#[error("block validation failed: {0}")]
BlockValidationFailed(String),
}

impl IntoResponse for BeaconClientError {
Expand Down
4 changes: 4 additions & 0 deletions crates/relay/src/beacon/multi_beacon_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ impl MultiBeaconClient {
return Ok(());
}

Err(BeaconClientError::BlockValidationFailed(details)) => {
last_error = Some(BeaconClientError::BlockValidationFailed(details));
}

Err(err) => {
last_error = Some(err);
}
Expand Down