reth_basic_payload_builder/
stack.rs

1use crate::{
2    BuildArguments, BuildOutcome, HeaderForPayload, PayloadBuilder, PayloadBuilderAttributes,
3    PayloadBuilderError, PayloadConfig,
4};
5
6use alloy_eips::eip4895::Withdrawals;
7use alloy_primitives::{Address, B256, U256};
8use reth_payload_builder::PayloadId;
9use reth_payload_primitives::BuiltPayload;
10use reth_primitives::{NodePrimitives, SealedBlock};
11
12use alloy_eips::eip7685::Requests;
13use std::{error::Error, fmt};
14
15/// hand rolled Either enum to handle two builder types
16#[derive(Debug, Clone)]
17pub enum Either<L, R> {
18    /// left variant
19    Left(L),
20    /// right variant
21    Right(R),
22}
23
24impl<L, R> fmt::Display for Either<L, R>
25where
26    L: fmt::Display,
27    R: fmt::Display,
28{
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::Left(l) => write!(f, "Left: {}", l),
32            Self::Right(r) => write!(f, "Right: {}", r),
33        }
34    }
35}
36
37impl<L, R> Error for Either<L, R>
38where
39    L: Error + 'static,
40    R: Error + 'static,
41{
42    fn source(&self) -> Option<&(dyn Error + 'static)> {
43        match self {
44            Self::Left(l) => Some(l),
45            Self::Right(r) => Some(r),
46        }
47    }
48}
49
50impl<L, R> PayloadBuilderAttributes for Either<L, R>
51where
52    L: PayloadBuilderAttributes,
53    R: PayloadBuilderAttributes,
54    L::Error: Error + 'static,
55    R::Error: Error + 'static,
56{
57    type RpcPayloadAttributes = Either<L::RpcPayloadAttributes, R::RpcPayloadAttributes>;
58    type Error = Either<L::Error, R::Error>;
59
60    fn try_new(
61        parent: B256,
62        rpc_payload_attributes: Self::RpcPayloadAttributes,
63        version: u8,
64    ) -> Result<Self, Self::Error> {
65        match rpc_payload_attributes {
66            Either::Left(attr) => {
67                L::try_new(parent, attr, version).map(Either::Left).map_err(Either::Left)
68            }
69            Either::Right(attr) => {
70                R::try_new(parent, attr, version).map(Either::Right).map_err(Either::Right)
71            }
72        }
73    }
74
75    fn payload_id(&self) -> PayloadId {
76        match self {
77            Self::Left(l) => l.payload_id(),
78            Self::Right(r) => r.payload_id(),
79        }
80    }
81
82    fn parent(&self) -> B256 {
83        match self {
84            Self::Left(l) => l.parent(),
85            Self::Right(r) => r.parent(),
86        }
87    }
88
89    fn timestamp(&self) -> u64 {
90        match self {
91            Self::Left(l) => l.timestamp(),
92            Self::Right(r) => r.timestamp(),
93        }
94    }
95
96    fn parent_beacon_block_root(&self) -> Option<B256> {
97        match self {
98            Self::Left(l) => l.parent_beacon_block_root(),
99            Self::Right(r) => r.parent_beacon_block_root(),
100        }
101    }
102
103    fn suggested_fee_recipient(&self) -> Address {
104        match self {
105            Self::Left(l) => l.suggested_fee_recipient(),
106            Self::Right(r) => r.suggested_fee_recipient(),
107        }
108    }
109
110    fn prev_randao(&self) -> B256 {
111        match self {
112            Self::Left(l) => l.prev_randao(),
113            Self::Right(r) => r.prev_randao(),
114        }
115    }
116
117    fn withdrawals(&self) -> &Withdrawals {
118        match self {
119            Self::Left(l) => l.withdrawals(),
120            Self::Right(r) => r.withdrawals(),
121        }
122    }
123}
124
125/// this structure enables the chaining of multiple `PayloadBuilder` implementations,
126/// creating a hierarchical fallback system. It's designed to be nestable, allowing
127/// for complex builder arrangements like `Stack<Stack<A, B>, C>` with different
128#[derive(Debug)]
129pub struct PayloadBuilderStack<L, R> {
130    left: L,
131    right: R,
132}
133
134impl<L, R> PayloadBuilderStack<L, R> {
135    /// Creates a new `PayloadBuilderStack` with the given left and right builders.
136    pub const fn new(left: L, right: R) -> Self {
137        Self { left, right }
138    }
139}
140
141impl<L, R> Clone for PayloadBuilderStack<L, R>
142where
143    L: Clone,
144    R: Clone,
145{
146    fn clone(&self) -> Self {
147        Self::new(self.left.clone(), self.right.clone())
148    }
149}
150
151impl<L, R> BuiltPayload for Either<L, R>
152where
153    L: BuiltPayload,
154    R: BuiltPayload<Primitives = L::Primitives>,
155{
156    type Primitives = L::Primitives;
157
158    fn block(&self) -> &SealedBlock<<L::Primitives as NodePrimitives>::Block> {
159        match self {
160            Self::Left(l) => l.block(),
161            Self::Right(r) => r.block(),
162        }
163    }
164
165    fn fees(&self) -> U256 {
166        match self {
167            Self::Left(l) => l.fees(),
168            Self::Right(r) => r.fees(),
169        }
170    }
171
172    fn requests(&self) -> Option<Requests> {
173        match self {
174            Self::Left(l) => l.requests(),
175            Self::Right(r) => r.requests(),
176        }
177    }
178}
179
180impl<L, R> PayloadBuilder for PayloadBuilderStack<L, R>
181where
182    L: PayloadBuilder + Unpin + 'static,
183    R: PayloadBuilder + Unpin + 'static,
184    L::Attributes: Unpin + Clone,
185    R::Attributes: Unpin + Clone,
186    L::BuiltPayload: Unpin + Clone,
187    R::BuiltPayload:
188        BuiltPayload<Primitives = <L::BuiltPayload as BuiltPayload>::Primitives> + Unpin + Clone,
189{
190    type Attributes = Either<L::Attributes, R::Attributes>;
191    type BuiltPayload = Either<L::BuiltPayload, R::BuiltPayload>;
192
193    fn try_build(
194        &self,
195        args: BuildArguments<Self::Attributes, Self::BuiltPayload>,
196    ) -> Result<BuildOutcome<Self::BuiltPayload>, PayloadBuilderError> {
197        match args.config.attributes {
198            Either::Left(ref left_attr) => {
199                let left_args: BuildArguments<L::Attributes, L::BuiltPayload> = BuildArguments {
200                    cached_reads: args.cached_reads.clone(),
201                    config: PayloadConfig {
202                        parent_header: args.config.parent_header.clone(),
203                        attributes: left_attr.clone(),
204                    },
205                    cancel: args.cancel.clone(),
206                    best_payload: args.best_payload.clone().and_then(|payload| {
207                        if let Either::Left(p) = payload {
208                            Some(p)
209                        } else {
210                            None
211                        }
212                    }),
213                };
214
215                self.left.try_build(left_args).map(|out| out.map_payload(Either::Left))
216            }
217            Either::Right(ref right_attr) => {
218                let right_args = BuildArguments {
219                    cached_reads: args.cached_reads.clone(),
220                    config: PayloadConfig {
221                        parent_header: args.config.parent_header.clone(),
222                        attributes: right_attr.clone(),
223                    },
224                    cancel: args.cancel.clone(),
225                    best_payload: args.best_payload.clone().and_then(|payload| {
226                        if let Either::Right(p) = payload {
227                            Some(p)
228                        } else {
229                            None
230                        }
231                    }),
232                };
233
234                self.right.try_build(right_args).map(|out| out.map_payload(Either::Right))
235            }
236        }
237    }
238
239    fn build_empty_payload(
240        &self,
241        config: PayloadConfig<Self::Attributes, HeaderForPayload<Self::BuiltPayload>>,
242    ) -> Result<Self::BuiltPayload, PayloadBuilderError> {
243        match config.attributes {
244            Either::Left(left_attr) => {
245                let left_config = PayloadConfig {
246                    parent_header: config.parent_header.clone(),
247                    attributes: left_attr,
248                };
249                self.left.build_empty_payload(left_config).map(Either::Left)
250            }
251            Either::Right(right_attr) => {
252                let right_config = PayloadConfig {
253                    parent_header: config.parent_header.clone(),
254                    attributes: right_attr,
255                };
256                self.right.build_empty_payload(right_config).map(Either::Right)
257            }
258        }
259    }
260}