Skip to main content

reth_evm/
engine.rs

1use crate::{execute::ExecutableTxFor, ConfigureEvm, EvmEnvFor, ExecutionCtxFor, TxEnvFor};
2use alloy_consensus::transaction::Either;
3use alloy_evm::{block::ExecutableTxParts, RecoveredTx};
4use rayon::prelude::*;
5use reth_primitives_traits::TxTy;
6
7/// [`ConfigureEvm`] extension providing methods for executing payloads.
8pub trait ConfigureEngineEvm<ExecutionData>: ConfigureEvm {
9    /// Returns an [`crate::EvmEnv`] for the given payload.
10    fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result<EvmEnvFor<Self>, Self::Error>;
11
12    /// Returns an [`ExecutionCtxFor`] for the given payload.
13    fn context_for_payload<'a>(
14        &self,
15        payload: &'a ExecutionData,
16    ) -> Result<ExecutionCtxFor<'a, Self>, Self::Error>;
17
18    /// Returns an [`ExecutableTxIterator`] for the given payload.
19    fn tx_iterator_for_payload(
20        &self,
21        payload: &ExecutionData,
22    ) -> Result<impl ExecutableTxIterator<Self>, Self::Error>;
23}
24
25/// Converts a raw transaction into an executable transaction.
26///
27/// This trait abstracts the conversion logic (e.g., decoding, signature recovery) that is
28/// parallelized in the engine.
29pub trait ConvertTx<RawTx>: Send + Sync + 'static {
30    /// The executable transaction type.
31    type Tx;
32    /// Errors that may occur during conversion.
33    type Error;
34    /// Converts a raw transaction.
35    fn convert(&self, raw: RawTx) -> Result<Self::Tx, Self::Error>;
36}
37
38// Blanket impl so closures still work.
39impl<F, RawTx, Tx, Err> ConvertTx<RawTx> for F
40where
41    F: Fn(RawTx) -> Result<Tx, Err> + Send + Sync + 'static,
42{
43    type Tx = Tx;
44    type Error = Err;
45    fn convert(&self, raw: RawTx) -> Result<Tx, Err> {
46        self(raw)
47    }
48}
49
50impl<A, B, RA, RB> ConvertTx<Either<RA, RB>> for Either<A, B>
51where
52    A: ConvertTx<RA>,
53    B: ConvertTx<RB>,
54{
55    type Tx = Either<A::Tx, B::Tx>;
56    type Error = Either<A::Error, B::Error>;
57    fn convert(&self, raw: Either<RA, RB>) -> Result<Self::Tx, Self::Error> {
58        match (self, raw) {
59            (Self::Left(a), Either::Left(raw)) => {
60                a.convert(raw).map(Either::Left).map_err(Either::Left)
61            }
62            (Self::Right(b), Either::Right(raw)) => {
63                b.convert(raw).map(Either::Right).map_err(Either::Right)
64            }
65            _ => unreachable!(),
66        }
67    }
68}
69
70/// A helper trait representing a pair of a "raw" transactions iterator and a closure that can be
71/// used to convert them to an executable transaction. This tuple is used in the engine to
72/// parallelize heavy work like decoding or recovery.
73pub trait ExecutableTxTuple: Send + 'static {
74    /// Raw transaction that can be converted to an [`ExecutableTxTuple::Tx`]
75    ///
76    /// This can be any type that can be converted to an [`ExecutableTxTuple::Tx`]. For example,
77    /// an unrecovered transaction or just the transaction bytes.
78    type RawTx: Send + Sync + 'static;
79    /// The executable transaction type iterator yields.
80    type Tx: Clone + Send + Sync + 'static;
81    /// Errors that may occur while recovering or decoding transactions.
82    type Error: core::error::Error + Send + Sync + 'static;
83
84    /// Iterator over [`ExecutableTxTuple::RawTx`].
85    type IntoIter: IntoParallelIterator<Item = Self::RawTx, Iter: IndexedParallelIterator>
86        + IntoIterator<Item = Self::RawTx, IntoIter: Send>
87        + Send
88        + 'static;
89    /// Converter that can be used to convert a [`ExecutableTxTuple::RawTx`] to a
90    /// [`ExecutableTxTuple::Tx`]. This might involve heavy work like decoding or recovery
91    /// and will be parallelized in the engine.
92    type Convert: ConvertTx<Self::RawTx, Tx = Self::Tx, Error = Self::Error>;
93
94    /// Decomposes into the raw transaction iterator and converter.
95    fn into_parts(self) -> (Self::IntoIter, Self::Convert);
96}
97
98impl<RawTx, Tx, Err, I, F> ExecutableTxTuple for (I, F)
99where
100    RawTx: Send + Sync + 'static,
101    Tx: Clone + Send + Sync + 'static,
102    Err: core::error::Error + Send + Sync + 'static,
103    I: IntoParallelIterator<Item = RawTx, Iter: IndexedParallelIterator>
104        + IntoIterator<Item = RawTx, IntoIter: Send>
105        + Send
106        + 'static,
107    F: Fn(RawTx) -> Result<Tx, Err> + Send + Sync + 'static,
108{
109    type RawTx = RawTx;
110    type Tx = Tx;
111    type Error = Err;
112
113    type IntoIter = I;
114    type Convert = F;
115
116    fn into_parts(self) -> (I, F) {
117        self
118    }
119}
120
121/// Iterator over executable transactions.
122///
123/// See also [`ExecutableTxTuple`] for the raw transaction iterator and converter this extension
124/// trait builds on.
125pub trait ExecutableTxIterator<Evm: ConfigureEvm>:
126    ExecutableTxTuple<Tx: ExecutableTxFor<Evm, Recovered = Self::Recovered>>
127{
128    /// HACK: for some reason, this duplicated AT is the only way to enforce the inner Recovered:
129    /// Send + Sync bound. Effectively alias for `Self::Tx::Recovered`.
130    type Recovered: RecoveredTx<TxTy<Evm::Primitives>> + Send + Sync;
131}
132
133impl<T, Evm: ConfigureEvm> ExecutableTxIterator<Evm> for T
134where
135    T: ExecutableTxTuple<Tx: ExecutableTxFor<Evm, Recovered: Send + Sync>>,
136{
137    type Recovered = <T::Tx as ExecutableTxParts<TxEnvFor<Evm>, TxTy<Evm::Primitives>>>::Recovered;
138}
139
140/// Wraps `Either<L, R>` to implement both [`IntoParallelIterator`] and [`IntoIterator`],
141/// mapping items through [`Either::Left`] / [`Either::Right`] on demand without collecting.
142#[derive(Debug)]
143pub struct EitherIter<L, R>(Either<L, R>);
144
145impl<L, R> IntoParallelIterator for EitherIter<L, R>
146where
147    L: IntoParallelIterator,
148    R: IntoParallelIterator,
149    L::Iter: IndexedParallelIterator,
150    R::Iter: IndexedParallelIterator,
151{
152    type Item = Either<L::Item, R::Item>;
153    type Iter = Either<
154        rayon::iter::Map<L::Iter, fn(L::Item) -> Either<L::Item, R::Item>>,
155        rayon::iter::Map<R::Iter, fn(R::Item) -> Either<L::Item, R::Item>>,
156    >;
157
158    fn into_par_iter(self) -> Self::Iter {
159        match self.0 {
160            Either::Left(l) => Either::Left(l.into_par_iter().map(Either::Left)),
161            Either::Right(r) => Either::Right(r.into_par_iter().map(Either::Right)),
162        }
163    }
164}
165
166impl<L, R> IntoIterator for EitherIter<L, R>
167where
168    L: IntoIterator,
169    R: IntoIterator,
170{
171    type Item = Either<L::Item, R::Item>;
172    type IntoIter = Either<
173        core::iter::Map<L::IntoIter, fn(L::Item) -> Either<L::Item, R::Item>>,
174        core::iter::Map<R::IntoIter, fn(R::Item) -> Either<L::Item, R::Item>>,
175    >;
176
177    fn into_iter(self) -> Self::IntoIter {
178        match self.0 {
179            Either::Left(l) => Either::Left(l.into_iter().map(Either::Left)),
180            Either::Right(r) => Either::Right(r.into_iter().map(Either::Right)),
181        }
182    }
183}
184
185// SAFETY: `EitherIter` is just a newtype over `Either<L, R>`.
186unsafe impl<L: Send, R: Send> Send for EitherIter<L, R> {}
187
188impl<A: ExecutableTxTuple, B: ExecutableTxTuple> ExecutableTxTuple for Either<A, B> {
189    type RawTx = Either<A::RawTx, B::RawTx>;
190    type Tx = Either<A::Tx, B::Tx>;
191    type Error = Either<A::Error, B::Error>;
192    type IntoIter = EitherIter<A::IntoIter, B::IntoIter>;
193    type Convert = Either<A::Convert, B::Convert>;
194
195    fn into_parts(self) -> (Self::IntoIter, Self::Convert) {
196        match self {
197            Self::Left(a) => {
198                let (iter, convert) = a.into_parts();
199                (EitherIter(Either::Left(iter)), Either::Left(convert))
200            }
201            Self::Right(b) => {
202                let (iter, convert) = b.into_parts();
203                (EitherIter(Either::Right(iter)), Either::Right(convert))
204            }
205        }
206    }
207}