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_traits::{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        let BuildArguments { cached_reads, config, cancel, best_payload } = args;
198        let PayloadConfig { parent_header, attributes } = config;
199
200        match attributes {
201            Either::Left(left_attr) => {
202                let left_args: BuildArguments<L::Attributes, L::BuiltPayload> = BuildArguments {
203                    cached_reads,
204                    config: PayloadConfig { parent_header, attributes: left_attr },
205                    cancel,
206                    best_payload: best_payload.and_then(|payload| {
207                        if let Either::Left(p) = payload {
208                            Some(p)
209                        } else {
210                            None
211                        }
212                    }),
213                };
214                self.left.try_build(left_args).map(|out| out.map_payload(Either::Left))
215            }
216            Either::Right(right_attr) => {
217                let right_args = BuildArguments {
218                    cached_reads,
219                    config: PayloadConfig { parent_header, attributes: right_attr },
220                    cancel,
221                    best_payload: best_payload.and_then(|payload| {
222                        if let Either::Right(p) = payload {
223                            Some(p)
224                        } else {
225                            None
226                        }
227                    }),
228                };
229                self.right.try_build(right_args).map(|out| out.map_payload(Either::Right))
230            }
231        }
232    }
233
234    fn build_empty_payload(
235        &self,
236        config: PayloadConfig<Self::Attributes, HeaderForPayload<Self::BuiltPayload>>,
237    ) -> Result<Self::BuiltPayload, PayloadBuilderError> {
238        match config {
239            PayloadConfig { parent_header, attributes: Either::Left(left_attr) } => {
240                let left_config = PayloadConfig { parent_header, attributes: left_attr };
241                self.left.build_empty_payload(left_config).map(Either::Left)
242            }
243            PayloadConfig { parent_header, attributes: Either::Right(right_attr) } => {
244                let right_config = PayloadConfig { parent_header, attributes: right_attr };
245                self.right.build_empty_payload(right_config).map(Either::Right)
246            }
247        }
248    }
249}