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    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 requests(&self) -> Option<Requests> {
139        match self {
140            Self::Left(l) => l.requests(),
141            Self::Right(r) => r.requests(),
142        }
143    }
144}
145
146impl<L, R> PayloadBuilder for PayloadBuilderStack<L, R>
147where
148    L: PayloadBuilder + Unpin + 'static,
149    R: PayloadBuilder + Unpin + 'static,
150    L::Attributes: Unpin + Clone,
151    R::Attributes: Unpin + Clone,
152    L::BuiltPayload: Unpin + Clone,
153    R::BuiltPayload:
154        BuiltPayload<Primitives = <L::BuiltPayload as BuiltPayload>::Primitives> + Unpin + Clone,
155{
156    type Attributes = Either<L::Attributes, R::Attributes>;
157    type BuiltPayload = Either<L::BuiltPayload, R::BuiltPayload>;
158
159    fn try_build(
160        &self,
161        args: BuildArguments<Self::Attributes, Self::BuiltPayload>,
162    ) -> Result<BuildOutcome<Self::BuiltPayload>, PayloadBuilderError> {
163        let BuildArguments {
164            cached_reads,
165            execution_cache,
166            trie_handle,
167            config,
168            cancel,
169            best_payload,
170        } = args;
171        let PayloadConfig { parent_header, attributes, payload_id } = config;
172
173        match attributes {
174            Either::Left(left_attr) => {
175                let left_args: BuildArguments<L::Attributes, L::BuiltPayload> = BuildArguments {
176                    cached_reads,
177                    execution_cache,
178                    trie_handle,
179                    config: PayloadConfig { parent_header, attributes: left_attr, payload_id },
180                    cancel,
181                    best_payload: best_payload.and_then(|payload| {
182                        if let Either::Left(p) = payload {
183                            Some(p)
184                        } else {
185                            None
186                        }
187                    }),
188                };
189                self.left.try_build(left_args).map(|out| out.map_payload(Either::Left))
190            }
191            Either::Right(right_attr) => {
192                let right_args = BuildArguments {
193                    cached_reads,
194                    execution_cache,
195                    trie_handle,
196                    config: PayloadConfig { parent_header, attributes: right_attr, payload_id },
197                    cancel,
198                    best_payload: best_payload.and_then(|payload| {
199                        if let Either::Right(p) = payload {
200                            Some(p)
201                        } else {
202                            None
203                        }
204                    }),
205                };
206                self.right.try_build(right_args).map(|out| out.map_payload(Either::Right))
207            }
208        }
209    }
210
211    fn build_empty_payload(
212        &self,
213        config: PayloadConfig<Self::Attributes, HeaderForPayload<Self::BuiltPayload>>,
214    ) -> Result<Self::BuiltPayload, PayloadBuilderError> {
215        match config {
216            PayloadConfig { parent_header, attributes: Either::Left(left_attr), payload_id } => {
217                let left_config =
218                    PayloadConfig { parent_header, attributes: left_attr, payload_id };
219                self.left.build_empty_payload(left_config).map(Either::Left)
220            }
221            PayloadConfig { parent_header, attributes: Either::Right(right_attr), payload_id } => {
222                let right_config =
223                    PayloadConfig { parent_header, attributes: right_attr, payload_id };
224                self.right.build_empty_payload(right_config).map(Either::Right)
225            }
226        }
227    }
228}