reth_basic_payload_builder/
stack.rs

1use crate::{
2    BuildArguments, BuildOutcome, PayloadBuilder, PayloadBuilderAttributes, PayloadBuilderError,
3    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::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,
155{
156    fn block(&self) -> &SealedBlock {
157        match self {
158            Self::Left(l) => l.block(),
159            Self::Right(r) => r.block(),
160        }
161    }
162
163    fn fees(&self) -> U256 {
164        match self {
165            Self::Left(l) => l.fees(),
166            Self::Right(r) => r.fees(),
167        }
168    }
169
170    fn requests(&self) -> Option<Requests> {
171        match self {
172            Self::Left(l) => l.requests(),
173            Self::Right(r) => r.requests(),
174        }
175    }
176}
177
178impl<L, R, Pool, Client> PayloadBuilder<Pool, Client> for PayloadBuilderStack<L, R>
179where
180    L: PayloadBuilder<Pool, Client> + Unpin + 'static,
181    R: PayloadBuilder<Pool, Client> + Unpin + 'static,
182    Client: Clone,
183    Pool: Clone,
184    L::Attributes: Unpin + Clone,
185    R::Attributes: Unpin + Clone,
186    L::BuiltPayload: Unpin + Clone,
187    R::BuiltPayload: Unpin + Clone,
188    <<L as PayloadBuilder<Pool, Client>>::Attributes as PayloadBuilderAttributes>::Error: 'static,
189    <<R as PayloadBuilder<Pool, Client>>::Attributes as PayloadBuilderAttributes>::Error: 'static,
190{
191    type Attributes = Either<L::Attributes, R::Attributes>;
192    type BuiltPayload = Either<L::BuiltPayload, R::BuiltPayload>;
193
194    fn try_build(
195        &self,
196        args: BuildArguments<Pool, Client, Self::Attributes, Self::BuiltPayload>,
197    ) -> Result<BuildOutcome<Self::BuiltPayload>, PayloadBuilderError> {
198        match args.config.attributes {
199            Either::Left(ref left_attr) => {
200                let left_args: BuildArguments<Pool, Client, L::Attributes, L::BuiltPayload> =
201                    BuildArguments {
202                        client: args.client.clone(),
203                        pool: args.pool.clone(),
204                        cached_reads: args.cached_reads.clone(),
205                        config: PayloadConfig {
206                            parent_header: args.config.parent_header.clone(),
207                            attributes: left_attr.clone(),
208                        },
209                        cancel: args.cancel.clone(),
210                        best_payload: args.best_payload.clone().and_then(|payload| {
211                            if let Either::Left(p) = payload {
212                                Some(p)
213                            } else {
214                                None
215                            }
216                        }),
217                    };
218
219                self.left.try_build(left_args).map(|out| out.map_payload(Either::Left))
220            }
221            Either::Right(ref right_attr) => {
222                let right_args = BuildArguments {
223                    client: args.client.clone(),
224                    pool: args.pool.clone(),
225                    cached_reads: args.cached_reads.clone(),
226                    config: PayloadConfig {
227                        parent_header: args.config.parent_header.clone(),
228                        attributes: right_attr.clone(),
229                    },
230                    cancel: args.cancel.clone(),
231                    best_payload: args.best_payload.clone().and_then(|payload| {
232                        if let Either::Right(p) = payload {
233                            Some(p)
234                        } else {
235                            None
236                        }
237                    }),
238                };
239
240                self.right.try_build(right_args).map(|out| out.map_payload(Either::Right))
241            }
242        }
243    }
244
245    fn build_empty_payload(
246        &self,
247        client: &Client,
248        config: PayloadConfig<Self::Attributes>,
249    ) -> Result<Self::BuiltPayload, PayloadBuilderError> {
250        match config.attributes {
251            Either::Left(left_attr) => {
252                let left_config = PayloadConfig {
253                    parent_header: config.parent_header.clone(),
254                    attributes: left_attr,
255                };
256                self.left.build_empty_payload(client, left_config).map(Either::Left)
257            }
258            Either::Right(right_attr) => {
259                let right_config = PayloadConfig {
260                    parent_header: config.parent_header.clone(),
261                    attributes: right_attr,
262                };
263                self.right.build_empty_payload(client, right_config).map(Either::Right)
264            }
265        }
266    }
267}