helios/execution/src/evm.rs

305 lines
10 KiB
Rust
Raw Normal View History

2022-11-02 19:26:15 +00:00
use std::{cmp, collections::HashMap, fmt::Display, str::FromStr, sync::Arc, thread};
2022-08-24 01:33:48 +00:00
use bytes::Bytes;
use ethers::{
abi::ethereum_types::BigEndianHash,
prelude::{Address, H160, H256, U256},
types::transaction::eip2930::AccessListItem,
};
2022-08-24 01:33:48 +00:00
use eyre::Result;
use futures::future::join_all;
use log::trace;
2022-08-26 01:18:47 +00:00
use revm::{AccountInfo, Bytecode, Database, Env, TransactOut, TransactTo, EVM};
use tokio::runtime::Runtime;
2022-08-24 01:33:48 +00:00
2022-08-29 17:31:17 +00:00
use consensus::types::ExecutionPayload;
use crate::{
rpc::ExecutionRpc,
types::{Account, CallOpts},
};
2022-08-24 01:33:48 +00:00
use super::ExecutionClient;
pub struct Evm<R: ExecutionRpc> {
evm: EVM<ProofDB<R>>,
chain_id: u64,
2022-08-24 01:33:48 +00:00
}
impl<R: ExecutionRpc> Evm<R> {
2022-11-02 19:26:15 +00:00
pub fn new(
execution: Arc<ExecutionClient<R>>,
payload: ExecutionPayload,
chain_id: u64,
) -> Self {
let mut evm: EVM<ProofDB<R>> = EVM::new();
2022-08-24 01:33:48 +00:00
let db = ProofDB::new(execution, payload);
evm.database(db);
Evm { evm, chain_id }
2022-08-24 01:33:48 +00:00
}
2022-11-02 19:26:15 +00:00
pub async fn call(&mut self, opts: &CallOpts) -> Result<Vec<u8>> {
let account_map = self.batch_fetch_accounts(opts).await?;
self.evm.db.as_mut().unwrap().set_accounts(account_map);
self.evm.env = self.get_env(opts);
2022-11-02 19:26:15 +00:00
let tx = self.evm.transact();
let output = tx.1;
match tx.0 {
revm::Return::Revert => Err(eyre::eyre!("execution reverted")),
revm::Return::OutOfGas => Err(eyre::eyre!("execution reverted: out of gas")),
revm::Return::OutOfFund => Err(eyre::eyre!("not enough funds")),
revm::Return::CallTooDeep => Err(eyre::eyre!("execution reverted: call too deep")),
revm::Return::InvalidJump => {
Err(eyre::eyre!("execution reverted: invalid jump destination"))
}
revm::Return::InvalidOpcode => Err(eyre::eyre!("execution reverted: invalid opcode")),
revm::Return::LackOfFundForGasLimit => Err(eyre::eyre!("not enough funds")),
revm::Return::GasPriceLessThenBasefee => Err(eyre::eyre!("gas price too low")),
_ => {
if let Some(err) = &self.evm.db.as_ref().unwrap().error {
return Err(eyre::eyre!(err.clone()));
}
match output {
TransactOut::None => Err(eyre::eyre!("Invalid Call")),
TransactOut::Create(..) => Err(eyre::eyre!("Invalid Call")),
TransactOut::Call(bytes) => Ok(bytes.to_vec()),
}
}
2022-08-24 01:33:48 +00:00
}
}
2022-08-27 20:43:27 +00:00
2022-11-02 19:26:15 +00:00
pub async fn estimate_gas(&mut self, opts: &CallOpts) -> Result<u64> {
let account_map = self.batch_fetch_accounts(opts).await?;
self.evm.db.as_mut().unwrap().set_accounts(account_map);
self.evm.env = self.get_env(opts);
2022-11-02 19:26:15 +00:00
let tx = self.evm.transact();
let gas = tx.2;
match tx.0 {
revm::Return::Revert => Err(eyre::eyre!("execution reverted")),
revm::Return::OutOfGas => Err(eyre::eyre!("execution reverted: out of gas")),
revm::Return::OutOfFund => Err(eyre::eyre!("not enough funds")),
revm::Return::CallTooDeep => Err(eyre::eyre!("execution reverted: call too deep")),
revm::Return::InvalidJump => {
Err(eyre::eyre!("execution reverted: invalid jump destination"))
}
revm::Return::InvalidOpcode => Err(eyre::eyre!("execution reverted: invalid opcode")),
revm::Return::LackOfFundForGasLimit => Err(eyre::eyre!("not enough funds")),
revm::Return::GasPriceLessThenBasefee => Err(eyre::eyre!("gas price too low")),
_ => {
if let Some(err) = &self.evm.db.as_ref().unwrap().error {
return Err(eyre::eyre!(err.clone()));
}
// overestimate to avoid out of gas reverts
let gas_scaled = (1.10 * gas as f64) as u64;
Ok(gas_scaled)
}
}
}
2022-11-02 19:26:15 +00:00
async fn batch_fetch_accounts(&self, opts: &CallOpts) -> Result<HashMap<Address, Account>> {
let db = self.evm.db.as_ref().unwrap();
let rpc = db.execution.rpc.clone();
let payload = db.payload.clone();
let execution = db.execution.clone();
let block = db.payload.block_number;
let opts_moved = CallOpts {
from: opts.from,
to: opts.to,
value: opts.value,
data: opts.data.clone(),
gas: opts.gas,
gas_price: opts.gas_price,
};
let block_moved = block.clone();
2022-11-02 19:26:15 +00:00
let mut list = rpc.create_access_list(&opts_moved, block_moved).await?.0;
let from_access_entry = AccessListItem {
address: opts_moved.from.unwrap_or_default(),
storage_keys: Vec::default(),
};
let to_access_entry = AccessListItem {
address: opts_moved.to,
storage_keys: Vec::default(),
};
let producer_account = AccessListItem {
address: Address::from_slice(&payload.fee_recipient),
storage_keys: Vec::default(),
};
list.push(from_access_entry);
list.push(to_access_entry);
list.push(producer_account);
let mut accounts = Vec::new();
let batch_size = 20;
for i in (0..list.len()).step_by(batch_size) {
let end = cmp::min(i + batch_size, list.len());
let chunk = &list[i..end];
let account_chunk_futs = chunk.iter().map(|account| {
let addr_fut = futures::future::ready(account.address);
let account_fut = execution.get_account(
&account.address,
Some(account.storage_keys.as_slice()),
&payload,
);
async move { (addr_fut.await, account_fut.await) }
});
2022-11-02 19:26:15 +00:00
let mut account_chunk = join_all(account_chunk_futs).await;
accounts.append(&mut account_chunk);
}
let mut account_map = HashMap::new();
accounts.iter().for_each(|account| {
let addr = account.0;
2022-10-05 20:06:15 +00:00
if let Ok(account) = &account.1 {
account_map.insert(addr, account.clone());
}
});
Ok(account_map)
}
fn get_env(&self, opts: &CallOpts) -> Env {
2022-08-27 20:43:27 +00:00
let mut env = Env::default();
let payload = &self.evm.db.as_ref().unwrap().payload;
env.tx.transact_to = TransactTo::Call(opts.to);
env.tx.caller = opts.from.unwrap_or(Address::zero());
env.tx.value = opts.value.unwrap_or(U256::from(0));
env.tx.data = Bytes::from(opts.data.clone().unwrap_or(vec![]));
env.tx.gas_limit = opts.gas.map(|v| v.as_u64()).unwrap_or(u64::MAX);
env.tx.gas_price = opts.gas_price.unwrap_or(U256::zero());
2022-08-27 20:43:27 +00:00
env.block.number = U256::from(payload.block_number);
env.block.coinbase = Address::from_slice(&payload.fee_recipient);
env.block.timestamp = U256::from(payload.timestamp);
env.block.difficulty = U256::from_little_endian(&payload.prev_randao);
2022-08-27 20:43:27 +00:00
env.cfg.chain_id = self.chain_id.into();
2022-08-27 20:43:27 +00:00
env
2022-08-27 20:43:27 +00:00
}
2022-08-24 01:33:48 +00:00
}
struct ProofDB<R: ExecutionRpc> {
2022-11-02 19:26:15 +00:00
execution: Arc<ExecutionClient<R>>,
2022-08-24 01:33:48 +00:00
payload: ExecutionPayload,
accounts: HashMap<Address, Account>,
2022-08-24 01:33:48 +00:00
error: Option<String>,
}
impl<R: ExecutionRpc> ProofDB<R> {
2022-11-02 19:26:15 +00:00
pub fn new(execution: Arc<ExecutionClient<R>>, payload: ExecutionPayload) -> Self {
2022-08-24 01:33:48 +00:00
ProofDB {
execution,
payload,
accounts: HashMap::new(),
2022-08-24 01:33:48 +00:00
error: None,
}
}
2022-08-24 16:28:43 +00:00
pub fn safe_unwrap<T: Default, E: Display>(&mut self, res: Result<T, E>) -> T {
2022-08-24 16:28:43 +00:00
match res {
Ok(value) => value,
Err(err) => {
self.error = Some(err.to_string());
T::default()
2022-08-26 01:18:47 +00:00
}
2022-08-24 16:28:43 +00:00
}
}
2022-08-24 01:33:48 +00:00
pub fn set_accounts(&mut self, accounts: HashMap<Address, Account>) {
self.accounts = accounts;
}
2022-08-24 01:33:48 +00:00
fn get_account(&mut self, address: Address, slots: &[H256]) -> Account {
2022-08-26 01:18:47 +00:00
let execution = self.execution.clone();
let addr = address.clone();
let payload = self.payload.clone();
let slots = slots.to_owned();
2022-08-26 01:18:47 +00:00
let handle = thread::spawn(move || {
let account_fut = execution.get_account(&addr, Some(&slots), &payload);
2022-08-26 01:18:47 +00:00
let runtime = Runtime::new()?;
runtime.block_on(account_fut)
});
self.safe_unwrap(handle.join().unwrap())
}
}
2022-08-24 01:33:48 +00:00
impl<R: ExecutionRpc> Database for ProofDB<R> {
fn basic(&mut self, address: H160) -> AccountInfo {
if is_precompile(&address) {
return AccountInfo::default();
}
2022-08-26 01:18:47 +00:00
trace!(
"fetch basic evm state for addess=0x{}",
hex::encode(address.as_bytes())
);
2022-08-26 01:18:47 +00:00
let account = match self.accounts.get(&address) {
Some(account) => account.clone(),
None => self.get_account(address, &[]),
};
2022-08-24 01:33:48 +00:00
let bytecode = Bytecode::new_raw(Bytes::from(account.code.clone()));
AccountInfo::new(account.balance, account.nonce, bytecode)
2022-08-24 01:33:48 +00:00
}
fn block_hash(&mut self, _number: U256) -> H256 {
H256::default()
}
fn storage(&mut self, address: H160, slot: U256) -> U256 {
trace!(
"fetch evm state for address=0x{}, slot={}",
hex::encode(address.as_bytes()),
slot
);
2022-08-26 01:18:47 +00:00
let slot = H256::from_uint(&slot);
2022-08-24 01:33:48 +00:00
match self.accounts.get(&address) {
Some(account) => match account.slots.get(&slot) {
Some(slot) => slot.clone(),
None => self
.get_account(address, &[slot])
.slots
.get(&slot)
.unwrap()
.clone(),
},
None => self
.get_account(address, &[slot])
.slots
.get(&slot)
.unwrap()
.clone(),
}
2022-08-24 01:33:48 +00:00
}
fn code_by_hash(&mut self, _code_hash: H256) -> Bytecode {
panic!("should never be called");
}
}
fn is_precompile(address: &Address) -> bool {
address.le(&Address::from_str("0x0000000000000000000000000000000000000009").unwrap())
&& address.gt(&Address::zero())
2022-08-24 01:33:48 +00:00
}