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::{Bytes, 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    fn slot_number(&self) -> Option<u64> {
84        match self {
85            Self::Left(l) => l.slot_number(),
86            Self::Right(r) => r.slot_number(),
87        }
88    }
89}
90
91/// this structure enables the chaining of multiple `PayloadBuilder` implementations,
92/// creating a hierarchical fallback system. It's designed to be nestable, allowing
93/// for complex builder arrangements like `Stack<Stack<A, B>, C>` with different
94#[derive(Debug)]
95pub struct PayloadBuilderStack<L, R> {
96    left: L,
97    right: R,
98}
99
100impl<L, R> PayloadBuilderStack<L, R> {
101    /// Creates a new `PayloadBuilderStack` with the given left and right builders.
102    pub const fn new(left: L, right: R) -> Self {
103        Self { left, right }
104    }
105}
106
107impl<L, R> Clone for PayloadBuilderStack<L, R>
108where
109    L: Clone,
110    R: Clone,
111{
112    fn clone(&self) -> Self {
113        Self::new(self.left.clone(), self.right.clone())
114    }
115}
116
117impl<L, R> BuiltPayload for Either<L, R>
118where
119    L: BuiltPayload,
120    R: BuiltPayload<Primitives = L::Primitives>,
121{
122    type Primitives = L::Primitives;
123
124    fn block(&self) -> &SealedBlock<<L::Primitives as NodePrimitives>::Block> {
125        match self {
126            Self::Left(l) => l.block(),
127            Self::Right(r) => r.block(),
128        }
129    }
130
131    fn fees(&self) -> U256 {
132        match self {
133            Self::Left(l) => l.fees(),
134            Self::Right(r) => r.fees(),
135        }
136    }
137
138    fn block_access_list(&self) -> Option<&Bytes> {
139        match self {
140            Self::Left(l) => l.block_access_list(),
141            Self::Right(r) => r.block_access_list(),
142        }
143    }
144
145    fn requests(&self) -> Option<Requests> {
146        match self {
147            Self::Left(l) => l.requests(),
148            Self::Right(r) => r.requests(),
149        }
150    }
151}
152
153impl<L, R> PayloadBuilder for PayloadBuilderStack<L, R>
154where
155    L: PayloadBuilder + Unpin + 'static,
156    R: PayloadBuilder + Unpin + 'static,
157    L::Attributes: Unpin + Clone,
158    R::Attributes: Unpin + Clone,
159    L::BuiltPayload: Unpin + Clone,
160    R::BuiltPayload:
161        BuiltPayload<Primitives = <L::BuiltPayload as BuiltPayload>::Primitives> + Unpin + Clone,
162{
163    type Attributes = Either<L::Attributes, R::Attributes>;
164    type BuiltPayload = Either<L::BuiltPayload, R::BuiltPayload>;
165
166    fn try_build(
167        &self,
168        args: BuildArguments<Self::Attributes, Self::BuiltPayload>,
169    ) -> Result<BuildOutcome<Self::BuiltPayload>, PayloadBuilderError> {
170        let BuildArguments {
171            cached_reads,
172            execution_cache,
173            trie_handle,
174            config,
175            cancel,
176            best_payload,
177        } = args;
178        let PayloadConfig { parent_header, attributes, payload_id } = config;
179
180        match attributes {
181            Either::Left(left_attr) => {
182                let left_args: BuildArguments<L::Attributes, L::BuiltPayload> = BuildArguments {
183                    cached_reads,
184                    execution_cache,
185                    trie_handle,
186                    config: PayloadConfig { parent_header, attributes: left_attr, payload_id },
187                    cancel,
188                    best_payload: best_payload.and_then(|payload| {
189                        if let Either::Left(p) = payload {
190                            Some(p)
191                        } else {
192                            None
193                        }
194                    }),
195                };
196                self.left.try_build(left_args).map(|out| out.map_payload(Either::Left))
197            }
198            Either::Right(right_attr) => {
199                let right_args = BuildArguments {
200                    cached_reads,
201                    execution_cache,
202                    trie_handle,
203                    config: PayloadConfig { parent_header, attributes: right_attr, payload_id },
204                    cancel,
205                    best_payload: best_payload.and_then(|payload| {
206                        if let Either::Right(p) = payload {
207                            Some(p)
208                        } else {
209                            None
210                        }
211                    }),
212                };
213                self.right.try_build(right_args).map(|out| out.map_payload(Either::Right))
214            }
215        }
216    }
217
218    fn build_empty_payload(
219        &self,
220        config: PayloadConfig<Self::Attributes, HeaderForPayload<Self::BuiltPayload>>,
221    ) -> Result<Self::BuiltPayload, PayloadBuilderError> {
222        match config {
223            PayloadConfig { parent_header, attributes: Either::Left(left_attr), payload_id } => {
224                let left_config =
225                    PayloadConfig { parent_header, attributes: left_attr, payload_id };
226                self.left.build_empty_payload(left_config).map(Either::Left)
227            }
228            PayloadConfig { parent_header, attributes: Either::Right(right_attr), payload_id } => {
229                let right_config =
230                    PayloadConfig { parent_header, attributes: right_attr, payload_id };
231                self.right.build_empty_payload(right_config).map(Either::Right)
232            }
233        }
234    }
235}