Skip to content

Commit

Permalink
feat: integrate revm + impl address recovery (#73)
Browse files Browse the repository at this point in the history
**Motivation**

Replace evm code in eftests with an implementation that uses ethrex
types

**Description**

* Implement `execute_tx` using revm
* Move `evm` crate into `core` crate
* Move `Transaction` into its own module
* Implement multiple getters for `Transaction` fields
* Implement address recovery from signed transactions
* Implement conversions between ef tests types and ethrex types
* Add one more ef test
<!-- Link to issues: Resolves #111, Resolves #222 -->

Closes #21
  • Loading branch information
fmoletta authored Jul 2, 2024
1 parent e388466 commit ad45537
Show file tree
Hide file tree
Showing 17 changed files with 875 additions and 327 deletions.
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ members = [
"ethrex",
"crates/consensus",
"crates/core",
"crates/evm",
"crates/net",
"crates/rpc",
"crates/storage",
Expand All @@ -18,7 +17,6 @@ edition = "2021"
[workspace.dependencies]
ethrex-consensus = { path = "./crates/consensus" }
ethrex-core = { path = "./crates/core" }
ethrex-evm = { path = "./crates/evm" }
ethrex-net = { path = "./crates/net" }
ethrex-rpc = { path = "./crates/rpc" }
ethrex-storage = { path = "./crates/storage" }
Expand All @@ -29,6 +27,6 @@ tracing-subscriber = "0.3.0"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
libmdbx = { version = "0.5.0", features = ["orm"] }
bytes = "1.6.0"
tokio = { version = "1.38.0", features = ["full"] }
bytes = { version = "1.6.0", features = ["serde"] }
thiserror = "1.0.61"
7 changes: 6 additions & 1 deletion crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
keccak-hash = "0.10.0"
patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" }
sha3 = "0.10.8"
secp256k1 = { version = "0.29", default-features = false, features = [
"global-context",
"recovery",
] }
revm = { version = "10.0.0", features = ["serde", "std", "serde-json"] }
patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" }
bytes.workspace = true

[dev-dependencies]
Expand Down
86 changes: 86 additions & 0 deletions crates/core/src/evm/execution_result.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use bytes::Bytes;
use ethereum_types::Address;
use revm::primitives::result::Output as RevmOutput;
use revm::primitives::result::SuccessReason as RevmSuccessReason;
use revm::primitives::ExecutionResult as RevmExecutionResult;

pub enum ExecutionResult {
Success {
reason: SuccessReason,
gas_used: u64,
gas_refunded: u64,
output: Output,
},
/// Reverted by `REVERT` opcode
Revert { gas_used: u64, output: Bytes },
/// Reverted for other reasons, spends all gas.
Halt {
reason: String,
/// Halting will spend all the gas, which will be equal to gas_limit.
gas_used: u64,
},
}

pub enum SuccessReason {
Stop,
Return,
SelfDestruct,
EofReturnContract,
}

pub enum Output {
Call(Bytes),
Create(Bytes, Option<Address>),
}

impl From<RevmExecutionResult> for ExecutionResult {
fn from(val: RevmExecutionResult) -> Self {
match val {
RevmExecutionResult::Success {
reason,
gas_used,
gas_refunded,
logs: _,
output,
} => ExecutionResult::Success {
reason: match reason {
RevmSuccessReason::Stop => SuccessReason::Stop,
RevmSuccessReason::Return => SuccessReason::Return,
RevmSuccessReason::SelfDestruct => SuccessReason::SelfDestruct,
RevmSuccessReason::EofReturnContract => SuccessReason::EofReturnContract,
},
gas_used,
gas_refunded,
output: match output {
RevmOutput::Call(bytes) => Output::Call(bytes.0),
RevmOutput::Create(bytes, addr) => Output::Create(
bytes.0,
addr.map(|addr| Address::from_slice(addr.0.as_ref())),
),
},
},
RevmExecutionResult::Revert { gas_used, output } => ExecutionResult::Revert {
gas_used,
output: output.0,
},
RevmExecutionResult::Halt { reason, gas_used } => ExecutionResult::Halt {
reason: format!("{:?}", reason),
gas_used,
},
}
}
}

impl ExecutionResult {
pub fn is_success(&self) -> bool {
matches!(
self,
ExecutionResult::Success {
reason: _,
gas_used: _,
gas_refunded: _,
output: _
}
)
}
}
115 changes: 115 additions & 0 deletions crates/core/src/evm/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
mod execution_result;

use crate::types::TxKind;

use super::{
types::{Account, BlockHeader, Transaction},
Address,
};
use revm::{
inspector_handle_register,
inspectors::TracerEip3155,
primitives::{BlockEnv, Bytecode, TxEnv, U256},
CacheState, Evm,
};
use std::collections::HashMap;
// Rename imported types for clarity
use revm::primitives::AccountInfo as RevmAccountInfo;
use revm::primitives::Address as RevmAddress;
use revm::primitives::TxKind as RevmTxKind;
// Export needed types
pub use execution_result::*;
pub use revm::primitives::SpecId;

pub fn execute_tx(
tx: &Transaction,
header: &BlockHeader,
pre: &HashMap<Address, Account>, // TODO: Modify this type when we have a defined State structure
spec_id: SpecId,
) -> ExecutionResult {
let block_env = block_env(header);
let tx_env = tx_env(tx);
let cache_state = cache_state(pre);
let mut state = revm::db::State::builder()
.with_cached_prestate(cache_state)
.with_bundle_update()
.build();
let mut evm = Evm::builder()
.with_db(&mut state)
.with_block_env(block_env)
.with_tx_env(tx_env)
.with_spec_id(spec_id)
.reset_handler()
.with_external_context(TracerEip3155::new(Box::new(std::io::stderr())).without_summary())
.append_handler_register(inspector_handle_register)
.build();
let tx_result = evm.transact().unwrap();
tx_result.result.into()
}

fn cache_state(pre: &HashMap<Address, Account>) -> CacheState {
let mut cache_state = revm::CacheState::new(false);
for (address, account) in pre {
let acc_info = RevmAccountInfo {
balance: U256::from_limbs(account.info.balance.0),
code_hash: account.info.code_hash.0.into(),
code: Some(Bytecode::new_raw(account.code.clone().into())),
nonce: account.info.nonce,
};

let mut storage = HashMap::new();
for (k, v) in &account.storage {
storage.insert(U256::from_be_bytes(k.0), U256::from_be_bytes(v.0));
}

cache_state.insert_account_with_storage(address.to_fixed_bytes().into(), acc_info, storage);
}
cache_state
}

fn block_env(header: &BlockHeader) -> BlockEnv {
BlockEnv {
number: U256::from(header.number),
coinbase: RevmAddress(header.coinbase.0.into()),
timestamp: U256::from(header.timestamp),
gas_limit: U256::from(header.gas_limit),
basefee: U256::from(header.base_fee_per_gas),
difficulty: U256::from_limbs(header.difficulty.0),
prevrandao: Some(header.prev_randao.as_fixed_bytes().into()),
..Default::default()
}
}

fn tx_env(tx: &Transaction) -> TxEnv {
TxEnv {
caller: RevmAddress(tx.sender().0.into()),
gas_limit: tx.gas_limit(),
gas_price: U256::from(tx.gas_price()),
transact_to: tx.to().into(),
value: U256::from_limbs(tx.value().0),
data: tx.data().clone().into(),
nonce: Some(tx.nonce()),
chain_id: tx.chain_id(),
access_list: tx
.access_list()
.into_iter()
.map(|(addr, list)| {
(
RevmAddress(addr.0.into()),
list.into_iter().map(|a| U256::from_be_bytes(a.0)).collect(),
)
})
.collect(),
gas_priority_fee: tx.max_priority_fee().map(U256::from),
..Default::default()
}
}

impl From<TxKind> for RevmTxKind {
fn from(val: TxKind) -> Self {
match val {
TxKind::Call(address) => RevmTxKind::Call(address.0.into()),
TxKind::Create => RevmTxKind::Create,
}
}
}
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod rlp;
pub use ethereum_types::*;
pub mod evm;
pub mod serde_utils;
pub mod types;
2 changes: 1 addition & 1 deletion crates/core/src/types/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl From<GenesisAccount> for Account {
}
}

fn code_hash(code: &Bytes) -> H256 {
pub fn code_hash(code: &Bytes) -> H256 {
keccak_hash::keccak(code.as_ref())
}

Expand Down
Loading

0 comments on commit ad45537

Please sign in to comment.