feat: add reth-revm crate (#1526)

This commit is contained in:
Matthias Seitz
2023-02-23 14:16:42 +01:00
committed by GitHub
parent a3067d9067
commit 8299ca6fd6
17 changed files with 200 additions and 140 deletions

15
Cargo.lock generated
View File

@ -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",

View File

@ -16,6 +16,7 @@ members = [
"crates/net/network",
"crates/net/downloaders",
"crates/primitives",
"crates/revm",
"crates/rlp",
"crates/rlp/rlp-derive",
"crates/rpc/ipc",

View File

@ -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" }

View File

@ -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<ExecutionResult, Error> {
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<DB: StateProvider>(
#[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};

View File

@ -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;

15
crates/revm/Cargo.toml Normal file
View File

@ -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"}

34
crates/revm/src/compat.rs Normal file
View File

@ -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) },
}
}

View File

@ -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<DB> = CacheDB<State<DB>>;
/// Wrapper around StateProvider that implements revm database trait
pub struct State<DB: StateProvider>(pub DB);
impl<DB: StateProvider> State<DB> {
/// 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<DB: StateProvider> DatabaseRef for State<DB> {
type Error = Error;
fn basic(&self, address: H160) -> Result<Option<AccountInfo>, 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<Bytecode, Self::Error> {
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<U256, Self::Error> {
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<H256, Self::Error> {
Ok(self.0.block_hash(number)?.unwrap_or_default())
}
}

View File

@ -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<DB> = CacheDB<State<DB>>;
/// 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<DB: StateProvider>(pub DB);
impl<DB: StateProvider> State<DB> {
/// 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<DB: StateProvider> DatabaseRef for State<DB> {
type Error = Error;
fn basic(&self, address: H160) -> Result<Option<AccountInfo>, 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<Bytecode, Self::Error> {
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<U256, Self::Error> {
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<H256, Self::Error> {
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) },
}
}

20
crates/revm/src/lib.rs Normal file
View File

@ -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::*;

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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};

View File

@ -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" }

View File

@ -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::*;