diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index af6f300..bed129d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,19 +39,14 @@ jobs: uses: actions/checkout@v4 with: submodules: recursive - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: stable - + - uses: Swatinem/rust-cache@v2 with: key: cargo-check cache-on-failure: true - - name: cargo check --workspace --all-features - run: cargo check --workspace --all-features + - name: cargo check --workspace --all-targets --all-features + run: cargo check --workspace --all-targets --all-features cargo-clippy: name: cargo clippy @@ -86,4 +81,4 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - name: Run tests - run: cargo test --workspace --features ci -- --nocapture \ No newline at end of file + run: cargo test --workspace --all-targets --features ci -- --nocapture \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index dcaf63e..6fef72c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4151,14 +4151,17 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types", "async-trait", + "auto_impl", "eth-network-exts", "exe-runners", "eyre", "futures", "op-alloy-consensus", "op-alloy-network", + "op-revm", "reth-chainspec", "reth-db", + "reth-evm", "reth-network-api", "reth-node-ethereum", "reth-node-types", diff --git a/Cargo.toml b/Cargo.toml index 3237f8a..4e925f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ reth-optimism-rpc = { git = "https://github.com/ethereum-optimism/optimism", ver reth-optimism-primitives = { git = "https://github.com/ethereum-optimism/optimism", version = "1.11.3", tag = "op-reth/v1.11.3" } reth-revm = { git = "https://github.com/paradigmxyz/reth", version = "1.11.3", tag = "v1.11.3" } +reth-evm = { git = "https://github.com/paradigmxyz/reth", version = "1.11.3", tag = "v1.11.3" } reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", version = "1.11.3", tag = "v1.11.3" } reth-storage-api = { git = "https://github.com/paradigmxyz/reth", version = "1.11.3", tag = "v1.11.3" } reth-node-types = { git = "https://github.com/paradigmxyz/reth", version = "1.11.3", tag = "v1.11.3" } @@ -62,6 +63,13 @@ revm = { version = "34.0.0", features = [ "optional_block_gas_limit", "c-kzg", ], default-features = false } +op-revm = { version = "15.0.0", features = [ + "std", + "secp256k1", + "optional_balance_check", + "optional_block_gas_limit", + "c-kzg", +], default-features = false } # async tokio = "1" diff --git a/crates/eth-network-exts/src/lib.rs b/crates/eth-network-exts/src/lib.rs index 83118b0..e03cec2 100644 --- a/crates/eth-network-exts/src/lib.rs +++ b/crates/eth-network-exts/src/lib.rs @@ -8,6 +8,14 @@ pub trait EthNetworkExt: Send + Sync { type TypeExt; const CHAIN_ID: u64; + + fn is_op_chain() -> bool { + match Self::CHAIN_ID { + 1 | 11155111 => false, + 8453 | 130 => true, + _ => unreachable!() + } + } } pub trait AllExtensions: std::fmt::Debug + Send + Sync + Clone + Copy + Unpin + 'static {} diff --git a/crates/lib-reth/Cargo.toml b/crates/lib-reth/Cargo.toml index 1303053..281aa14 100644 --- a/crates/lib-reth/Cargo.toml +++ b/crates/lib-reth/Cargo.toml @@ -43,6 +43,7 @@ reth-revm = { workspace = true, optional = true } reth-rpc-eth-api = { workspace = true, optional = true } reth-storage-api = { workspace = true, optional = true } reth-node-types = { workspace = true, optional = true } +reth-evm = { workspace = true, optional = true } uniswap-storage = { workspace = true, optional = true } eth-network-exts.workspace = true @@ -73,6 +74,7 @@ revm.workspace = true revm-database = { workspace = true, default-features = false, features = [ "alloydb", ] } +op-revm = { workspace = true, optional = true } # async tokio = { workspace = true, features = ["full"] } @@ -82,6 +84,7 @@ async-trait.workspace = true # misc eyre.workspace = true exe-runners = { workspace = true, features = ["reth-tasks"] } +auto_impl.workspace = true [dev-dependencies] @@ -90,36 +93,22 @@ serial_test.workspace = true [features] default = ["full"] -full = ["ipc", "ws", "uniswap-storage", "revm", "reth-libmdbx"] +full = ["mainnet-full", "op-full", "ipc", "ws", "uniswap-storage"] ipc = [] ws = [] -rayon = ["exe-runners/rayon"] +mainnet-full = ["revm", "reth-db"] +op-full = ["op-revm", "op-reth-db"] -revm = [ - "reth-libmdbx", - "dep:reth-rpc-eth-api", - "dep:reth-revm", - "dep:alloy-eips", -] -op-reth-libmdbx = [ - "reth-libmdbx", - "dep:reth-optimism-chainspec", - "dep:reth-optimism-primitives", - "dep:reth-optimism-node", - "dep:reth-optimism-evm", - "dep:reth-optimism-rpc", - "dep:op-alloy-network", - "dep:op-alloy-consensus", - "uniswap-storage/l2-angstrom", - "eth-network-exts/l2", -] +revm = ["dep:alloy-eips", "dep:reth-revm", "dep:reth-evm"] +op-revm = ["revm", "dep:op-revm", "dep:op-alloy-network"] -reth-libmdbx = [ + +reth-db = [ "dep:reth-provider", "dep:reth-rpc", "dep:reth-transaction-pool", @@ -134,7 +123,21 @@ reth-libmdbx = [ "dep:reth-storage-api", "dep:reth-rpc-eth-api", ] +op-reth-db = [ + "reth-db", + "dep:reth-optimism-chainspec", + "dep:reth-optimism-primitives", + "dep:reth-optimism-node", + "dep:reth-optimism-evm", + "dep:reth-optimism-rpc", + "dep:op-alloy-network", + "dep:op-alloy-consensus", + "uniswap-storage/l2-angstrom", + "eth-network-exts/l2", +] + uniswap-storage = ["dep:uniswap-storage", "dep:alloy-eips"] +rayon = ["exe-runners/rayon"] ci = [] diff --git a/crates/lib-reth/README.md b/crates/lib-reth/README.md index c164461..18d27d5 100644 --- a/crates/lib-reth/README.md +++ b/crates/lib-reth/README.md @@ -11,13 +11,13 @@ This crate provides a comprehensive library for connecting to Ethereum endpoints - HTTP (default) - IPC (feature = `ipc`) - WebSocket (feature = `ws`) -- Direct database access via libmdbx (feature = `reth-libmdbx`) +- Direct database access via libmdbx (feature = `reth-db`) ## Features - `full` - All connection types and integrations - `revm` - REVM execution support -- `op-reth-libmdbx` - Optimism node support +- `op-reth-db` - Optimism node support - `rayon` - Parallel execution support ## Supported Functionality diff --git a/crates/lib-reth/src/lib.rs b/crates/lib-reth/src/lib.rs index 2ac4b13..c0a6b81 100644 --- a/crates/lib-reth/src/lib.rs +++ b/crates/lib-reth/src/lib.rs @@ -1,22 +1,22 @@ #![allow(ambiguous_glob_reexports)] -#[cfg(feature = "reth-libmdbx")] +#[cfg(feature = "reth-db")] pub mod reth_libmdbx; pub mod rpc; pub mod traits; -#[cfg(feature = "op-reth-libmdbx")] +#[cfg(feature = "op-reth-db")] pub mod op_reth { pub use reth_optimism_chainspec::*; pub use reth_optimism_node::*; pub use reth_optimism_primitives::*; } -#[cfg(feature = "reth-libmdbx")] +#[cfg(feature = "reth-db")] pub use regular_reth::*; -#[cfg(feature = "reth-libmdbx")] +#[cfg(feature = "reth-db")] mod regular_reth { pub use reth_chainspec::*; pub use reth_node_ethereum::EthereumNode; diff --git a/crates/lib-reth/src/reth_libmdbx/node_types/mod.rs b/crates/lib-reth/src/reth_libmdbx/node_types/mod.rs index faf1b5a..d1c9840 100644 --- a/crates/lib-reth/src/reth_libmdbx/node_types/mod.rs +++ b/crates/lib-reth/src/reth_libmdbx/node_types/mod.rs @@ -1,7 +1,5 @@ use std::sync::Arc; -#[cfg(feature = "revm")] -use alloy_eips::BlockId; use alloy_provider::{IpcConnect, RootProvider, WsConnect, builder}; use eth_network_exts::EthNetworkExt; use reth_node_types::NodeTypes; @@ -14,7 +12,7 @@ use reth_tasks::TaskSpawner; use crate::{reth_libmdbx::DbConfig, traits::EthStream}; pub mod node; -#[cfg(feature = "op-reth-libmdbx")] +#[cfg(feature = "op-reth-db")] pub mod op_node; pub(crate) fn provider_runtime() -> eyre::Result { @@ -129,18 +127,27 @@ where } #[cfg(feature = "revm")] -impl crate::traits::EthRevm for RethNodeClient -where - Ext::RethNode: NodeClientSpec -{ - type InnerDb = crate::traits::reth_revm_utils::RethLibmdbxDatabaseRef; +mod revm_impl { - fn make_inner_db(&self, block: BlockId) -> eyre::Result { - use reth_provider::StateProviderFactory; + use reth_provider::StateProviderFactory; + use reth_revm::database::StateProviderDatabase; - let state_provider = self.eth_db_provider().state_by_block_id(block)?; + use super::*; + use crate::traits::{EthRevm, EthRevmParams, reth_revm_utils::RethLibmdbxDatabaseRef}; - let this = reth_revm::database::StateProviderDatabase::new(state_provider); - Ok(crate::traits::reth_revm_utils::RethLibmdbxDatabaseRef::new(this)) + impl EthRevm for RethNodeClient + where + Ext: EthNetworkExt, + Ext::RethNode: NodeClientSpec + { + type InnerDb = RethLibmdbxDatabaseRef; + type Params = EthRevmParams; + + fn make_inner_db(&self, params: &EthRevmParams) -> eyre::Result { + let state_provider = self.eth_db_provider().state_by_block_id(params.block_id)?; + + let this = StateProviderDatabase::new(state_provider); + Ok(RethLibmdbxDatabaseRef::new(this)) + } } } diff --git a/crates/lib-reth/src/rpc.rs b/crates/lib-reth/src/rpc.rs index 3c4a096..f8901e5 100644 --- a/crates/lib-reth/src/rpc.rs +++ b/crates/lib-reth/src/rpc.rs @@ -14,8 +14,6 @@ use alloy_rpc_client::WsConnect; use alloy_rpc_types::Filter; #[cfg(any(feature = "ipc", feature = "ws"))] use futures::Stream; -#[cfg(feature = "revm")] -use revm_database::{AlloyDB, WrapDatabaseAsync}; #[cfg(any(feature = "ipc", feature = "ws"))] use crate::traits::EthStream; @@ -103,18 +101,35 @@ where } #[cfg(feature = "revm")] -impl crate::traits::AsyncEthRevm for EthRpcClient -where - P: Provider + Clone, - N: Network -{ - type InnerDb = AlloyDB; - - fn make_inner_db( - &self, - block_number: u64, - handle: tokio::runtime::Handle - ) -> eyre::Result> { - Ok(WrapDatabaseAsync::with_handle(AlloyDB::new(self.provider.clone(), block_number.into()), handle)) +mod revm_impl { + + use revm_database::{AlloyDB, WrapDatabaseAsync}; + + use super::*; + use crate::traits::{AsyncEthRevmParams, EthRevm}; + + impl EthRevm for EthRpcClient + where + P: Provider + Clone, + N: Network + { + type InnerDb = WrapDatabaseAsync>; + type Params = AsyncEthRevmParams; + + fn make_inner_db(&self, params: &AsyncEthRevmParams) -> eyre::Result { + Ok(WrapDatabaseAsync::with_handle(AlloyDB::new(self.provider.clone(), params.block_id), params.handle.clone())) + } + } + + impl EthRevm for RootProvider + where + N: Network + { + type InnerDb = WrapDatabaseAsync>>; + type Params = AsyncEthRevmParams; + + fn make_inner_db(&self, params: &AsyncEthRevmParams) -> eyre::Result { + Ok(WrapDatabaseAsync::with_handle(AlloyDB::new(self.clone(), params.block_id), params.handle.clone())) + } } } diff --git a/crates/lib-reth/src/traits/mod.rs b/crates/lib-reth/src/traits/mod.rs index 13a804a..7569bfa 100644 --- a/crates/lib-reth/src/traits/mod.rs +++ b/crates/lib-reth/src/traits/mod.rs @@ -6,5 +6,5 @@ mod revm; #[cfg(feature = "revm")] pub use revm::*; -#[cfg(feature = "revm")] +#[cfg(all(feature = "revm", feature = "reth-db"))] pub mod reth_revm_utils; diff --git a/crates/lib-reth/src/traits/revm.rs b/crates/lib-reth/src/traits/revm.rs index 0117105..72a0dba 100644 --- a/crates/lib-reth/src/traits/revm.rs +++ b/crates/lib-reth/src/traits/revm.rs @@ -1,69 +1,135 @@ use alloy_eips::BlockId; +use alloy_primitives::ChainId; use revm::{ - Context, DatabaseRef, MainBuilder, MainContext, + Context, DatabaseRef, Journal, MainBuilder, MainContext, context::{BlockEnv, CfgEnv, Evm, TxEnv}, - handler::{EthFrame, EthPrecompiles, instructions::EthInstructions}, + handler::{EthFrame, EthPrecompiles, EvmTr, instructions::EthInstructions}, interpreter::interpreter::EthInterpreter }; -use revm_database::{CacheDB, WrapDatabaseAsync, async_db::DatabaseAsyncRef}; +use revm_database::CacheDB; +type NetworkRevmContext = Context, Journal>, CHAIN>; -pub type RevmEvm = Evm< - Context, +type MainnetRevmEvm = Evm< + NetworkRevmContext, (), - EthInstructions>, + EthInstructions>, EthPrecompiles, EthFrame >; -/// revm utils +pub fn empty_mainnet_revm( + db: CacheDB, + chain_id: ChainId, + disable_nonce_check: bool +) -> MainnetRevmEvm { + let mut evm = Context::mainnet() + .modify_cfg_chained(|cfg| cfg.chain_id = chain_id) + .with_db(db) + .build_mainnet(); + + if disable_nonce_check { + evm.ctx_mut() + .modify_cfg(|cfg| cfg.disable_nonce_check = true); + } + + evm +} + +#[cfg(feature = "op-revm")] +pub use op_impl::empty_op_mainnet_revm; +#[cfg(feature = "op-revm")] +pub use op_revm::OpTransaction; + +#[cfg(feature = "op-revm")] +mod op_impl { + use op_revm::{DefaultOp, L1BlockInfo, OpBuilder, OpEvm, OpSpecId, OpTransaction, precompiles::OpPrecompiles}; + use revm::handler::EvmTr; + + use super::*; + + type OptimismRevmEvm = OpEvm< + NetworkRevmContext, CfgEnv, L1BlockInfo>, + (), + EthInstructions, CfgEnv, L1BlockInfo>>, + OpPrecompiles + >; + + pub fn empty_op_mainnet_revm( + db: CacheDB, + chain_id: ChainId, + disable_nonce_check: bool + ) -> OptimismRevmEvm { + let mut evm = Context::op() + .modify_cfg_chained(|cfg| cfg.chain_id = chain_id) + .with_db(db) + .build_op(); + + if disable_nonce_check { + evm.ctx_mut() + .modify_cfg(|cfg| cfg.disable_nonce_check = true); + } + + evm + } +} + +#[auto_impl::auto_impl(&, Box, Arc)] pub trait EthRevm { type InnerDb: DatabaseRef; + type Params: EthRevmInput; /// `makes the inner database fetcher` - fn make_inner_db(&self, block_id: BlockId) -> eyre::Result; + fn make_inner_db(&self, params: &Self::Params) -> eyre::Result; /// `makes a new cache db` - fn make_cache_db(&self, block_id: BlockId) -> eyre::Result> { - Ok(CacheDB::new(self.make_inner_db(block_id)?)) + fn make_cache_db(&self, params: &Self::Params) -> eyre::Result> { + Ok(CacheDB::new(self.make_inner_db(params)?)) } +} - /// `makes a new cache db` - fn make_empty_evm(&self, block_id: BlockId) -> eyre::Result>> { - let cache = self.make_cache_db(block_id)?; - let evm = Context::mainnet().with_db(cache).build_mainnet(); - Ok(evm) - } +pub trait EthRevmInput: Send + Sync { + fn block_id(&self) -> BlockId; + + fn chain_id(&self) -> ChainId; + + fn handle(&self) -> Option; } -/// async revm utils -pub trait AsyncEthRevm { - type InnerDb: DatabaseAsyncRef; +pub struct EthRevmParams { + pub block_id: BlockId, + pub chain_id: ChainId +} - /// `makes the inner database fetcher` - fn make_inner_db( - &self, - block_number: u64, - handle: tokio::runtime::Handle - ) -> eyre::Result>; +impl EthRevmInput for EthRevmParams { + fn block_id(&self) -> BlockId { + self.block_id + } - /// `makes a new cache db` - fn make_cache_db( - &self, - block_number: u64, - handle: tokio::runtime::Handle - ) -> eyre::Result>> { - Ok(CacheDB::new(self.make_inner_db(block_number, handle)?)) + fn chain_id(&self) -> ChainId { + self.chain_id } - /// `makes a new evm with a cache db` - fn make_evm( - &self, - block_number: u64, - handle: tokio::runtime::Handle - ) -> eyre::Result>>> { - let cache = self.make_cache_db(block_number, handle)?; - let evm = Context::mainnet().with_db(cache).build_mainnet(); + fn handle(&self) -> Option { + None + } +} + +pub struct AsyncEthRevmParams { + pub block_id: BlockId, + pub chain_id: ChainId, + pub handle: tokio::runtime::Handle +} + +impl EthRevmInput for AsyncEthRevmParams { + fn block_id(&self) -> BlockId { + self.block_id + } + + fn chain_id(&self) -> ChainId { + self.chain_id + } - Ok(evm) + fn handle(&self) -> Option { + Some(self.handle.clone()) } }