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

158
crates/revm/src/config.rs Normal file
View File

@ -0,0 +1,158 @@
//! Reth block execution/validation configuration and constants
use reth_primitives::{ChainSpec, Hardfork, Head};
/// Two ethereum worth of wei
pub const WEI_2ETH: u128 = 2000000000000000000u128;
/// Three ethereum worth of wei
pub const WEI_3ETH: u128 = 3000000000000000000u128;
/// Five ethereum worth of wei
pub const WEI_5ETH: u128 = 5000000000000000000u128;
/// return revm_spec from spec configuration.
pub fn revm_spec(chain_spec: &ChainSpec, block: Head) -> revm::primitives::SpecId {
if chain_spec.fork(Hardfork::Shanghai).active_at_head(&block) {
revm::primitives::SHANGHAI
} else if chain_spec.fork(Hardfork::Paris).active_at_head(&block) {
revm::primitives::MERGE
} else if chain_spec.fork(Hardfork::London).active_at_head(&block) {
revm::primitives::LONDON
} else if chain_spec.fork(Hardfork::Berlin).active_at_head(&block) {
revm::primitives::BERLIN
} else if chain_spec.fork(Hardfork::Istanbul).active_at_head(&block) {
revm::primitives::ISTANBUL
} else if chain_spec.fork(Hardfork::Petersburg).active_at_head(&block) {
revm::primitives::PETERSBURG
} else if chain_spec.fork(Hardfork::Byzantium).active_at_head(&block) {
revm::primitives::BYZANTIUM
} else if chain_spec.fork(Hardfork::SpuriousDragon).active_at_head(&block) {
revm::primitives::SPURIOUS_DRAGON
} else if chain_spec.fork(Hardfork::Tangerine).active_at_head(&block) {
revm::primitives::TANGERINE
} else if chain_spec.fork(Hardfork::Homestead).active_at_head(&block) {
revm::primitives::HOMESTEAD
} else if chain_spec.fork(Hardfork::Frontier).active_at_head(&block) {
revm::primitives::FRONTIER
} else {
panic!("wrong configuration")
}
}
#[cfg(test)]
mod tests {
use crate::config::revm_spec;
use reth_primitives::{ChainSpecBuilder, Head, MAINNET, U256};
#[test]
fn test_to_revm_spec() {
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().paris_activated().build(), Head::default()),
revm::primitives::MERGE
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().london_activated().build(), Head::default()),
revm::primitives::LONDON
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().berlin_activated().build(), Head::default()),
revm::primitives::BERLIN
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().istanbul_activated().build(), Head::default()),
revm::primitives::ISTANBUL
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().petersburg_activated().build(), Head::default()),
revm::primitives::PETERSBURG
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().byzantium_activated().build(), Head::default()),
revm::primitives::BYZANTIUM
);
assert_eq!(
revm_spec(
&ChainSpecBuilder::mainnet().spurious_dragon_activated().build(),
Head::default()
),
revm::primitives::SPURIOUS_DRAGON
);
assert_eq!(
revm_spec(
&ChainSpecBuilder::mainnet().tangerine_whistle_activated().build(),
Head::default()
),
revm::primitives::TANGERINE
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().homestead_activated().build(), Head::default()),
revm::primitives::HOMESTEAD
);
assert_eq!(
revm_spec(&ChainSpecBuilder::mainnet().frontier_activated().build(), Head::default()),
revm::primitives::FRONTIER
);
}
#[test]
fn test_eth_spec() {
assert_eq!(
revm_spec(
&MAINNET,
Head {
total_difficulty: U256::from(58_750_000_000_000_000_000_010_u128),
difficulty: U256::from(10_u128),
..Default::default()
}
),
revm::primitives::MERGE
);
// TTD trumps the block number
assert_eq!(
revm_spec(
&MAINNET,
Head {
number: 15537394 - 10,
total_difficulty: U256::from(58_750_000_000_000_000_000_010_u128),
difficulty: U256::from(10_u128),
..Default::default()
}
),
revm::primitives::MERGE
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 15537394 - 10, ..Default::default() }),
revm::primitives::LONDON
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 12244000 + 10, ..Default::default() }),
revm::primitives::BERLIN
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 12244000 - 10, ..Default::default() }),
revm::primitives::ISTANBUL
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 7280000 + 10, ..Default::default() }),
revm::primitives::PETERSBURG
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 7280000 - 10, ..Default::default() }),
revm::primitives::BYZANTIUM
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 2675000 + 10, ..Default::default() }),
revm::primitives::SPURIOUS_DRAGON
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 2675000 - 10, ..Default::default() }),
revm::primitives::TANGERINE
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 1150000 + 10, ..Default::default() }),
revm::primitives::HOMESTEAD
);
assert_eq!(
revm_spec(&MAINNET, Head { number: 1150000 - 10, ..Default::default() }),
revm::primitives::FRONTIER
);
}
}

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())
}
}

