Skip to content

Commit ad45537

Browse files
authored
feat: integrate revm + impl address recovery (#73)
**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
1 parent e388466 commit ad45537

File tree

17 files changed

+875
-327
lines changed

17 files changed

+875
-327
lines changed

Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ members = [
33
"ethrex",
44
"crates/consensus",
55
"crates/core",
6-
"crates/evm",
76
"crates/net",
87
"crates/rpc",
98
"crates/storage",
@@ -18,7 +17,6 @@ edition = "2021"
1817
[workspace.dependencies]
1918
ethrex-consensus = { path = "./crates/consensus" }
2019
ethrex-core = { path = "./crates/core" }
21-
ethrex-evm = { path = "./crates/evm" }
2220
ethrex-net = { path = "./crates/net" }
2321
ethrex-rpc = { path = "./crates/rpc" }
2422
ethrex-storage = { path = "./crates/storage" }
@@ -29,6 +27,6 @@ tracing-subscriber = "0.3.0"
2927
serde = { version = "1.0.203", features = ["derive"] }
3028
serde_json = "1.0.117"
3129
libmdbx = { version = "0.5.0", features = ["orm"] }
30+
bytes = "1.6.0"
3231
tokio = { version = "1.38.0", features = ["full"] }
33-
bytes = { version = "1.6.0", features = ["serde"] }
3432
thiserror = "1.0.61"

crates/core/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ serde.workspace = true
1212
serde_json.workspace = true
1313
thiserror.workspace = true
1414
keccak-hash = "0.10.0"
15-
patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" }
1615
sha3 = "0.10.8"
16+
secp256k1 = { version = "0.29", default-features = false, features = [
17+
"global-context",
18+
"recovery",
19+
] }
20+
revm = { version = "10.0.0", features = ["serde", "std", "serde-json"] }
21+
patricia-merkle-tree = { git = "https://github.com/lambdaclass/merkle_patricia_tree.git" }
1722
bytes.workspace = true
1823

1924
[dev-dependencies]
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use bytes::Bytes;
2+
use ethereum_types::Address;
3+
use revm::primitives::result::Output as RevmOutput;
4+
use revm::primitives::result::SuccessReason as RevmSuccessReason;
5+
use revm::primitives::ExecutionResult as RevmExecutionResult;
6+
7+
pub enum ExecutionResult {
8+
Success {
9+
reason: SuccessReason,
10+
gas_used: u64,
11+
gas_refunded: u64,
12+
output: Output,
13+
},
14+
/// Reverted by `REVERT` opcode
15+
Revert { gas_used: u64, output: Bytes },
16+
/// Reverted for other reasons, spends all gas.
17+
Halt {
18+
reason: String,
19+
/// Halting will spend all the gas, which will be equal to gas_limit.
20+
gas_used: u64,
21+
},
22+
}
23+
24+
pub enum SuccessReason {
25+
Stop,
26+
Return,
27+
SelfDestruct,
28+
EofReturnContract,
29+
}
30+
31+
pub enum Output {
32+
Call(Bytes),
33+
Create(Bytes, Option<Address>),
34+
}
35+
36+
impl From<RevmExecutionResult> for ExecutionResult {
37+
fn from(val: RevmExecutionResult) -> Self {
38+
match val {
39+
RevmExecutionResult::Success {
40+
reason,
41+
gas_used,
42+
gas_refunded,
43+
logs: _,
44+
output,
45+
} => ExecutionResult::Success {
46+
reason: match reason {
47+
RevmSuccessReason::Stop => SuccessReason::Stop,
48+
RevmSuccessReason::Return => SuccessReason::Return,
49+
RevmSuccessReason::SelfDestruct => SuccessReason::SelfDestruct,
50+
RevmSuccessReason::EofReturnContract => SuccessReason::EofReturnContract,
51+
},
52+
gas_used,
53+
gas_refunded,
54+
output: match output {
55+
RevmOutput::Call(bytes) => Output::Call(bytes.0),
56+
RevmOutput::Create(bytes, addr) => Output::Create(
57+
bytes.0,
58+
addr.map(|addr| Address::from_slice(addr.0.as_ref())),
59+
),
60+
},
61+
},
62+
RevmExecutionResult::Revert { gas_used, output } => ExecutionResult::Revert {
63+
gas_used,
64+
output: output.0,
65+
},
66+
RevmExecutionResult::Halt { reason, gas_used } => ExecutionResult::Halt {
67+
reason: format!("{:?}", reason),
68+
gas_used,
69+
},
70+
}
71+
}
72+
}
73+
74+
impl ExecutionResult {
75+
pub fn is_success(&self) -> bool {
76+
matches!(
77+
self,
78+
ExecutionResult::Success {
79+
reason: _,
80+
gas_used: _,
81+
gas_refunded: _,
82+
output: _
83+
}
84+
)
85+
}
86+
}

crates/core/src/evm/mod.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
mod execution_result;
2+
3+
use crate::types::TxKind;
4+
5+
use super::{
6+
types::{Account, BlockHeader, Transaction},
7+
Address,
8+
};
9+
use revm::{
10+
inspector_handle_register,
11+
inspectors::TracerEip3155,
12+
primitives::{BlockEnv, Bytecode, TxEnv, U256},
13+
CacheState, Evm,
14+
};
15+
use std::collections::HashMap;
16+
// Rename imported types for clarity
17+
use revm::primitives::AccountInfo as RevmAccountInfo;
18+
use revm::primitives::Address as RevmAddress;
19+
use revm::primitives::TxKind as RevmTxKind;
20+
// Export needed types
21+
pub use execution_result::*;
22+
pub use revm::primitives::SpecId;
23+
24+
pub fn execute_tx(
25+
tx: &Transaction,
26+
header: &BlockHeader,
27+
pre: &HashMap<Address, Account>, // TODO: Modify this type when we have a defined State structure
28+
spec_id: SpecId,
29+
) -> ExecutionResult {
30+
let block_env = block_env(header);
31+
let tx_env = tx_env(tx);
32+
let cache_state = cache_state(pre);
33+
let mut state = revm::db::State::builder()
34+
.with_cached_prestate(cache_state)
35+
.with_bundle_update()
36+
.build();
37+
let mut evm = Evm::builder()
38+
.with_db(&mut state)
39+
.with_block_env(block_env)
40+
.with_tx_env(tx_env)
41+
.with_spec_id(spec_id)
42+
.reset_handler()
43+
.with_external_context(TracerEip3155::new(Box::new(std::io::stderr())).without_summary())
44+
.append_handler_register(inspector_handle_register)
45+
.build();
46+
let tx_result = evm.transact().unwrap();
47+
tx_result.result.into()
48+
}
49+
50+
fn cache_state(pre: &HashMap<Address, Account>) -> CacheState {
51+
let mut cache_state = revm::CacheState::new(false);
52+
for (address, account) in pre {
53+
let acc_info = RevmAccountInfo {
54+
balance: U256::from_limbs(account.info.balance.0),
55+
code_hash: account.info.code_hash.0.into(),
56+
code: Some(Bytecode::new_raw(account.code.clone().into())),
57+
nonce: account.info.nonce,
58+
};
59+
60+
let mut storage = HashMap::new();
61+
for (k, v) in &account.storage {
62+
storage.insert(U256::from_be_bytes(k.0), U256::from_be_bytes(v.0));
63+
}
64+
65+
cache_state.insert_account_with_storage(address.to_fixed_bytes().into(), acc_info, storage);
66+
}
67+
cache_state
68+
}
69+
70+
fn block_env(header: &BlockHeader) -> BlockEnv {
71+
BlockEnv {
72+
number: U256::from(header.number),
73+
coinbase: RevmAddress(header.coinbase.0.into()),
74+
timestamp: U256::from(header.timestamp),
75+
gas_limit: U256::from(header.gas_limit),
76+
basefee: U256::from(header.base_fee_per_gas),
77+
difficulty: U256::from_limbs(header.difficulty.0),
78+
prevrandao: Some(header.prev_randao.as_fixed_bytes().into()),
79+
..Default::default()
80+
}
81+
}
82+
83+
fn tx_env(tx: &Transaction) -> TxEnv {
84+
TxEnv {
85+
caller: RevmAddress(tx.sender().0.into()),
86+
gas_limit: tx.gas_limit(),
87+
gas_price: U256::from(tx.gas_price()),
88+
transact_to: tx.to().into(),
89+
value: U256::from_limbs(tx.value().0),
90+
data: tx.data().clone().into(),
91+
nonce: Some(tx.nonce()),
92+
chain_id: tx.chain_id(),
93+
access_list: tx
94+
.access_list()
95+
.into_iter()
96+
.map(|(addr, list)| {
97+
(
98+
RevmAddress(addr.0.into()),
99+
list.into_iter().map(|a| U256::from_be_bytes(a.0)).collect(),
100+
)
101+
})
102+
.collect(),
103+
gas_priority_fee: tx.max_priority_fee().map(U256::from),
104+
..Default::default()
105+
}
106+
}
107+
108+
impl From<TxKind> for RevmTxKind {
109+
fn from(val: TxKind) -> Self {
110+
match val {
111+
TxKind::Call(address) => RevmTxKind::Call(address.0.into()),
112+
TxKind::Create => RevmTxKind::Create,
113+
}
114+
}
115+
}

crates/core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod rlp;
22
pub use ethereum_types::*;
3+
pub mod evm;
34
pub mod serde_utils;
45
pub mod types;

crates/core/src/types/account.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl From<GenesisAccount> for Account {
3636
}
3737
}
3838

39-
fn code_hash(code: &Bytes) -> H256 {
39+
pub fn code_hash(code: &Bytes) -> H256 {
4040
keccak_hash::keccak(code.as_ref())
4141
}
4242

0 commit comments

Comments
 (0)