Skip to main content

reth_basic_payload_builder/
stack.rs

1use crate::{
2    BuildArguments, BuildOutcome, HeaderForPayload, PayloadBuilder, PayloadBuilderError,
3    PayloadConfig,
4};
5
6use alloy_primitives::{B256, U256};
7use reth_payload_builder::PayloadId;
8use reth_payload_primitives::{BuiltPayload, PayloadAttributes};
9use reth_primitives_traits::{NodePrimitives, SealedBlock};
10
11use alloy_eips::eip7685::Requests;
12use std::{error::Error, fmt};
13
14/// hand rolled Either enum to handle two builder types
15#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
16#[serde(untagged)]
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> PayloadAttributes for Either<L, R>
51where
52    L: PayloadAttributes,
53    R: PayloadAttributes,
54{
55    fn payload_id(&self, parent_hash: &B256) -> PayloadId {
56        match self {
57            Self::Left(l) => l.payload_id(parent_hash),
58            Self::Right(r) => r.payload_id(parent_hash),
59        }
60    }
61
62    fn timestamp(&self) -> u64 {
63        match self {
64            Self::Left(l) => l.timestamp(),
65            Self::Right(r) => r.timestamp(),
66        }
67    }
68
69    fn parent_beacon_block_root(&self) -> Option<B256> {
70        match self {
71            Self::Left(l) => l.parent_beacon_block_root(),
72            Self::Right(r) => r.parent_beacon_block_root(),
73        }
74    }
75
76    fn withdrawals(&self) -> Option<&Vec<alloy_eips::eip4895::Withdrawal>> {
77        match self {
78            Self::Left(l) => l.withdrawals(),
79            Self::Right(r) => r.withdrawals(),
80        }
81    }
82}
83
84/// this structure enables the chaining of multiple `PayloadBuilder` implementations,
85/// creating a hierarchical fallback system. It's designed to be nestable, allowing
86/// for complex builder arrangements like `Stack<Stack<A, B>, C>` with different
87#[derive(Debug)]
88pub struct PayloadBuilderStack<L, R> {
89    left: L,
90    right: R,
91}
92
93impl<L, R> PayloadBuilderStack<L, R> {
94    /// Creates a new `PayloadBuilderStack` with the given left and right builders.
95    pub const fn new(left: L, right: R) -> Self {
96        Self { left, right }
97    }
98}
99
100impl<L, R> Clone for PayloadBuilderStack<L, R>
101where
102    L: Clone,
103    R: Clone,
104{
105    fn clone(&self) -> Self {
106        Self::new(self.left.clone(), self.right.clone())
107    }
108}
109
110impl<L, R> BuiltPayload for Either<L, R>
111where
112    L: BuiltPayload,
113    R: BuiltPayload<Primitives = L::Primitives>,
114{
115    type Primitives = L::Primitives;
116
117    fn block(&self) -> &SealedBlock<<L::Primitives as NodePrimitives>::Block> {
118        match self {
119            Self::Left(l) => l.block(),
120            Self::Right(r) => r.block(),
121        }
122    }
123
124    fn fees(&self) -> U256 {
125        match self {
126            Self::Left(l) => l.fees(),
127            Self::Right(r) => r.fees(),
128        }
129    }
130
131    fn requests(&self) -> Option<Requests> {
132        match self {
133            Self::Left(l) => l.requests(),
134            Self::Right(r) => r.requests(),
135        }
136    }
137}
138
139impl<L, R> PayloadBuilder for PayloadBuilderStack<L, R>
140where
141    L: PayloadBuilder + Unpin + 'static,
142    R: PayloadBuilder + Unpin + 'static,
143    L::Attributes: Unpin + Clone,
144    R::Attributes: Unpin + Clone,
145    L::BuiltPayload: Unpin + Clone,
146    R::BuiltPayload:
147        BuiltPayload<Primitives = <L::BuiltPayload as BuiltPayload>::Primitives> + Unpin + Clone,
148{
149    type Attributes = Either<L::Attributes, R::Attributes>;
150    type BuiltPayload = Either<L::BuiltPayload, R::BuiltPayload>;
151
152    fn try_build(
153        &self,
154        args: BuildArguments<Self::Attributes, Self::BuiltPayload>,
155    ) -> Result<BuildOutcome<Self::BuiltPayload>, PayloadBuilderError> {
156        let BuildArguments { cached_reads, config, cancel, best_payload } = args;
157        let PayloadConfig { parent_header, attributes, payload_id } = config;
158
159        match attributes {
160            Either::Left(left_attr) => {
161                let left_args: BuildArguments<L::Attributes, L::BuiltPayload> = BuildArguments {
162                    cached_reads,
163                    config: PayloadConfig { parent_header, attributes: left_attr, payload_id },
164                    cancel,
165                    best_payload: best_payload.and_then(|payload| {
166                        if let Either::Left(p) = payload {
167                            Some(p)
168                        } else {
169                            None
170                        }
171                    }),
172                };
173                self.left.try_build(left_args).map(|out| out.map_payload(Either::Left))
174            }
175            Either::Right(right_attr) => {
176                let right_args = BuildArguments {
177                    cached_reads,
178                    config: PayloadConfig { parent_header, attributes: right_attr, payload_id },
179                    cancel,
180                    best_payload: best_payload.and_then(|payload| {
181                        if let Either::Right(p) = payload {
182                            Some(p)
183                        } else {
184                            None
185                        }
186                    }),
187                };
188                self.right.try_build(right_args).map(|out| out.map_payload(Either::Right))
189            }
190        }
191    }
192
193    fn build_empty_payload(
194        &self,
195        config: PayloadConfig<Self::Attributes, HeaderForPayload<Self::BuiltPayload>>,
196    ) -> Result<Self::BuiltPayload, PayloadBuilderError> {
197        match config {
198            PayloadConfig { parent_header, attributes: Either::Left(left_attr), payload_id } => {
199                let left_config =
200                    PayloadConfig { parent_header, attributes: left_attr, payload_id };
201                self.left.build_empty_payload(left_config).map(Either::Left)
202            }
203            PayloadConfig { parent_header, attributes: Either::Right(right_attr), payload_id } => {
204                let right_config =
205                    PayloadConfig { parent_header, attributes: right_attr, payload_id };
206                self.right.build_empty_payload(right_config).map(Either::Right)
207            }
208        }
209    }
210}