From 8299ca6fd662e5be3655696776f504753b9e6d03 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 23 Feb 2023 14:16:42 +0100 Subject: [PATCH] feat: add reth-revm crate (#1526) --- Cargo.lock | 15 ++- Cargo.toml | 1 + crates/executor/Cargo.toml | 1 + crates/executor/src/executor.rs | 51 +++----- crates/executor/src/lib.rs | 3 - crates/revm/Cargo.toml | 15 +++ crates/revm/src/compat.rs | 34 +++++ crates/{executor => revm}/src/config.rs | 0 crates/revm/src/database.rs | 63 ++++++++++ .../src/revm_wrap.rs => revm/src/env.rs} | 119 ++++-------------- crates/revm/src/lib.rs | 20 +++ crates/rpc/rpc-engine-api/Cargo.toml | 1 + crates/rpc/rpc-engine-api/src/engine_api.rs | 6 +- crates/rpc/rpc/Cargo.toml | 2 +- crates/rpc/rpc/src/eth/api/call.rs | 2 +- crates/stages/Cargo.toml | 1 + crates/stages/src/stages/execution.rs | 6 +- 17 files changed, 200 insertions(+), 140 deletions(-) create mode 100644 crates/revm/Cargo.toml create mode 100644 crates/revm/src/compat.rs rename crates/{executor => revm}/src/config.rs (100%) create mode 100644 crates/revm/src/database.rs rename crates/{executor/src/revm_wrap.rs => revm/src/env.rs} (55%) create mode 100644 crates/revm/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d7d0e3a42..1ef97790c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4598,6 +4598,7 @@ dependencies = [ "reth-interfaces", "reth-primitives", "reth-provider", + "reth-revm", "reth-rlp", "revm", "rlp", @@ -4843,6 +4844,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "reth-revm" +version = "0.1.0" +dependencies = [ + "reth-interfaces", + "reth-primitives", + "reth-provider", + "revm", +] + [[package]] name = "reth-rlp" version = "0.1.2" @@ -4887,11 +4898,11 @@ dependencies = [ "jsonwebtoken", "pin-project", "rand 0.8.5", - "reth-executor", "reth-interfaces", "reth-network-api", "reth-primitives", "reth-provider", + "reth-revm", "reth-rlp", "reth-rpc-api", "reth-rpc-engine-api", @@ -4953,6 +4964,7 @@ dependencies = [ "reth-interfaces", "reth-primitives", "reth-provider", + "reth-revm", "reth-rlp", "reth-rpc-types", "thiserror", @@ -5048,6 +5060,7 @@ dependencies = [ "reth-metrics-derive", "reth-primitives", "reth-provider", + "reth-revm", "reth-rlp", "reth-staged-sync", "serde", diff --git a/Cargo.toml b/Cargo.toml index bd29418d4..d505dc105 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ members = [ "crates/net/network", "crates/net/downloaders", "crates/primitives", + "crates/revm", "crates/rlp", "crates/rlp/rlp-derive", "crates/rpc/ipc", diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index 3e0f0fbb2..18ff91ecf 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -10,6 +10,7 @@ readme = "README.md" # reth reth-primitives = { path = "../primitives" } reth-interfaces = { path = "../interfaces" } +reth-revm = { path = "../revm" } reth-rlp = { path = "../rlp" } reth-db = { path = "../storage/db" } reth-provider = { path = "../storage/provider" } diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index ec3bb77fb..f7362139f 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -1,22 +1,22 @@ -use crate::{ - config::{revm_spec, WEI_2ETH, WEI_3ETH, WEI_5ETH}, - execution_result::{ - AccountChangeSet, AccountInfoChangeSet, ExecutionResult, TransactionChangeSet, - }, - revm_wrap::{self, into_reth_log, to_reth_acc, SubState}, +use crate::execution_result::{ + AccountChangeSet, AccountInfoChangeSet, ExecutionResult, TransactionChangeSet, }; use hashbrown::hash_map::Entry; use reth_interfaces::executor::{BlockExecutor, Error}; use reth_primitives::{ - bloom::logs_bloom, Account, Address, Block, Bloom, ChainSpec, Hardfork, Head, Header, Log, - Receipt, TransactionSigned, H256, U256, + bloom::logs_bloom, Account, Address, Block, Bloom, ChainSpec, Hardfork, Header, Log, Receipt, + TransactionSigned, H256, U256, }; use reth_provider::StateProvider; +use reth_revm::{ + config::{WEI_2ETH, WEI_3ETH, WEI_5ETH}, + database::SubState, + env::{fill_block_env, fill_cfg_env, fill_tx_env}, + into_reth_log, to_reth_acc, +}; use revm::{ db::AccountState, - primitives::{ - Account as RevmAccount, AccountInfo, AnalysisKind, Bytecode, ResultAndState, SpecId, - }, + primitives::{Account as RevmAccount, AccountInfo, Bytecode, ResultAndState, SpecId}, EVM, }; use std::collections::{BTreeMap, HashMap}; @@ -63,24 +63,11 @@ where } } - fn init_block_env(&mut self, header: &Header, total_difficulty: U256) { - let spec_id = revm_spec( - self.chain_spec, - Head { - number: header.number, - timestamp: header.timestamp, - difficulty: header.difficulty, - total_difficulty, - hash: Default::default(), - }, - ); - - self.evm.env.cfg.chain_id = U256::from(self.chain_spec.chain().id()); - self.evm.env.cfg.spec_id = spec_id; - self.evm.env.cfg.perf_all_precompiles_have_balance = false; - self.evm.env.cfg.perf_analyse_created_bytecodes = AnalysisKind::Raw; - - revm_wrap::fill_block_env(&mut self.evm.env.block, header, spec_id >= SpecId::MERGE); + /// Initializes the config and block env. + fn init_env(&mut self, header: &Header, total_difficulty: U256) { + fill_cfg_env(&mut self.evm.env.cfg, self.chain_spec, header, total_difficulty); + let after_merge = self.evm.env.cfg.spec_id >= SpecId::MERGE; + fill_block_env(&mut self.evm.env.block, header, after_merge); } /// Commit change to database and return change diff that is used to update state and create @@ -356,7 +343,7 @@ where ) -> Result { let senders = self.recover_senders(&block.body, senders)?; - self.init_block_env(&block.header, total_difficulty); + self.init_env(&block.header, total_difficulty); let mut cumulative_gas_used = 0; // output of execution @@ -374,7 +361,7 @@ where } // Fill revm structure. - revm_wrap::fill_tx_env(&mut self.evm.env.tx, transaction, sender); + fill_tx_env(&mut self.evm.env.tx, transaction, sender); // Execute transaction. let out = if self.use_printer_tracer { @@ -507,12 +494,12 @@ pub fn execute( #[cfg(test)] mod tests { use super::*; - use crate::revm_wrap::State; use reth_primitives::{ hex_literal::hex, keccak256, Account, Address, Bytes, ChainSpecBuilder, ForkCondition, StorageKey, H256, MAINNET, U256, }; use reth_provider::{AccountProvider, BlockHashProvider, StateProvider}; + use reth_revm::database::State; use reth_rlp::Decodable; use std::{collections::HashMap, str::FromStr}; diff --git a/crates/executor/src/lib.rs b/crates/executor/src/lib.rs index 6e599039e..523fc7a0f 100644 --- a/crates/executor/src/lib.rs +++ b/crates/executor/src/lib.rs @@ -7,12 +7,9 @@ //! Reth executor executes transaction in block of data. -pub mod config; pub mod eth_dao_fork; /// Execution result types pub mod execution_result; /// Executor pub mod executor; -/// Wrapper around revm database and types -pub mod revm_wrap; diff --git a/crates/revm/Cargo.toml b/crates/revm/Cargo.toml new file mode 100644 index 000000000..1f740e83a --- /dev/null +++ b/crates/revm/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "reth-revm" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" +repository = "https://github.com/paradigmxyz/reth" +description = "reth specific revm utilities" + +[dependencies] +# reth +reth-primitives = { path = "../primitives" } +reth-interfaces = { path = "../interfaces" } +reth-provider = { path = "../storage/provider" } + +revm = { version = "3.0.0"} \ No newline at end of file diff --git a/crates/revm/src/compat.rs b/crates/revm/src/compat.rs new file mode 100644 index 000000000..b581b8716 --- /dev/null +++ b/crates/revm/src/compat.rs @@ -0,0 +1,34 @@ +use reth_primitives::{Account, Log as RethLog, H160, H256, KECCAK_EMPTY}; +use revm::primitives::{AccountInfo, Log}; + +/// Check equality between [`reth_primitives::Log`] and [`revm::primitives::Log`] +pub fn is_log_equal(revm_log: &Log, reth_log: &reth_primitives::Log) -> bool { + revm_log.topics.len() == reth_log.topics.len() && + revm_log.address.0 == reth_log.address.0 && + revm_log.data == reth_log.data.0 && + !revm_log + .topics + .iter() + .zip(reth_log.topics.iter()) + .any(|(revm_topic, reth_topic)| revm_topic.0 != reth_topic.0) +} + +/// Into reth primitive [Log] from [revm::primitives::Log]. +pub fn into_reth_log(log: Log) -> RethLog { + RethLog { + address: H160(log.address.0), + topics: log.topics.into_iter().map(|h| H256(h.0)).collect(), + data: log.data.into(), + } +} + +/// Create reth primitive [Account] from [revm::primitives::AccountInfo]. +/// Check if revm bytecode hash is [KECCAK_EMPTY] and put None to reth [Account] +pub fn to_reth_acc(revm_acc: &AccountInfo) -> Account { + let code_hash = revm_acc.code_hash; + Account { + balance: revm_acc.balance, + nonce: revm_acc.nonce, + bytecode_hash: if code_hash == KECCAK_EMPTY { None } else { Some(code_hash) }, + } +} diff --git a/crates/executor/src/config.rs b/crates/revm/src/config.rs similarity index 100% rename from crates/executor/src/config.rs rename to crates/revm/src/config.rs diff --git a/crates/revm/src/database.rs b/crates/revm/src/database.rs new file mode 100644 index 000000000..9740f6475 --- /dev/null +++ b/crates/revm/src/database.rs @@ -0,0 +1,63 @@ +use reth_interfaces::Error; +use reth_primitives::{H160, H256, KECCAK_EMPTY, U256}; +use reth_provider::StateProvider; +use revm::{ + db::{CacheDB, DatabaseRef}, + primitives::{AccountInfo, Bytecode}, +}; + +/// SubState of database. Uses revm internal cache with binding to reth StateProvider trait. +pub type SubState = CacheDB>; + +/// Wrapper around StateProvider that implements revm database trait +pub struct State(pub DB); + +impl State { + /// Create new State with generic StateProvider. + pub fn new(db: DB) -> Self { + Self(db) + } + + /// Return inner state reference + pub fn state(&self) -> &DB { + &self.0 + } + + /// Return inner state mutable reference + pub fn state_mut(&mut self) -> &mut DB { + &mut self.0 + } + + /// Consume State and return inner StateProvider. + pub fn into_inner(self) -> DB { + self.0 + } +} + +impl DatabaseRef for State { + type Error = Error; + + fn basic(&self, address: H160) -> Result, Self::Error> { + Ok(self.0.basic_account(address)?.map(|account| AccountInfo { + balance: account.balance, + nonce: account.nonce, + code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY), + code: None, + })) + } + + fn code_by_hash(&self, code_hash: H256) -> Result { + let bytecode = self.0.bytecode_by_hash(code_hash)?.unwrap_or_default(); + Ok(Bytecode::new_raw(bytecode.0)) + } + + fn storage(&self, address: H160, index: U256) -> Result { + let index = H256(index.to_be_bytes()); + let ret = self.0.storage(address, index)?.unwrap_or_default(); + Ok(ret) + } + + fn block_hash(&self, number: U256) -> Result { + Ok(self.0.block_hash(number)?.unwrap_or_default()) + } +} diff --git a/crates/executor/src/revm_wrap.rs b/crates/revm/src/env.rs similarity index 55% rename from crates/executor/src/revm_wrap.rs rename to crates/revm/src/env.rs index e21a349f9..087eccc8d 100644 --- a/crates/executor/src/revm_wrap.rs +++ b/crates/revm/src/env.rs @@ -1,70 +1,33 @@ -use reth_interfaces::Error; +use crate::config::revm_spec; use reth_primitives::{ - Account, Address, Header, Log as RethLog, Transaction, TransactionKind, TransactionSigned, - TxEip1559, TxEip2930, TxLegacy, H160, H256, KECCAK_EMPTY, U256, -}; -use reth_provider::StateProvider; -use revm::{ - db::{CacheDB, DatabaseRef}, - primitives::{AccountInfo, BlockEnv, Bytecode, Log, TransactTo, TxEnv}, + Address, ChainSpec, Head, Header, Transaction, TransactionKind, TransactionSigned, TxEip1559, + TxEip2930, TxLegacy, U256, }; +use revm::primitives::{AnalysisKind, BlockEnv, CfgEnv, TransactTo, TxEnv}; -/// SubState of database. Uses revm internal cache with binding to reth StateProvider trait. -pub type SubState = CacheDB>; +/// Fill [CfgEnv] fields according to the chain spec and given header +pub fn fill_cfg_env( + cfg_env: &mut CfgEnv, + chain_spec: &ChainSpec, + header: &Header, + total_difficulty: U256, +) { + let spec_id = revm_spec( + chain_spec, + Head { + number: header.number, + timestamp: header.timestamp, + difficulty: header.difficulty, + total_difficulty, + hash: Default::default(), + }, + ); -/// Wrapper around StateProvider that implements revm database trait -pub struct State(pub DB); - -impl State { - /// Create new State with generic StateProvider. - pub fn new(db: DB) -> Self { - Self(db) - } - - /// Return inner state reference - pub fn state(&self) -> &DB { - &self.0 - } - - /// Return inner state mutable reference - pub fn state_mut(&mut self) -> &mut DB { - &mut self.0 - } - - /// Consume State and return inner StateProvider. - pub fn into_inner(self) -> DB { - self.0 - } + cfg_env.chain_id = U256::from(chain_spec.chain().id()); + cfg_env.spec_id = spec_id; + cfg_env.perf_all_precompiles_have_balance = false; + cfg_env.perf_analyse_created_bytecodes = AnalysisKind::Raw; } - -impl DatabaseRef for State { - type Error = Error; - - fn basic(&self, address: H160) -> Result, Self::Error> { - Ok(self.0.basic_account(address)?.map(|account| AccountInfo { - balance: account.balance, - nonce: account.nonce, - code_hash: account.bytecode_hash.unwrap_or(KECCAK_EMPTY), - code: None, - })) - } - - fn code_by_hash(&self, code_hash: H256) -> Result { - let bytecode = self.0.bytecode_by_hash(code_hash)?.unwrap_or_default(); - Ok(Bytecode::new_raw(bytecode.0)) - } - - fn storage(&self, address: H160, index: U256) -> Result { - let index = H256(index.to_be_bytes()); - let ret = self.0.storage(address, index)?.unwrap_or_default(); - Ok(ret) - } - - fn block_hash(&self, number: U256) -> Result { - Ok(self.0.block_hash(number)?.unwrap_or_default()) - } -} - /// Fill block environment from Block. pub fn fill_block_env(block_env: &mut BlockEnv, header: &Header, after_merge: bool) { block_env.number = U256::from(header.number); @@ -179,35 +142,3 @@ pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: } } } - -/// Check equality between [`reth_primitives::Log`] and [`revm::primitives::Log`] -pub fn is_log_equal(revm_log: &Log, reth_log: &reth_primitives::Log) -> bool { - revm_log.topics.len() == reth_log.topics.len() && - revm_log.address.0 == reth_log.address.0 && - revm_log.data == reth_log.data.0 && - !revm_log - .topics - .iter() - .zip(reth_log.topics.iter()) - .any(|(revm_topic, reth_topic)| revm_topic.0 != reth_topic.0) -} - -/// Into reth primitive [Log] from [revm::primitives::Log]. -pub fn into_reth_log(log: Log) -> RethLog { - RethLog { - address: H160(log.address.0), - topics: log.topics.into_iter().map(|h| H256(h.0)).collect(), - data: log.data.into(), - } -} - -/// Create reth primitive [Account] from [revm::primitives::AccountInfo]. -/// Check if revm bytecode hash is [KECCAK_EMPTY] and put None to reth [Account] -pub fn to_reth_acc(revm_acc: &AccountInfo) -> Account { - let code_hash = revm_acc.code_hash; - Account { - balance: revm_acc.balance, - nonce: revm_acc.nonce, - bytecode_hash: if code_hash == KECCAK_EMPTY { None } else { Some(code_hash) }, - } -} diff --git a/crates/revm/src/lib.rs b/crates/revm/src/lib.rs new file mode 100644 index 000000000..642c2890e --- /dev/null +++ b/crates/revm/src/lib.rs @@ -0,0 +1,20 @@ +#![warn(missing_docs, unreachable_pub, unused_crate_dependencies)] +#![deny(unused_must_use, rust_2018_idioms)] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) +))] + +//! revm utils and implementations specific to reth. + +pub mod config; + +/// Contains glue code for integrating reth database into revm's [Database](revm::Database). +pub mod database; + +/// Helpers for configuring revm [Env](revm::primitives::Env) +pub mod env; + +/// Helpers for type compatibility between reth and revm types +mod compat; +pub use compat::*; diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index 25cc579cc..3be1201f4 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -13,6 +13,7 @@ reth-interfaces = { path = "../../interfaces" } reth-provider = { path = "../../storage/provider" } reth-rlp = { path = "../../rlp" } reth-executor = { path = "../../executor" } +reth-revm = { path = "../../revm" } reth-rpc-types = { path = "../rpc-types" } # async diff --git a/crates/rpc/rpc-engine-api/src/engine_api.rs b/crates/rpc/rpc-engine-api/src/engine_api.rs index a635a1f61..beedd9aaf 100644 --- a/crates/rpc/rpc-engine-api/src/engine_api.rs +++ b/crates/rpc/rpc-engine-api/src/engine_api.rs @@ -1,9 +1,6 @@ use crate::{message::EngineApiMessageVersion, EngineApiError, EngineApiMessage, EngineApiResult}; use futures::StreamExt; -use reth_executor::{ - executor, - revm_wrap::{State, SubState}, -}; +use reth_executor::executor; use reth_interfaces::consensus::ForkchoiceState; use reth_primitives::{ proofs::{self, EMPTY_LIST_HASH}, @@ -11,6 +8,7 @@ use reth_primitives::{ H64, U256, }; use reth_provider::{BlockProvider, HeaderProvider, StateProviderFactory}; +use reth_revm::database::{State, SubState}; use reth_rlp::Decodable; use reth_rpc_types::engine::{ ExecutionPayload, ExecutionPayloadBodies, ForkchoiceUpdated, PayloadAttributes, PayloadStatus, diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index 35897baac..d62c0783a 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -19,7 +19,7 @@ reth-provider = { path = "../../storage/provider", features = ["test-utils"] } reth-transaction-pool = { path = "../../transaction-pool", features = ["test-utils"]} reth-network-api = { path = "../../net/network-api", features = ["test-utils"] } reth-rpc-engine-api = { path = "../rpc-engine-api" } -reth-executor = { path = "../../executor" } +reth-revm = { path = "../../revm" } reth-tasks = { path = "../../tasks" } # eth diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index fad0984e4..43d4e4072 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -3,9 +3,9 @@ #![allow(unused)] // TODO rm later use crate::{eth::error::EthResult, EthApi}; -use reth_executor::revm_wrap::{State, SubState}; use reth_primitives::{BlockId, U256}; use reth_provider::{BlockProvider, StateProvider, StateProviderFactory}; +use reth_revm::database::{State, SubState}; use reth_rpc_types::CallRequest; use revm::primitives::{BlockEnv, Env, ResultAndState}; diff --git a/crates/stages/Cargo.toml b/crates/stages/Cargo.toml index 2a13ad9e3..eb14a8e7c 100644 --- a/crates/stages/Cargo.toml +++ b/crates/stages/Cargo.toml @@ -18,6 +18,7 @@ normal = [ reth-primitives = { path = "../primitives" } reth-interfaces = { path = "../interfaces" } reth-executor = { path = "../executor" } +reth-revm = { path = "../revm" } reth-rlp = { path = "../rlp" } reth-db = { path = "../storage/db" } reth-provider = { path = "../storage/provider" } diff --git a/crates/stages/src/stages/execution.rs b/crates/stages/src/stages/execution.rs index 1c268cdde..8da23cbb6 100644 --- a/crates/stages/src/stages/execution.rs +++ b/crates/stages/src/stages/execution.rs @@ -9,13 +9,11 @@ use reth_db::{ tables, transaction::{DbTx, DbTxMut}, }; -use reth_executor::{ - execution_result::AccountChangeSet, - revm_wrap::{State, SubState}, -}; +use reth_executor::execution_result::AccountChangeSet; use reth_interfaces::provider::Error as ProviderError; use reth_primitives::{Address, Block, ChainSpec, Hardfork, StorageEntry, H256, MAINNET, U256}; use reth_provider::{LatestStateProviderRef, Transaction}; +use reth_revm::database::{State, SubState}; use std::fmt::Debug; use tracing::*;