From 52472e6c6b125d5f038e93f6c5eddc57b230ba66 Mon Sep 17 00:00:00 2001 From: nekomoto911 Date: Tue, 24 Sep 2024 10:39:16 +0800 Subject: [PATCH] grevm: introduce grevm to reth --- .cargo/config.toml | 3 + Cargo.toml | 1 + crates/blockchain-tree/src/chain.rs | 4 +- crates/consensus/auto-seal/src/lib.rs | 6 +- crates/ethereum/evm/Cargo.toml | 1 + crates/ethereum/evm/src/execute.rs | 15 +++- crates/ethereum/evm/src/lib.rs | 1 + crates/ethereum/evm/src/parallel_execute.rs | 80 +++++++++++++++++++++ crates/evm/src/either.rs | 16 ++++- crates/evm/src/execute.rs | 26 ++++++- crates/evm/src/noop.rs | 12 +++- crates/evm/src/test_utils.rs | 11 +++ 12 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 crates/ethereum/evm/src/parallel_execute.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 9430f38d1..5cd2d124d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -14,3 +14,6 @@ rustflags = [ # in line with Linux (whereas default for Windows is 1MB) "-Clink-arg=/STACK:10000000", ] + +[net] +git-fetch-with-cli = true diff --git a/Cargo.toml b/Cargo.toml index bfe18acbf..f056d4c0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -366,6 +366,7 @@ reth-provider = { path = "crates/storage/provider" } reth-prune = { path = "crates/prune/prune" } reth-prune-types = { path = "crates/prune/types" } reth-revm = { path = "crates/revm" } +reth-grevm = { package = "grevm", git = "ssh://git@github.com/Galxe/grevm.git", branch = "main" } reth-rpc = { path = "crates/rpc/rpc" } reth-rpc-api = { path = "crates/rpc/rpc-api" } reth-rpc-api-testing-util = { path = "crates/rpc/rpc-testing-util" } diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 74510de23..069d8fd2d 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -205,7 +205,7 @@ impl AppendableChain { let provider = BundleStateProvider::new(state_provider, bundle_state_data_provider); let db = StateProviderDatabase::new(&provider); - let executor = externals.executor_factory.executor(db); + let executor = externals.executor_factory.parallel_executor(db); let block_hash = block.hash(); let block = block.unseal(); @@ -241,7 +241,7 @@ impl AppendableChain { return Err(ConsensusError::BodyStateRootDiff( GotExpected { got: state_root, expected: block.state_root }.into(), ) - .into()) + .into()); } tracing::debug!( diff --git a/crates/consensus/auto-seal/src/lib.rs b/crates/consensus/auto-seal/src/lib.rs index 8bf9616a0..84d79c7dd 100644 --- a/crates/consensus/auto-seal/src/lib.rs +++ b/crates/consensus/auto-seal/src/lib.rs @@ -370,13 +370,13 @@ impl StorageInner { trace!(target: "consensus::auto", transactions=?&block.body, "executing transactions"); - let mut db = StateProviderDatabase::new( + let db = Arc::new(StateProviderDatabase::new( provider.latest().map_err(InternalBlockExecutionError::LatestBlock)?, - ); + )); // execute the block let block_execution_output = - executor.executor(&mut db).execute((&block, U256::ZERO).into())?; + executor.parallel_executor(db.clone()).execute((&block, U256::ZERO).into())?; let gas_used = block_execution_output.gas_used; let execution_outcome = ExecutionOutcome::from((block_execution_output, block.number)); let hashed_state = HashedPostState::from_bundle_state(&execution_outcome.state().state); diff --git a/crates/ethereum/evm/Cargo.toml b/crates/ethereum/evm/Cargo.toml index 25f2d9c6a..3effde207 100644 --- a/crates/ethereum/evm/Cargo.toml +++ b/crates/ethereum/evm/Cargo.toml @@ -17,6 +17,7 @@ reth-ethereum-forks.workspace = true reth-evm.workspace = true reth-primitives = { workspace = true, features = ["reth-codec"] } reth-revm.workspace = true +reth-grevm.workspace = true reth-ethereum-consensus.workspace = true reth-prune-types.workspace = true reth-execution-types.workspace = true diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 8c15dee96..f7d99c569 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -2,6 +2,7 @@ use crate::{ dao_fork::{DAO_HARDFORK_BENEFICIARY, DAO_HARDKFORK_ACCOUNTS}, + parallel_execute::EthGrevmExecutor, EthEvmConfig, }; use core::fmt::Display; @@ -10,7 +11,7 @@ use reth_ethereum_consensus::validate_block_post_execution; use reth_evm::{ execute::{ BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput, - BlockExecutorProvider, BlockValidationError, Executor, ProviderError, + BlockExecutorProvider, BlockValidationError, Executor, ParallelDatabase, ProviderError, }, system_calls::{ apply_beacon_root_contract_call, apply_blockhashes_contract_call, @@ -89,6 +90,9 @@ where type BatchExecutor + Display>> = EthBatchExecutor; + type ParallelExecutor + Display + Clone>> = + EthGrevmExecutor; + fn executor(&self, db: DB) -> Self::Executor where DB: Database + Display>, @@ -103,6 +107,13 @@ where let executor = self.eth_executor(db); EthBatchExecutor { executor, batch_record: BlockBatchRecord::default() } } + + fn parallel_executor(&self, db: DB) -> Self::ParallelExecutor + where + DB: ParallelDatabase + Display + Clone>, + { + EthGrevmExecutor::new(self.chain_spec.clone(), self.evm_config.clone(), db) + } } /// Helper type for the output of executing a block. @@ -175,7 +186,7 @@ where transaction_gas_limit: transaction.gas_limit(), block_available_gas, } - .into()) + .into()); } self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender); diff --git a/crates/ethereum/evm/src/lib.rs b/crates/ethereum/evm/src/lib.rs index bead8ae39..2f5ab0f60 100644 --- a/crates/ethereum/evm/src/lib.rs +++ b/crates/ethereum/evm/src/lib.rs @@ -24,6 +24,7 @@ mod config; pub use config::{revm_spec, revm_spec_by_timestamp_after_merge}; pub mod execute; +pub mod parallel_execute; /// Ethereum DAO hardfork state change data. pub mod dao_fork; diff --git a/crates/ethereum/evm/src/parallel_execute.rs b/crates/ethereum/evm/src/parallel_execute.rs new file mode 100644 index 000000000..2e9df54f8 --- /dev/null +++ b/crates/ethereum/evm/src/parallel_execute.rs @@ -0,0 +1,80 @@ +use core::fmt::{Debug, Display}; +use std::sync::Arc; + +use reth_chainspec::ChainSpec; +use reth_evm::{ + execute::{BlockExecutionError, Executor, ParallelDatabase}, + ConfigureEvm, +}; +use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput}; +use reth_grevm::{new_grevm_scheduler, GrevmScheduler}; + +use reth_primitives::{BlockWithSenders, Receipt}; +use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, TxEnv}; + +pub struct EthGrevmExecutor { + chain_spec: Arc, + evm_config: EvmConfig, + database: DB, +} + +impl EthGrevmExecutor { + pub fn new(chain_spec: Arc, evm_config: EvmConfig, database: DB) -> Self { + Self { chain_spec, evm_config, database } + } +} + +impl Executor for EthGrevmExecutor +where + EvmConfig: ConfigureEvm, + DB: ParallelDatabase, +{ + type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; + type Output = BlockExecutionOutput; + type Error = BlockExecutionError; + + fn execute(self, input: Self::Input<'_>) -> Result { + // Initialize evm env + let mut cfg = CfgEnvWithHandlerCfg::new(Default::default(), Default::default()); + let mut block_env = BlockEnv::default(); + self.evm_config.fill_cfg_and_block_env( + &mut cfg, + &mut block_env, + self.chain_spec.as_ref(), + &input.block.header, + input.total_difficulty, + ); + let env = EnvWithHandlerCfg::new_with_cfg_env(cfg, block_env, Default::default()); + + // Fill TxEnv from transaction + let mut txs = vec![TxEnv::default(); input.block.body.len()]; + for (tx_env, (sender, tx)) in txs.iter_mut().zip(input.block.transactions_with_sender()) { + self.evm_config.fill_tx_env(tx_env, tx, *sender); + } + + let executor = new_grevm_scheduler(env.spec_id(), *env.env, self.database, txs); + let output = executor.parallel_execute().map_err(|e| BlockExecutionError::msg(e))?; + + let mut receipts = Vec::with_capacity(output.results.len()); + let mut cumulative_gas_used = 0; + for (result, tx_type) in + output.results.into_iter().zip(input.block.transactions().map(|tx| tx.tx_type())) + { + cumulative_gas_used += result.gas_used(); + receipts.push(Receipt { + tx_type, + success: result.is_success(), + cumulative_gas_used, + logs: result.into_logs(), + ..Default::default() + }); + } + + Ok(BlockExecutionOutput { + state: output.state, + receipts, + requests: vec![], + gas_used: cumulative_gas_used, + }) + } +} diff --git a/crates/evm/src/either.rs b/crates/evm/src/either.rs index 84e1733e4..0888be58a 100644 --- a/crates/evm/src/either.rs +++ b/crates/evm/src/either.rs @@ -2,7 +2,7 @@ use core::fmt::Display; -use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor}; +use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor, ParallelDatabase}; use reth_execution_errors::BlockExecutionError; use reth_execution_types::{BlockExecutionInput, BlockExecutionOutput, ExecutionOutcome}; use reth_primitives::{BlockNumber, BlockWithSenders, Receipt}; @@ -24,6 +24,9 @@ where type BatchExecutor + Display>> = Either, B::BatchExecutor>; + type ParallelExecutor + Display + Clone>> = + Either, B::ParallelExecutor>; + fn executor(&self, db: DB) -> Self::Executor where DB: Database + Display>, @@ -43,6 +46,16 @@ where Self::Right(b) => Either::Right(b.batch_executor(db)), } } + + fn parallel_executor(&self, db: DB) -> Self::ParallelExecutor + where + DB: ParallelDatabase + Display + Clone>, + { + match self { + Self::Left(a) => Either::Left(a.parallel_executor(db)), + Self::Right(b) => Either::Right(b.parallel_executor(db)), + } + } } impl Executor for Either @@ -59,7 +72,6 @@ where Output = BlockExecutionOutput, Error = BlockExecutionError, >, - DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = BlockExecutionOutput; diff --git a/crates/evm/src/execute.rs b/crates/evm/src/execute.rs index 2109d557f..fedd824d0 100644 --- a/crates/evm/src/execute.rs +++ b/crates/evm/src/execute.rs @@ -9,7 +9,7 @@ use core::fmt::Display; use reth_primitives::{BlockNumber, BlockWithSenders, Receipt}; use reth_prune_types::PruneModes; -use revm_primitives::db::Database; +use revm_primitives::db::{Database, DatabaseRef}; /// A general purpose executor trait that executes an input (e.g. block) and produces an output /// (e.g. state changes and receipts). @@ -94,6 +94,10 @@ pub trait BatchExecutor { fn size_hint(&self) -> Option; } +pub trait ParallelDatabase: DatabaseRef + Send + Sync {} + +impl + Send + Sync> ParallelDatabase for T {} + /// A type that can create a new executor for block execution. pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static { /// An executor that can execute a single block given a database. @@ -122,6 +126,13 @@ pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static { Error = BlockExecutionError, >; + type ParallelExecutor + Display + Clone>>: for<'a> Executor< + DB, + Input<'a> = BlockExecutionInput<'a, BlockWithSenders>, + Output = BlockExecutionOutput, + Error = BlockExecutionError, + >; + /// Creates a new executor for single block execution. /// /// This is used to execute a single block and get the changed state. @@ -136,6 +147,10 @@ pub trait BlockExecutorProvider: Send + Sync + Clone + Unpin + 'static { fn batch_executor(&self, db: DB) -> Self::BatchExecutor where DB: Database + Display>; + + fn parallel_executor(&self, db: DB) -> Self::ParallelExecutor + where + DB: ParallelDatabase + Display + Clone>; } #[cfg(test)] @@ -152,6 +167,8 @@ mod tests { impl BlockExecutorProvider for TestExecutorProvider { type Executor + Display>> = TestExecutor; type BatchExecutor + Display>> = TestExecutor; + type ParallelExecutor + Display + Clone>> = + TestExecutor; fn executor(&self, _db: DB) -> Self::Executor where @@ -166,6 +183,13 @@ mod tests { { TestExecutor(PhantomData) } + + fn parallel_executor(&self, _db: DB) -> Self::ParallelExecutor + where + DB: ParallelDatabase + Display + Clone>, + { + TestExecutor(PhantomData) + } } struct TestExecutor(PhantomData); diff --git a/crates/evm/src/noop.rs b/crates/evm/src/noop.rs index ff8e893b2..a9f5afed9 100644 --- a/crates/evm/src/noop.rs +++ b/crates/evm/src/noop.rs @@ -9,7 +9,7 @@ use reth_prune_types::PruneModes; use reth_storage_errors::provider::ProviderError; use revm_primitives::db::Database; -use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor}; +use crate::execute::{BatchExecutor, BlockExecutorProvider, Executor, ParallelDatabase}; const UNAVAILABLE_FOR_NOOP: &str = "execution unavailable for noop"; @@ -23,6 +23,9 @@ impl BlockExecutorProvider for NoopBlockExecutorProvider { type BatchExecutor + Display>> = Self; + type ParallelExecutor + Display + Clone>> = + Self; + fn executor(&self, _: DB) -> Self::Executor where DB: Database + Display>, @@ -36,6 +39,13 @@ impl BlockExecutorProvider for NoopBlockExecutorProvider { { Self } + + fn parallel_executor(&self, _: DB) -> Self::ParallelExecutor + where + DB: ParallelDatabase + Display + Clone>, + { + Self + } } impl Executor for NoopBlockExecutorProvider { diff --git a/crates/evm/src/test_utils.rs b/crates/evm/src/test_utils.rs index c3aa34a56..c30c0c4f5 100644 --- a/crates/evm/src/test_utils.rs +++ b/crates/evm/src/test_utils.rs @@ -2,6 +2,7 @@ use crate::execute::{ BatchExecutor, BlockExecutionInput, BlockExecutionOutput, BlockExecutorProvider, Executor, + ParallelDatabase, }; use parking_lot::Mutex; use reth_execution_errors::BlockExecutionError; @@ -30,6 +31,9 @@ impl BlockExecutorProvider for MockExecutorProvider { type BatchExecutor + Display>> = Self; + type ParallelExecutor + Display + Clone>> = + Self; + fn executor(&self, _: DB) -> Self::Executor where DB: Database + Display>, @@ -43,6 +47,13 @@ impl BlockExecutorProvider for MockExecutorProvider { { self.clone() } + + fn parallel_executor(&self, _: DB) -> Self::ParallelExecutor + where + DB: ParallelDatabase + Display + Clone>, + { + self.clone() + } } impl Executor for MockExecutorProvider {