144
crates/revm/src/env.rs Normal file
View File

@ -0,0 +1,144 @@
use crate::config::revm_spec;
use reth_primitives::{
Address, ChainSpec, Head, Header, Transaction, TransactionKind, TransactionSigned, TxEip1559,
TxEip2930, TxLegacy, U256,
};
use revm::primitives::{AnalysisKind, BlockEnv, CfgEnv, TransactTo, TxEnv};
/// 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(),
},
);
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;
}
/// 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);
block_env.coinbase = header.beneficiary;
block_env.timestamp = U256::from(header.timestamp);
if after_merge {
block_env.prevrandao = Some(header.mix_hash);
block_env.difficulty = U256::ZERO;
} else {
block_env.difficulty = header.difficulty;
block_env.prevrandao = None;
}
block_env.basefee = U256::from(header.base_fee_per_gas.unwrap_or_default());
block_env.gas_limit = U256::from(header.gas_limit);
}
/// Fill transaction environment from Transaction.
pub fn fill_tx_env(tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
tx_env.caller = sender;
match transaction.as_ref() {
Transaction::Legacy(TxLegacy {
nonce,
chain_id,
gas_price,
gas_limit,
to,
value,
input,
}) => {
tx_env.gas_limit = *gas_limit;
tx_env.gas_price = U256::from(*gas_price);
tx_env.gas_priority_fee = None;
tx_env.transact_to = match to {
TransactionKind::Call(to) => TransactTo::Call(*to),
TransactionKind::Create => TransactTo::create(),
};
tx_env.value = U256::from(*value);
tx_env.data = input.0.clone();
tx_env.chain_id = *chain_id;
tx_env.nonce = Some(*nonce);
}
Transaction::Eip2930(TxEip2930 {
nonce,
chain_id,
gas_price,
gas_limit,
to,
value,
input,
access_list,
}) => {
tx_env.gas_limit = *gas_limit;
tx_env.gas_price = U256::from(*gas_price);
tx_env.gas_priority_fee = None;
tx_env.transact_to = match to {
TransactionKind::Call(to) => TransactTo::Call(*to),
TransactionKind::Create => TransactTo::create(),
};
tx_env.value = U256::from(*value);
tx_env.data = input.0.clone();
tx_env.chain_id = Some(*chain_id);
tx_env.nonce = Some(*nonce);
tx_env.access_list = access_list
.0
.iter()
.map(|l| {
(
l.address,
l.storage_keys
.iter()
.map(|k| U256::from_be_bytes(k.to_fixed_bytes()))
.collect(),
)
})
.collect();
}
Transaction::Eip1559(TxEip1559 {
nonce,
chain_id,
gas_limit,
max_fee_per_gas,
max_priority_fee_per_gas,
to,
value,
input,
access_list,
}) => {
tx_env.gas_limit = *gas_limit;
tx_env.gas_price = U256::from(*max_fee_per_gas);
tx_env.gas_priority_fee = Some(U256::from(*max_priority_fee_per_gas));
tx_env.transact_to = match to {
TransactionKind::Call(to) => TransactTo::Call(*to),
TransactionKind::Create => TransactTo::create(),
};
tx_env.value = U256::from(*value);
tx_env.data = input.0.clone();
tx_env.chain_id = Some(*chain_id);
tx_env.nonce = Some(*nonce);
tx_env.access_list = access_list
.0
.iter()
.map(|l| {
(
l.address,
l.storage_keys
.iter()
.map(|k| U256::from_be_bytes(k.to_fixed_bytes()))
.collect(),
)
})
.collect();
}
}
}

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