diff --git a/crates/payload/basic/src/builder_stack.rs b/crates/payload/basic/src/builder_stack.rs index 63f207ceb2d1..4b50b2a14f52 100644 --- a/crates/payload/basic/src/builder_stack.rs +++ b/crates/payload/basic/src/builder_stack.rs @@ -2,143 +2,119 @@ use crate::{ BuildArguments, BuildOutcome, PayloadBuilder, PayloadBuilderError, PayloadConfig, PayloadBuilderAttributes }; -use futures_util::future::Either as EitherFuture; -use alloy_primitives::B256; +use alloy_primitives::{Address, B256}; use reth_payload_builder::PayloadId; use reth_payload_primitives::BuiltPayload; -use reth_primitives::{SealedBlock, U256}; +use reth_primitives::{SealedBlock, Withdrawals, U256}; use std::fmt; +use std::error::Error; -/// wrapper for either error -#[derive(Debug)] -pub enum EitherError { - /// left error +/// hand rolled Either enum to handle two builder types +#[derive(Debug, Clone)] +pub enum Either { + /// left variant Left(L), - /// right error + /// right variant Right(R), } -impl fmt::Display for EitherError +impl fmt::Display for Either where L: fmt::Display, R: fmt::Display, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - EitherError::Left(l) => write!(f, "Left error: {}", l), - EitherError::Right(r) => write!(f, "Right error: {}", r), + Either::Left(l) => write!(f, "Left: {}", l), + Either::Right(r) => write!(f, "Right: {}", r), } } } -impl std::error::Error for EitherError -where - L: std::error::Error, - R: std::error::Error, -{} - -/// represents attributes that can be either left or right type. -#[derive(Debug, Clone)] -pub struct EitherAttributes(EitherFuture); - -/// implement PayloadBuilderAttributes for EitherAttributes for use in PayloadBuilderStack -impl PayloadBuilderAttributes for EitherAttributes +impl Error for Either where - LAttr: PayloadBuilderAttributes, - RAttr: PayloadBuilderAttributes, + L: Error + 'static, + R: Error + 'static, { - type RpcPayloadAttributes = EitherFuture< - LAttr::RpcPayloadAttributes, - RAttr::RpcPayloadAttributes, - >; - type Error = EitherError; - - fn try_new( - parent: B256, - rpc_payload_attributes: Self::RpcPayloadAttributes, - ) -> Result { - match rpc_payload_attributes { - EitherFuture::Left(attr) => LAttr::try_new(parent, attr) - .map(|attr| EitherAttributes(EitherFuture::Left(attr))) - .map_err(EitherError::Left), - EitherFuture::Right(attr) => RAttr::try_new(parent, attr) - .map(|attr| EitherAttributes(EitherFuture::Right(attr))) - .map_err(EitherError::Right), - } - } - - fn payload_id(&self) -> PayloadId { - match &self.0 { - EitherFuture::Left(attr) => attr.payload_id(), - EitherFuture::Right(attr) => attr.payload_id(), - } - } - - fn parent(&self) -> B256 { - match &self.0 { - EitherFuture::Left(attr) => attr.parent(), - EitherFuture::Right(attr) => attr.parent(), - } - } - - fn timestamp(&self) -> u64 { - match &self.0 { - EitherFuture::Left(attr) => attr.timestamp(), - EitherFuture::Right(attr) => attr.timestamp(), - } - } - - fn parent_beacon_block_root(&self) -> Option { - match &self.0 { - EitherFuture::Left(attr) => attr.parent_beacon_block_root(), - EitherFuture::Right(attr) => attr.parent_beacon_block_root(), + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Either::Left(l) => Some(l), + Either::Right(r) => Some(r), } } +} - fn suggested_fee_recipient(&self) -> alloy_primitives::Address { - match &self.0 { - EitherFuture::Left(attr) => attr.suggested_fee_recipient(), - EitherFuture::Right(attr) => attr.suggested_fee_recipient(), - } - } +impl PayloadBuilderAttributes for Either + where + L: PayloadBuilderAttributes, + R: PayloadBuilderAttributes, + L::Error: Error + 'static, + R::Error: Error + 'static, + { + type RpcPayloadAttributes = Either; + type Error = Either; + + fn try_new( + parent: B256, + rpc_payload_attributes: Self::RpcPayloadAttributes, + ) -> Result { + match rpc_payload_attributes { + Either::Left(attr) => L::try_new(parent, attr).map(Either::Left).map_err(Either::Left), + Either::Right(attr) => R::try_new(parent, attr).map(Either::Right).map_err(Either::Right), + } + } + + fn payload_id(&self) -> PayloadId { + match self { + Either::Left(l) => l.payload_id(), + Either::Right(r) => r.payload_id(), + } + } - fn prev_randao(&self) -> B256 { - match &self.0 { - EitherFuture::Left(attr) => attr.prev_randao(), - EitherFuture::Right(attr) => attr.prev_randao(), - } - } + fn parent(&self) -> B256 { + match self { + Either::Left(l) => l.parent(), + Either::Right(r) => r.parent(), + } + } + + fn timestamp(&self) -> u64 { + match self { + Either::Left(l) => l.timestamp(), + Either::Right(r) => r.timestamp(), + } + } - fn withdrawals(&self) -> &reth_primitives::Withdrawals { - match &self.0 { - EitherFuture::Left(attr) => attr.withdrawals(), - EitherFuture::Right(attr) => attr.withdrawals(), - } - } -} + fn parent_beacon_block_root(&self) -> Option { + match self { + Either::Left(l) => l.parent_beacon_block_root(), + Either::Right(r) => r.parent_beacon_block_root(), + } + } -impl BuiltPayload for EitherAttributes -where - L: BuiltPayload, - R: BuiltPayload, -{ - fn block(&self) -> &SealedBlock { - match &self.0 { - EitherFuture::Left(l) => l.block(), - EitherFuture::Right(r) => r.block(), - } - } + fn suggested_fee_recipient(&self) -> Address { + match self { + Either::Left(l) => l.suggested_fee_recipient(), + Either::Right(r) => r.suggested_fee_recipient(), + } + } - fn fees(&self) -> U256 { - match &self.0 { - EitherFuture::Left(l) => l.fees(), - EitherFuture::Right(r) => r.fees(), - } - } -} + fn prev_randao(&self) -> B256 { + match self { + Either::Left(l) => l.prev_randao(), + Either::Right(r) => r.prev_randao(), + } + } + fn withdrawals(&self) -> &Withdrawals { + match self { + Either::Left(l) => l.withdrawals(), + Either::Right(r) => r.withdrawals(), + } + } + } /// this structure enables the chaining of multiple `PayloadBuilder` implementations, /// creating a hierarchical fallback system. It's designed to be nestable, allowing @@ -166,6 +142,26 @@ where } } +impl BuiltPayload for Either +where + L: BuiltPayload, + R: BuiltPayload, +{ + fn block(&self) -> &SealedBlock { + match self { + Either::Left(l) => l.block(), + Either::Right(r) => r.block(), + } + } + + fn fees(&self) -> U256 { + match self { + Either::Left(l) => l.fees(), + Either::Right(r) => r.fees(), + } + } +} + impl BuildOutcome { fn map_payload(self, f: F) -> BuildOutcome where @@ -184,101 +180,100 @@ impl BuildOutcome { impl PayloadBuilder for PayloadBuilderStack where - L: PayloadBuilder + Unpin + 'static, + L: PayloadBuilder + Unpin + 'static, R: PayloadBuilder + Unpin + 'static, Client: Clone, Pool: Clone, - L::Attributes: Unpin + Clone, // Ensure Attributes can be cloned - R::Attributes: Unpin + Clone, // Ensure Attributes can be cloned - L::BuiltPayload: Unpin + Clone, // Ensure BuiltPayload can be cloned - R::BuiltPayload: Unpin + Clone, // Ensure BuiltPayload can be cloned + L::Attributes: Unpin + Clone, + R::Attributes: Unpin + Clone, + L::BuiltPayload: Unpin + Clone, + R::BuiltPayload: Unpin + Clone, + <>::Attributes as PayloadBuilderAttributes>::Error: 'static, + <>::Attributes as PayloadBuilderAttributes>::Error: 'static, { - type Attributes = EitherAttributes; - type BuiltPayload = EitherAttributes; + type Attributes = Either; + type BuiltPayload = Either; - /// attempts to build a payload using the left builder first, falling back to the right fn try_build( &self, args: BuildArguments, ) -> Result, PayloadBuilderError> { - // attempt to build using the left builder - if let EitherFuture::Left(ref left_attr) = args.config.attributes.0 { - let left_args = BuildArguments { - client: args.client.clone(), - pool: args.pool.clone(), - cached_reads: args.cached_reads.clone(), - config: PayloadConfig { - parent_block: args.config.parent_block.clone(), - extra_data: args.config.extra_data.clone(), - attributes: left_attr.clone(), - }, - cancel: args.cancel.clone(), - best_payload: args.best_payload.clone().and_then(|p| { - if let EitherAttributes(EitherFuture::Left(payload)) = p { - Some(payload.clone()) - } else { - None - } - }), - }; + match args.config.attributes { + Either::Left(ref left_attr) => { + let left_args: BuildArguments = BuildArguments { + client: args.client.clone(), + pool: args.pool.clone(), + cached_reads: args.cached_reads.clone(), + config: PayloadConfig { + parent_block: args.config.parent_block.clone(), + extra_data: args.config.extra_data.clone(), + attributes: left_attr.clone(), + }, + cancel: args.cancel.clone(), + best_payload: args.best_payload.clone().and_then(|payload| { + if let Either::Left(p) = payload { + Some(p) + } else { + None + } + }), + }; - // try building with the left builder - match self.left.try_build(left_args) { - Ok(BuildOutcome::Better { payload, cached_reads }) => { - return Ok(BuildOutcome::Better { - payload: EitherAttributes(EitherFuture::Left(payload)), - cached_reads, - }); - } - Ok(other) => { - return Ok(other.map_payload(|payload| EitherAttributes(EitherFuture::Left(payload)))); - } - Err(_) => { - // if left builder fails, proceed to the right builder + match self.left.try_build(left_args) { + Ok(BuildOutcome::Better { payload, cached_reads }) => { + // Wrap the payload in Either::Left and return + return Ok(BuildOutcome::Better { + payload: Either::Left(payload), + cached_reads, + }) + } + Ok(other) => { + return Ok(other.map_payload(Either::Left)) + } + Err(_) => { + } } } - } + Either::Right(ref right_attr) => { + let right_args = BuildArguments { + client: args.client.clone(), + pool: args.pool.clone(), + cached_reads: args.cached_reads.clone(), + config: PayloadConfig { + parent_block: args.config.parent_block.clone(), + extra_data: args.config.extra_data.clone(), + attributes: right_attr.clone(), + }, + cancel: args.cancel.clone(), + best_payload: args.best_payload.clone().and_then(|payload| { + if let Either::Right(p) = payload { + Some(p) + } else { + None + } + }), + }; - // attempt to build using the right builder - if let EitherFuture::Right(ref right_attr) = args.config.attributes.0 { - let right_args = BuildArguments { - client: args.client.clone(), - pool: args.pool.clone(), - cached_reads: args.cached_reads.clone(), - config: PayloadConfig { - parent_block: args.config.parent_block.clone(), - extra_data: args.config.extra_data.clone(), - attributes: right_attr.clone(), - }, - cancel: args.cancel.clone(), - best_payload: args.best_payload.and_then(|p| { - if let EitherAttributes(EitherFuture::Right(payload)) = p { - Some(payload.clone()) - } else { - None + match self.right.try_build(right_args) { + Ok(BuildOutcome::Better { payload, cached_reads }) => { + return Ok(BuildOutcome::Better { + payload: Either::Right(payload), + cached_reads, + }) + } + Ok(other) => { + return Ok(other.map_payload(Either::Right)) + } + Err(e) => { + return Err(e); } - }), - }; - - // try building with the right builder - match self.right.try_build(right_args) { - Ok(BuildOutcome::Better { payload, cached_reads }) => { - return Ok(BuildOutcome::Better { - payload: EitherAttributes(EitherFuture::Right(payload)), - cached_reads, - }); - } - Ok(other) => { - return Ok(other.map_payload(|payload| EitherAttributes(EitherFuture::Right(payload)))); - } - Err(err) => { - return Err(err); } } } - - // if attributes don't match Left or Right variants, return an error - Err(PayloadBuilderError::MissingPayload) + Err(PayloadBuilderError::Other(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "Both left and right builders failed to build the payload" + )))) } fn build_empty_payload( @@ -286,26 +281,41 @@ where client: &Client, config: PayloadConfig, ) -> Result { - match config.attributes.0 { - EitherFuture::Left(left_attr) => { + match config.attributes { + Either::Left(left_attr) => { let left_config = PayloadConfig { attributes: left_attr, parent_block: config.parent_block.clone(), extra_data: config.extra_data.clone(), - }; - self.left.build_empty_payload(client, left_config) - .map(|payload| EitherAttributes(EitherFuture::Left(payload))) + + match self.left.build_empty_payload(client, left_config) { + Ok(payload_left) => { + return Ok(Either::Left(payload_left)) + }, + Err(_) => {} + } }, - EitherFuture::Right(right_attr) => { + Either::Right(right_attr) => { let right_config = PayloadConfig { - attributes: right_attr, parent_block: config.parent_block.clone(), extra_data: config.extra_data.clone(), + attributes: right_attr.clone(), }; - self.right.build_empty_payload(client, right_config) - .map(|payload| EitherAttributes(EitherFuture::Right(payload))) - }, + + match self.right.build_empty_payload(client, right_config) { + Ok(payload_right) => { + return Ok(Either::Right(payload_right)) + }, + Err(e) => { + return Err(e); + } + } + } } + Err(PayloadBuilderError::Other(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "Failed to build empty payload with both left and right builders" + )))) } } \ No newline at end of file diff --git a/crates/payload/basic/src/lib.rs b/crates/payload/basic/src/lib.rs index 83756354e3ee..97f0ae669f54 100644 --- a/crates/payload/basic/src/lib.rs +++ b/crates/payload/basic/src/lib.rs @@ -46,7 +46,7 @@ use tracing::{debug, trace, warn}; mod metrics; mod builder_stack; -pub use builder_stack::{EitherError, EitherAttributes, PayloadBuilderStack}; +pub use builder_stack::PayloadBuilderStack; /// The [`PayloadJobGenerator`] that creates [`BasicPayloadJob`]s. #[derive(Debug)]