Skip to main content

reth_evm/
engine.rs

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