Skip to content

Commit 162a0cf

Browse files
authored
Feat: evm module with custom EvmFactory trait (#554)
## 💡 Motivation and Context This PR brings small code cleanup for `EvmFactory`. I have created a new `EvmFactory` trait in rbuilder because the `Evm` trait from `alloy_revm` has a lot of associated types and trait bounds. These are now defined in the custom `EvmFactory` trait and resolved in the implementation for `EthCachedEvmFactory`, so this removes the need for extra trait bounds outside of the `evm` module (such as in the `execute_evm` function in `order_commit`). ## 📝 Summary - The `EthCachedEvmFactory` was moved to new module `evm` under `building` (`rbuilder::building::evm`), IMO this makes the overall code structure better as the separation of concerns is cleaner (more obvious) - `EthCachedEvmFactory::Context` is defined to `EthEvmContext<DB>`, which is an alias of `Context<BlockEnv, TxEnv, CfgEnv, DB>` - `EvmFactory` custom trait, which is a more concrete version than `alloy_revm::EvmFactory` - Remove the dependency on `EthEvmFactory` in `EthCachedEvmFactory` struct, but still use it's default implementation under the hood (we can also consider completely remove it and use `Context` instead) --- ## ✅ I have completed the following steps: * [x] Run `make lint` * [x] Run `make test` * [ ] Added tests (if applicable)
1 parent 8835bb1 commit 162a0cf

File tree

6 files changed

+129
-86
lines changed

6 files changed

+129
-86
lines changed

crates/rbuilder/src/building/evm.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use crate::building::precompile_cache::{PrecompileCache, WrappedPrecompile};
2+
use parking_lot::Mutex;
3+
use reth_evm::{
4+
eth::EthEvmContext, EthEvm, EthEvmFactory, Evm, EvmEnv, EvmFactory as RethEvmFactory,
5+
};
6+
use revm::{
7+
context::{
8+
result::{EVMError, HaltReason},
9+
TxEnv,
10+
},
11+
handler::EthPrecompiles,
12+
inspector::NoOpInspector,
13+
interpreter::interpreter::EthInterpreter,
14+
primitives::hardfork::SpecId,
15+
Database, Inspector,
16+
};
17+
use std::sync::Arc;
18+
19+
/// Custom trait to abstract over EVM construction with a cleaner and more concrete
20+
/// interface than the `Evm` trait from `alloy-revm`.
21+
///
22+
/// # Motivation
23+
///
24+
/// The `alloy_revm::Evm` trait comes with a large number of associated types and trait
25+
/// bounds. This new `EvmFactory` trait is designed to encapsulate those complexities,
26+
/// providing an EVM interface less dependent on `alloy-revm` crate.
27+
///
28+
/// It is particularly useful in reducing trait bound noise in other parts of the codebase
29+
/// (i.e. `execute_evm` in `order_commit`), and improves modularity.
30+
///
31+
/// See [`EthCachedEvmFactory`] for an implementation that integrates precompile
32+
/// caching and uses `reth_evm::EthEvm` internally.
33+
pub trait EvmFactory {
34+
type Evm<DB, I>: Evm<
35+
DB = DB,
36+
Tx = TxEnv,
37+
HaltReason = HaltReason,
38+
Error = EVMError<DB::Error>,
39+
Spec = SpecId,
40+
>
41+
where
42+
DB: Database<Error: Send + Sync + 'static>,
43+
I: Inspector<EthEvmContext<DB>>;
44+
45+
/// Create an EVM instance with default (no-op) inspector.
46+
fn create_evm<DB>(&self, db: DB, env: EvmEnv) -> Self::Evm<DB, NoOpInspector>
47+
where
48+
DB: Database<Error: Send + Sync + 'static>;
49+
50+
/// Create an EVM instance with a provided inspector.
51+
fn create_evm_with_inspector<DB, I>(
52+
&self,
53+
db: DB,
54+
env: EvmEnv,
55+
inspector: I,
56+
) -> Self::Evm<DB, I>
57+
where
58+
DB: Database<Error: Send + Sync + 'static>,
59+
I: Inspector<EthEvmContext<DB>, EthInterpreter>;
60+
}
61+
62+
#[derive(Debug, Clone, Default)]
63+
pub struct EthCachedEvmFactory {
64+
evm_factory: EthEvmFactory,
65+
cache: Arc<Mutex<PrecompileCache>>,
66+
}
67+
68+
/// Implementation of the `EvmFactory` trait for `EthCachedEvmFactory`.
69+
///
70+
/// This implementation uses `reth_evm::EthEvm` internally and provides a concrete
71+
/// type for the `Evm` trait.
72+
///
73+
/// It also integrates precompile caching using the [`PrecompileCache`] and
74+
/// [`WrappedPrecompile`] types.
75+
impl EvmFactory for EthCachedEvmFactory {
76+
type Evm<DB, I>
77+
= EthEvm<DB, I, WrappedPrecompile<EthPrecompiles>>
78+
where
79+
DB: Database<Error: Send + Sync + 'static>,
80+
I: Inspector<EthEvmContext<DB>>;
81+
82+
fn create_evm<DB>(&self, db: DB, env: EvmEnv) -> Self::Evm<DB, NoOpInspector>
83+
where
84+
DB: Database<Error: Send + Sync + 'static>,
85+
{
86+
let evm = self
87+
.evm_factory
88+
.create_evm(db, env)
89+
.into_inner()
90+
.with_precompiles(WrappedPrecompile::new(
91+
EthPrecompiles::default(),
92+
self.cache.clone(),
93+
));
94+
95+
EthEvm::new(evm, false)
96+
}
97+
98+
fn create_evm_with_inspector<DB, I>(
99+
&self,
100+
db: DB,
101+
input: EvmEnv,
102+
inspector: I,
103+
) -> Self::Evm<DB, I>
104+
where
105+
DB: Database<Error: Send + Sync + 'static>,
106+
I: Inspector<EthEvmContext<DB>, EthInterpreter>,
107+
{
108+
EthEvm::new(
109+
self.create_evm(db, input)
110+
.into_inner()
111+
.with_inspector(inspector),
112+
true,
113+
)
114+
}
115+
}

crates/rbuilder/src/building/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use alloy_evm::{block::system_calls::SystemCaller, env::EvmEnv, eth::eip6110};
1818
use alloy_primitives::{Address, Bytes, B256, U256};
1919
use alloy_rpc_types_beacon::events::PayloadAttributesEvent;
2020
use cached_reads::{LocalCachedReads, SharedCachedReads};
21+
use evm::EthCachedEvmFactory;
2122
use jsonrpsee::core::Serialize;
22-
use precompile_cache::EthCachedEvmFactory;
2323
use reth::{
2424
payload::PayloadId,
2525
primitives::{Block, Receipt, SealedBlock},
@@ -56,6 +56,7 @@ pub mod built_block_trace;
5656
pub mod cached_reads;
5757
#[cfg(test)]
5858
pub mod conflict;
59+
pub mod evm;
5960
pub mod evm_inspector;
6061
pub mod fmt;
6162
pub mod order_commit;

crates/rbuilder/src/building/order_commit.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::{
77
use crate::{
88
building::{
99
estimate_payout_gas_limit,
10+
evm::EvmFactory,
1011
evm_inspector::{RBuilderEVMInspector, UsedStateTrace},
1112
},
1213
primitives::{
@@ -21,14 +22,11 @@ use alloy_eips::eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK};
2122
use alloy_primitives::{Address, B256, U256};
2223
use reth::revm::database::StateProviderDatabase;
2324
use reth_errors::ProviderError;
24-
use reth_evm::{Evm, EvmEnv, EvmFactory};
25+
use reth_evm::{Evm, EvmEnv};
2526
use reth_primitives::Receipt;
2627
use reth_provider::{StateProvider, StateProviderBox};
2728
use revm::{
28-
context::{
29-
result::{HaltReason, ResultAndState},
30-
TxEnv,
31-
},
29+
context::result::ResultAndState,
3230
context_interface::result::{EVMError, ExecutionResult, InvalidTransaction},
3331
database::{states::bundle_state::BundleRetention, BundleState, State},
3432
Database, DatabaseCommit,
@@ -1247,18 +1245,14 @@ fn update_nonce_list_with_updates(
12471245
/// thats why it can't return `TransactionErr::GasLeft` and `TransactionErr::BlobGasLeft`
12481246
fn execute_evm<Factory>(
12491247
evm_factory: &Factory,
1250-
evm_env: EvmEnv<Factory::Spec>,
1248+
evm_env: EvmEnv,
12511249
tx_with_blobs: &TransactionSignedEcRecoveredWithBlobs,
12521250
used_state_tracer: Option<&mut UsedStateTrace>,
12531251
db: impl Database<Error = ProviderError>,
12541252
blocklist: &HashSet<Address>,
12551253
) -> Result<Result<ResultAndState, TransactionErr>, CriticalCommitOrderError>
12561254
where
1257-
Factory: EvmFactory<
1258-
Tx = TxEnv,
1259-
HaltReason = HaltReason,
1260-
Error<ProviderError> = EVMError<ProviderError>,
1261-
>,
1255+
Factory: EvmFactory,
12621256
{
12631257
let tx = tx_with_blobs.internal_tx_unsecure();
12641258
let mut rbuilder_inspector = RBuilderEVMInspector::new(tx, used_state_tracer);

crates/rbuilder/src/building/payout_tx.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use super::{BlockBuildingContext, BlockState, ThreadBlockBuildingContext};
1+
use super::{evm::EvmFactory, BlockBuildingContext, BlockState, ThreadBlockBuildingContext};
22
use crate::utils::Signer;
33
use alloy_consensus::{constants::KECCAK_EMPTY, TxEip1559};
44
use alloy_primitives::{Address, TxKind as TransactionKind, U256};
55
use reth_chainspec::ChainSpec;
66
use reth_errors::ProviderError;
7-
use reth_evm::{Evm, EvmFactory};
7+
use reth_evm::Evm;
88
use reth_primitives::{Recovered, Transaction, TransactionSigned};
99
use revm::context::result::{EVMError, ExecutionResult};
1010

crates/rbuilder/src/building/precompile_cache.rs

Lines changed: 3 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,11 @@ use alloy_primitives::{Address, Bytes};
44
use derive_more::{Deref, DerefMut};
55
use lru::LruCache;
66
use parking_lot::Mutex;
7-
use reth_evm::{eth::EthEvmContext, EthEvm, EthEvmFactory, EvmEnv, EvmFactory};
87
use revm::{
9-
context::{
10-
result::{EVMError, HaltReason},
11-
BlockEnv, Cfg, CfgEnv, ContextTr, TxEnv,
12-
},
13-
handler::{EthPrecompiles, PrecompileProvider},
14-
inspector::NoOpInspector,
15-
interpreter::{interpreter::EthInterpreter, InputsImpl, InterpreterResult},
8+
context::{Cfg, ContextTr},
9+
handler::PrecompileProvider,
10+
interpreter::{InputsImpl, InterpreterResult},
1611
primitives::hardfork::SpecId,
17-
Context, Database, Inspector,
1812
};
1913
use std::{num::NonZeroUsize, sync::Arc};
2014

@@ -104,65 +98,3 @@ impl<CTX: ContextTr, P: PrecompileProvider<CTX, Output = InterpreterResult>> Pre
10498
self.precompile.contains(address)
10599
}
106100
}
107-
108-
#[derive(Debug, Clone, Default)]
109-
pub struct EthCachedEvmFactory {
110-
evm_factory: EthEvmFactory,
111-
cache: Arc<Mutex<PrecompileCache>>,
112-
}
113-
114-
impl EvmFactory for EthCachedEvmFactory {
115-
type Evm<DB, I>
116-
= EthEvm<DB, I, WrappedPrecompile<EthPrecompiles>>
117-
where
118-
DB: Database<Error: Send + Sync + 'static>,
119-
I: Inspector<EthEvmContext<DB>>;
120-
121-
type Context<DB>
122-
= Context<BlockEnv, TxEnv, CfgEnv, DB>
123-
where
124-
DB: Database<Error: Send + Sync + 'static>;
125-
126-
type Error<DBError>
127-
= EVMError<DBError>
128-
where
129-
DBError: core::error::Error + Send + Sync + 'static;
130-
131-
type Tx = TxEnv;
132-
type HaltReason = HaltReason;
133-
type Spec = SpecId;
134-
135-
fn create_evm<DB>(&self, db: DB, input: EvmEnv) -> Self::Evm<DB, NoOpInspector>
136-
where
137-
DB: Database<Error: Send + Sync + 'static>,
138-
{
139-
let evm = self
140-
.evm_factory
141-
.create_evm(db, input)
142-
.into_inner()
143-
.with_precompiles(WrappedPrecompile::new(
144-
EthPrecompiles::default(),
145-
self.cache.clone(),
146-
));
147-
148-
EthEvm::new(evm, false)
149-
}
150-
151-
fn create_evm_with_inspector<DB, I>(
152-
&self,
153-
db: DB,
154-
input: EvmEnv,
155-
inspector: I,
156-
) -> Self::Evm<DB, I>
157-
where
158-
DB: Database<Error: Send + Sync + 'static>,
159-
I: Inspector<Self::Context<DB>, EthInterpreter>,
160-
{
161-
EthEvm::new(
162-
self.create_evm(db, input)
163-
.into_inner()
164-
.with_inspector(inspector),
165-
true,
166-
)
167-
}
168-
}

crates/rbuilder/src/building/testing/evm_inspector_tests/setup.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use crate::building::{
22
cached_reads::LocalCachedReads,
3+
evm::EvmFactory,
34
evm_inspector::{RBuilderEVMInspector, UsedStateTrace},
45
testing::test_chain_state::{BlockArgs, NamedAddr, TestChainState, TestContracts, TxArgs},
56
BlockState,
67
};
78
use alloy_primitives::Address;
8-
use reth_evm::{Evm, EvmFactory};
9+
use reth_evm::Evm;
910
use reth_primitives::{Recovered, TransactionSigned};
1011

1112
#[derive(Debug)]

0 commit comments

Comments
 (0)