2022-11-04 20:37:30 +00:00
|
|
|
use std::{
|
|
|
|
collections::{BTreeMap, HashMap},
|
|
|
|
str::FromStr,
|
|
|
|
sync::Arc,
|
|
|
|
thread,
|
|
|
|
};
|
2022-08-24 01:33:48 +00:00
|
|
|
|
|
|
|
use bytes::Bytes;
|
2022-11-04 20:37:30 +00:00
|
|
|
use common::{errors::BlockNotFoundError, types::BlockTag};
|
2022-09-06 17:57:47 +00:00
|
|
|
use ethers::{
|
|
|
|
abi::ethereum_types::BigEndianHash,
|
|
|
|
prelude::{Address, H160, H256, U256},
|
2022-09-23 23:40:01 +00:00
|
|
|
types::transaction::eip2930::AccessListItem,
|
2022-09-06 17:57:47 +00:00
|
|
|
};
|
2022-11-03 23:36:14 +00:00
|
|
|
use eyre::{Report, Result};
|
2022-09-22 19:40:06 +00:00
|
|
|
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;
|
|
|
|
|
2022-09-22 19:40:06 +00:00
|
|
|
use crate::{
|
2022-11-12 00:45:08 +00:00
|
|
|
constants::PARALLEL_QUERY_BATCH_SIZE,
|
2022-11-12 00:41:37 +00:00
|
|
|
errors::EvmError,
|
2022-11-02 03:52:28 +00:00
|
|
|
rpc::ExecutionRpc,
|
2022-09-22 19:40:06 +00:00
|
|
|
types::{Account, CallOpts},
|
|
|
|
};
|
2022-09-01 19:58:45 +00:00
|
|
|
|
2022-08-24 01:33:48 +00:00
|
|
|
use super::ExecutionClient;
|
|
|
|
|
2022-11-04 20:37:30 +00:00
|
|
|
pub struct Evm<'a, R: ExecutionRpc> {
|
|
|
|
evm: EVM<ProofDB<'a, R>>,
|
2022-09-02 19:29:51 +00:00
|
|
|
chain_id: u64,
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-04 20:37:30 +00:00
|
|
|
impl<'a, R: ExecutionRpc> Evm<'a, R> {
|
2022-11-02 19:26:15 +00:00
|
|
|
pub fn new(
|
|
|
|
execution: Arc<ExecutionClient<R>>,
|
2022-11-04 20:37:30 +00:00
|
|
|
current_payload: &'a ExecutionPayload,
|
|
|
|
payloads: &'a BTreeMap<u64, ExecutionPayload>,
|
2022-11-02 19:26:15 +00:00
|
|
|
chain_id: u64,
|
|
|
|
) -> Self {
|
2022-09-08 21:46:48 +00:00
|
|
|
let mut evm: EVM<ProofDB<R>> = EVM::new();
|
2022-11-04 20:37:30 +00:00
|
|
|
let db = ProofDB::new(execution, current_payload, payloads);
|
2022-08-24 01:33:48 +00:00
|
|
|
evm.database(db);
|
|
|
|
|
2022-09-02 19:29:51 +00:00
|
|
|
Evm { evm, chain_id }
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-12 00:41:37 +00:00
|
|
|
pub async fn call(&mut self, opts: &CallOpts) -> Result<Vec<u8>, EvmError> {
|
2022-11-02 19:26:15 +00:00
|
|
|
let account_map = self.batch_fetch_accounts(opts).await?;
|
2022-09-22 19:40:06 +00:00
|
|
|
self.evm.db.as_mut().unwrap().set_accounts(account_map);
|
|
|
|
|
2022-09-02 19:29:51 +00:00
|
|
|
self.evm.env = self.get_env(opts);
|
2022-11-03 23:36:14 +00:00
|
|
|
let tx = self.evm.transact().0;
|
2022-11-02 19:26:15 +00:00
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
match tx.exit_reason {
|
2022-11-12 00:41:37 +00:00
|
|
|
revm::Return::Revert => match tx.out {
|
|
|
|
TransactOut::Call(bytes) => Err(EvmError::Revert(Some(bytes))),
|
|
|
|
_ => Err(EvmError::Revert(None)),
|
|
|
|
},
|
2022-12-22 00:11:43 +00:00
|
|
|
revm::Return::Return | revm::Return::Stop => {
|
2022-11-02 19:26:15 +00:00
|
|
|
if let Some(err) = &self.evm.db.as_ref().unwrap().error {
|
2022-11-12 00:41:37 +00:00
|
|
|
return Err(EvmError::Generic(err.clone()));
|
2022-11-02 19:26:15 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
match tx.out {
|
2022-11-12 00:41:37 +00:00
|
|
|
TransactOut::None => Err(EvmError::Generic("Invalid Call".to_string())),
|
|
|
|
TransactOut::Create(..) => Err(EvmError::Generic("Invalid Call".to_string())),
|
2022-11-02 19:26:15 +00:00
|
|
|
TransactOut::Call(bytes) => Ok(bytes.to_vec()),
|
|
|
|
}
|
|
|
|
}
|
2022-11-12 00:41:37 +00:00
|
|
|
_ => Err(EvmError::Revm(tx.exit_reason)),
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-27 20:43:27 +00:00
|
|
|
|
2022-11-12 00:41:37 +00:00
|
|
|
pub async fn estimate_gas(&mut self, opts: &CallOpts) -> Result<u64, EvmError> {
|
2022-11-02 19:26:15 +00:00
|
|
|
let account_map = self.batch_fetch_accounts(opts).await?;
|
2022-09-22 19:40:06 +00:00
|
|
|
self.evm.db.as_mut().unwrap().set_accounts(account_map);
|
|
|
|
|
2022-09-02 19:29:51 +00:00
|
|
|
self.evm.env = self.get_env(opts);
|
2022-11-03 23:36:14 +00:00
|
|
|
let tx = self.evm.transact().0;
|
|
|
|
let gas = tx.gas_used;
|
2022-11-02 19:26:15 +00:00
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
match tx.exit_reason {
|
2022-11-12 00:41:37 +00:00
|
|
|
revm::Return::Revert => match tx.out {
|
|
|
|
TransactOut::Call(bytes) => Err(EvmError::Revert(Some(bytes))),
|
|
|
|
_ => Err(EvmError::Revert(None)),
|
|
|
|
},
|
2022-12-22 00:11:43 +00:00
|
|
|
revm::Return::Return | revm::Return::Stop => {
|
2022-11-02 19:26:15 +00:00
|
|
|
if let Some(err) = &self.evm.db.as_ref().unwrap().error {
|
2022-11-12 00:41:37 +00:00
|
|
|
return Err(EvmError::Generic(err.clone()));
|
2022-11-02 19:26:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// overestimate to avoid out of gas reverts
|
|
|
|
let gas_scaled = (1.10 * gas as f64) as u64;
|
|
|
|
Ok(gas_scaled)
|
|
|
|
}
|
2022-11-12 00:41:37 +00:00
|
|
|
_ => Err(EvmError::Revm(tx.exit_reason)),
|
2022-09-02 19:29:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-12 00:41:37 +00:00
|
|
|
async fn batch_fetch_accounts(
|
|
|
|
&self,
|
|
|
|
opts: &CallOpts,
|
|
|
|
) -> Result<HashMap<Address, Account>, EvmError> {
|
2022-09-22 19:40:06 +00:00
|
|
|
let db = self.evm.db.as_ref().unwrap();
|
|
|
|
let rpc = db.execution.rpc.clone();
|
2022-11-04 20:37:30 +00:00
|
|
|
let payload = db.current_payload.clone();
|
2022-09-22 19:40:06 +00:00
|
|
|
let execution = db.execution.clone();
|
2022-11-04 20:37:30 +00:00
|
|
|
let block = db.current_payload.block_number;
|
2022-09-22 19:40:06 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2022-11-12 00:41:37 +00:00
|
|
|
let mut list = rpc
|
2022-11-30 01:31:25 +00:00
|
|
|
.create_access_list(&opts_moved, block)
|
2022-11-12 00:41:37 +00:00
|
|
|
.await
|
|
|
|
.map_err(EvmError::RpcError)?
|
|
|
|
.0;
|
2022-11-02 19:26:15 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2022-11-12 00:45:08 +00:00
|
|
|
let mut account_map = HashMap::new();
|
|
|
|
for chunk in list.chunks(PARALLEL_QUERY_BATCH_SIZE) {
|
2022-11-30 01:31:25 +00:00
|
|
|
let account_chunk_futs = chunk.iter().map(|account| {
|
2022-09-22 19:40:06 +00:00
|
|
|
let account_fut = execution.get_account(
|
|
|
|
&account.address,
|
|
|
|
Some(account.storage_keys.as_slice()),
|
|
|
|
&payload,
|
|
|
|
);
|
2022-11-12 00:45:08 +00:00
|
|
|
async move { (account.address, account_fut.await) }
|
2022-09-22 19:40:06 +00:00
|
|
|
});
|
|
|
|
|
2022-11-12 00:45:08 +00:00
|
|
|
let account_chunk = join_all(account_chunk_futs).await;
|
2022-09-22 19:40:06 +00:00
|
|
|
|
2022-11-12 00:45:08 +00:00
|
|
|
account_chunk
|
|
|
|
.into_iter()
|
|
|
|
.filter(|i| i.1.is_ok())
|
|
|
|
.for_each(|(key, value)| {
|
|
|
|
account_map.insert(key, value.ok().unwrap());
|
|
|
|
});
|
|
|
|
}
|
2022-09-22 19:40:06 +00:00
|
|
|
|
2022-09-24 00:37:28 +00:00
|
|
|
Ok(account_map)
|
2022-09-22 19:40:06 +00:00
|
|
|
}
|
|
|
|
|
2022-09-02 19:29:51 +00:00
|
|
|
fn get_env(&self, opts: &CallOpts) -> Env {
|
2022-08-27 20:43:27 +00:00
|
|
|
let mut env = Env::default();
|
2022-11-04 20:37:30 +00:00
|
|
|
let payload = &self.evm.db.as_ref().unwrap().current_payload;
|
2022-09-02 19:29:51 +00:00
|
|
|
|
2022-09-01 19:58:45 +00:00
|
|
|
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![]));
|
2022-09-02 19:29:51 +00:00
|
|
|
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
|
|
|
|
2022-09-02 19:29:51 +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
|
|
|
|
2022-09-02 19:29:51 +00:00
|
|
|
env.cfg.chain_id = self.chain_id.into();
|
2022-08-27 20:43:27 +00:00
|
|
|
|
2022-09-02 19:29:51 +00:00
|
|
|
env
|
2022-08-27 20:43:27 +00:00
|
|
|
}
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-04 20:37:30 +00:00
|
|
|
struct ProofDB<'a, R: ExecutionRpc> {
|
2022-11-02 19:26:15 +00:00
|
|
|
execution: Arc<ExecutionClient<R>>,
|
2022-11-04 20:37:30 +00:00
|
|
|
current_payload: &'a ExecutionPayload,
|
|
|
|
payloads: &'a BTreeMap<u64, ExecutionPayload>,
|
2022-09-22 19:40:06 +00:00
|
|
|
accounts: HashMap<Address, Account>,
|
2022-08-24 01:33:48 +00:00
|
|
|
error: Option<String>,
|
|
|
|
}
|
|
|
|
|
2022-11-04 20:37:30 +00:00
|
|
|
impl<'a, R: ExecutionRpc> ProofDB<'a, R> {
|
|
|
|
pub fn new(
|
|
|
|
execution: Arc<ExecutionClient<R>>,
|
|
|
|
current_payload: &'a ExecutionPayload,
|
|
|
|
payloads: &'a BTreeMap<u64, ExecutionPayload>,
|
|
|
|
) -> Self {
|
2022-08-24 01:33:48 +00:00
|
|
|
ProofDB {
|
|
|
|
execution,
|
2022-11-04 20:37:30 +00:00
|
|
|
current_payload,
|
|
|
|
payloads,
|
2022-09-22 19:40:06 +00:00
|
|
|
accounts: HashMap::new(),
|
2022-08-24 01:33:48 +00:00
|
|
|
error: None,
|
|
|
|
}
|
|
|
|
}
|
2022-08-24 16:28:43 +00:00
|
|
|
|
2022-09-22 19:40:06 +00:00
|
|
|
pub fn set_accounts(&mut self, accounts: HashMap<Address, Account>) {
|
|
|
|
self.accounts = accounts;
|
|
|
|
}
|
2022-08-24 01:33:48 +00:00
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
fn get_account(&mut self, address: Address, slots: &[H256]) -> Result<Account> {
|
2022-08-26 01:18:47 +00:00
|
|
|
let execution = self.execution.clone();
|
2022-11-04 20:37:30 +00:00
|
|
|
let payload = self.current_payload.clone();
|
2022-09-22 19:40:06 +00:00
|
|
|
let slots = slots.to_owned();
|
2022-08-26 01:18:47 +00:00
|
|
|
|
|
|
|
let handle = thread::spawn(move || {
|
2022-11-30 01:31:25 +00:00
|
|
|
let account_fut = execution.get_account(&address, Some(&slots), &payload);
|
2022-08-26 01:18:47 +00:00
|
|
|
let runtime = Runtime::new()?;
|
|
|
|
runtime.block_on(account_fut)
|
|
|
|
});
|
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
handle.join().unwrap()
|
2022-09-22 19:40:06 +00:00
|
|
|
}
|
|
|
|
}
|
2022-08-24 01:33:48 +00:00
|
|
|
|
2022-11-04 20:37:30 +00:00
|
|
|
impl<'a, R: ExecutionRpc> Database for ProofDB<'a, R> {
|
2022-11-03 23:36:14 +00:00
|
|
|
type Error = Report;
|
|
|
|
|
|
|
|
fn basic(&mut self, address: H160) -> Result<Option<AccountInfo>, Report> {
|
2022-09-22 19:40:06 +00:00
|
|
|
if is_precompile(&address) {
|
2022-11-03 23:36:14 +00:00
|
|
|
return Ok(Some(AccountInfo::default()));
|
2022-09-22 19:40:06 +00:00
|
|
|
}
|
2022-08-26 01:18:47 +00:00
|
|
|
|
2022-09-22 19:40:06 +00:00
|
|
|
trace!(
|
2023-01-10 20:47:58 +00:00
|
|
|
"fetch basic evm state for address=0x{}",
|
2022-09-22 19:40:06 +00:00
|
|
|
hex::encode(address.as_bytes())
|
|
|
|
);
|
2022-08-26 01:18:47 +00:00
|
|
|
|
2022-09-22 19:40:06 +00:00
|
|
|
let account = match self.accounts.get(&address) {
|
|
|
|
Some(account) => account.clone(),
|
2022-11-03 23:36:14 +00:00
|
|
|
None => self.get_account(address, &[])?,
|
2022-09-22 19:40:06 +00:00
|
|
|
};
|
2022-08-24 01:33:48 +00:00
|
|
|
|
2022-09-22 19:40:06 +00:00
|
|
|
let bytecode = Bytecode::new_raw(Bytes::from(account.code.clone()));
|
2022-11-03 23:36:14 +00:00
|
|
|
Ok(Some(AccountInfo::new(
|
|
|
|
account.balance,
|
|
|
|
account.nonce,
|
|
|
|
bytecode,
|
|
|
|
)))
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-04 20:37:30 +00:00
|
|
|
fn block_hash(&mut self, number: U256) -> Result<H256, Report> {
|
|
|
|
let number = number.as_u64();
|
|
|
|
let payload = self
|
|
|
|
.payloads
|
|
|
|
.get(&number)
|
|
|
|
.ok_or(BlockNotFoundError::new(BlockTag::Number(number)))?;
|
|
|
|
Ok(H256::from_slice(&payload.block_hash))
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
fn storage(&mut self, address: H160, slot: U256) -> Result<U256, Report> {
|
2022-09-22 19:40:06 +00:00
|
|
|
trace!(
|
|
|
|
"fetch evm state for address=0x{}, slot={}",
|
|
|
|
hex::encode(address.as_bytes()),
|
|
|
|
slot
|
|
|
|
);
|
2022-08-26 01:18:47 +00:00
|
|
|
|
2022-09-22 19:40:06 +00:00
|
|
|
let slot = H256::from_uint(&slot);
|
2022-08-24 01:33:48 +00:00
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
Ok(match self.accounts.get(&address) {
|
2022-09-22 19:40:06 +00:00
|
|
|
Some(account) => match account.slots.get(&slot) {
|
2022-11-30 01:31:25 +00:00
|
|
|
Some(slot) => *slot,
|
|
|
|
None => *self
|
2022-11-03 23:36:14 +00:00
|
|
|
.get_account(address, &[slot])?
|
2022-09-22 19:40:06 +00:00
|
|
|
.slots
|
|
|
|
.get(&slot)
|
2022-11-30 01:31:25 +00:00
|
|
|
.unwrap(),
|
2022-09-22 19:40:06 +00:00
|
|
|
},
|
2022-11-30 01:31:25 +00:00
|
|
|
None => *self
|
2022-11-03 23:36:14 +00:00
|
|
|
.get_account(address, &[slot])?
|
2022-09-22 19:40:06 +00:00
|
|
|
.slots
|
|
|
|
.get(&slot)
|
2022-11-30 01:31:25 +00:00
|
|
|
.unwrap(),
|
2022-11-03 23:36:14 +00:00
|
|
|
})
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-03 23:36:14 +00:00
|
|
|
fn code_by_hash(&mut self, _code_hash: H256) -> Result<Bytecode, Report> {
|
|
|
|
Err(eyre::eyre!("should never be called"))
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_precompile(address: &Address) -> bool {
|
|
|
|
address.le(&Address::from_str("0x0000000000000000000000000000000000000009").unwrap())
|
2022-09-22 19:40:06 +00:00
|
|
|
&& address.gt(&Address::zero())
|
2022-08-24 01:33:48 +00:00
|
|
|
}
|