diff --git a/crates/revm/src/db/in_memory_db.rs b/crates/revm/src/db/in_memory_db.rs index 798193bc5d..c247869d55 100644 --- a/crates/revm/src/db/in_memory_db.rs +++ b/crates/revm/src/db/in_memory_db.rs @@ -8,7 +8,7 @@ use hashbrown::{hash_map::Entry, HashMap as Map}; use primitive_types::{H160, H256, U256}; -use crate::{Account, AccountInfo, Log}; +use crate::{Account, AccountInfo, Log, PrecompState}; use bytes::Bytes; use sha3::{Digest, Keccak256}; @@ -129,6 +129,7 @@ impl DatabaseCommit for CacheDB { btree_map::Entry::Occupied(mut entry) => { let db_acc = entry.get_mut(); db_acc.info = acc.info; + if matches!(acc.filth, Filth::NewlyCreated) { db_acc.account_state = AccountState::EVMStorageCleared; db_acc.storage = acc.storage.into_iter().collect(); @@ -297,6 +298,7 @@ impl Database for BenchmarkDB { balance: U256::from(10000000), code: Some(self.0.clone()), code_hash: KECCAK_EMPTY, + precomp_state: PrecompState::None, }; } AccountInfo::default() diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs index 1b590b6795..767ff7f46b 100644 --- a/crates/revm/src/evm_impl.rs +++ b/crates/revm/src/evm_impl.rs @@ -170,13 +170,13 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, for (add, _) in precompiles.as_slice() { precompile_acc.push(*add); } - subroutine.load_precompiles_default(&precompile_acc); + subroutine.load_precompiles_default(&precompile_acc, env.cfg.precompiles_called); } else { let mut precompile_acc = Map::new(); for (add, _) in precompiles.as_slice() { precompile_acc.insert(*add, db.basic(*add)); } - subroutine.load_precompiles(precompile_acc); + subroutine.load_precompiles(precompile_acc, env.cfg.precompiles_called); } Self { data: EVMData { diff --git a/crates/revm/src/instructions/host.rs b/crates/revm/src/instructions/host.rs index 71e1a3bd90..71251527ec 100644 --- a/crates/revm/src/instructions/host.rs +++ b/crates/revm/src/instructions/host.rs @@ -341,6 +341,7 @@ pub fn call( // load account and calculate gas cost. let (is_cold, exist) = host.load_account(to); let is_new = !exist; + //let is_cold = false; gas!( interp, diff --git a/crates/revm/src/models.rs b/crates/revm/src/models.rs index aa5be44801..adfbc15fb6 100644 --- a/crates/revm/src/models.rs +++ b/crates/revm/src/models.rs @@ -10,6 +10,15 @@ pub const KECCAK_EMPTY: H256 = H256([ 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70, ]); +/// State of a precompile +#[derive(Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] +pub enum PrecompState { + None, + Pending, + Called, +} + /// AccountInfo account information. #[derive(Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))] @@ -24,6 +33,8 @@ pub struct AccountInfo { /// inside of revm. #[cfg_attr(feature = "with-serde", serde(with = "serde_hex_bytes_opt"))] pub code: Option, + + pub precomp_state: PrecompState, } impl Default for AccountInfo { @@ -33,6 +44,7 @@ impl Default for AccountInfo { code_hash: KECCAK_EMPTY, code: Some(Bytes::new()), nonce: 0, + precomp_state: PrecompState::None, } } } @@ -49,6 +61,7 @@ impl AccountInfo { nonce, code: Some(code), code_hash, + precomp_state: PrecompState::None, } } @@ -219,6 +232,8 @@ pub struct TxEnv { pub struct CfgEnv { pub chain_id: U256, pub spec_id: SpecId, + /// should precompiles be marked as "called" or not, for gas calculation. + pub precompiles_called: bool, /// If all precompiles have some balance we can skip initially fetching them from the database. /// This is is not really needed on mainnet, and defaults to false, but in most cases it is /// safe to be set to `true`, depending on the chain. @@ -237,6 +252,7 @@ impl Default for CfgEnv { CfgEnv { chain_id: 1.into(), spec_id: SpecId::LATEST, + precompiles_called: true, perf_all_precompiles_have_balance: false, #[cfg(feature = "memory_limit")] memory_limit: 2u64.pow(32) - 1, diff --git a/crates/revm/src/subroutine.rs b/crates/revm/src/subroutine.rs index 95bdb9e5f5..f94e15c5cb 100644 --- a/crates/revm/src/subroutine.rs +++ b/crates/revm/src/subroutine.rs @@ -1,4 +1,4 @@ -use crate::{models::SelfDestructResult, Return, KECCAK_EMPTY}; +use crate::{models::SelfDestructResult, PrecompState, Return, KECCAK_EMPTY}; use alloc::{vec, vec::Vec}; use core::mem::{self}; use hashbrown::{hash_map::Entry, HashMap as Map}; @@ -90,24 +90,30 @@ impl SubRoutine { &mut self.state } - pub fn load_precompiles(&mut self, precompiles: Map) { + pub fn load_precompiles(&mut self, precompiles: Map, called: bool) { let state: Map = precompiles .into_iter() .map(|(k, value)| { let mut acc = Account::from(value); acc.filth = Filth::Precompile(false); + if called { + acc.info.precomp_state = PrecompState::Called; + } (k, acc) }) .collect(); self.state.extend(state); } - pub fn load_precompiles_default(&mut self, precompiles: &[H160]) { + pub fn load_precompiles_default(&mut self, precompiles: &[H160], called: bool) { let state: State = precompiles .iter() .map(|&k| { let mut acc = Account::from(AccountInfo::default()); acc.filth = Filth::Precompile(false); + if called { + acc.info.precomp_state = PrecompState::Called; + } (k, acc) }) .collect(); @@ -127,6 +133,13 @@ impl SubRoutine { match acc.filth { // acc was destroyed or if it is changed precompile, just add it to output. Filth::Destroyed | Filth::Precompile(true) => { + if acc.filth.is_precompile() { + if acc.info.precomp_state == PrecompState::Pending { + acc.info.precomp_state = PrecompState::Called; + } else { + continue; + } + } acc.info.code = None; acc.info.code_hash = KECCAK_EMPTY; out.insert(add, acc); @@ -246,6 +259,7 @@ impl SubRoutine { let to_is_cold = self.load_account(to, db); // check from balance and substract value let from = self.log_dirty(from, |_| {}); + if from.info.balance < value { return Err(Return::OutOfFund); } @@ -505,7 +519,17 @@ impl SubRoutine { pub fn load_account_exist(&mut self, address: H160, db: &mut DB) -> (bool, bool) { let (acc, is_cold) = self.load_code(address, db); let info = acc.info.clone(); - let is_empty = info.is_empty() && !acc.filth.is_precompile(); + let is_empty = if acc.filth.is_precompile() { + if acc.info.precomp_state == PrecompState::Called { + false + } else { + acc.filth.make_dirty(); + acc.info.precomp_state = PrecompState::Pending; + true + } + } else { + info.is_empty() + }; (is_cold, !is_empty) } @@ -630,7 +654,7 @@ pub struct Account { impl Account { pub fn is_empty(&self) -> bool { - self.info.is_empty() + self.info.is_empty() && !self.filth.is_precompile() } }