diff --git a/gasometer/src/lib.rs b/gasometer/src/lib.rs index d41924201..6518b66ba 100644 --- a/gasometer/src/lib.rs +++ b/gasometer/src/lib.rs @@ -452,7 +452,7 @@ pub fn dynamic_opcode_cost( stack: &Stack, is_static: bool, config: &Config, - handler: &H, + handler: &mut H, ) -> Result<(GasCost, StorageTarget, Option), ExitError> { let mut storage_target = StorageTarget::None; let gas_cost = match opcode { @@ -479,14 +479,14 @@ pub fn dynamic_opcode_cost( let target = stack.peek(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::ExtCodeSize { - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, } } Opcode::BALANCE => { let target = stack.peek(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::Balance { - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, } } Opcode::BLOCKHASH => GasCost::BlockHash, @@ -495,7 +495,7 @@ pub fn dynamic_opcode_cost( let target = stack.peek(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::ExtCodeHash { - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, } } Opcode::EXTCODEHASH => GasCost::Invalid(opcode), @@ -506,7 +506,7 @@ pub fn dynamic_opcode_cost( GasCost::CallCode { value: U256::from_big_endian(&stack.peek(2)?[..]), gas: U256::from_big_endian(&stack.peek(0)?[..]), - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } } @@ -515,7 +515,7 @@ pub fn dynamic_opcode_cost( storage_target = StorageTarget::Address(target); GasCost::StaticCall { gas: U256::from_big_endian(&stack.peek(0)?[..]), - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } } @@ -526,7 +526,7 @@ pub fn dynamic_opcode_cost( let target = stack.peek(0)?.into(); storage_target = StorageTarget::Address(target); GasCost::ExtCodeCopy { - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, len: U256::from_big_endian(&stack.peek(3)?[..]), } } @@ -540,7 +540,7 @@ pub fn dynamic_opcode_cost( let index = stack.peek(0)?; storage_target = StorageTarget::Slot(address, index); GasCost::SLoad { - target_is_cold: handler.is_cold(address, Some(index)), + target_is_cold: handler.is_cold(address, Some(index))?, } } @@ -549,7 +549,7 @@ pub fn dynamic_opcode_cost( storage_target = StorageTarget::Address(target); GasCost::DelegateCall { gas: U256::from_big_endian(&stack.peek(0)?[..]), - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } } @@ -570,7 +570,7 @@ pub fn dynamic_opcode_cost( original: handler.original_storage(address, index), current: handler.storage(address, index), new: value, - target_is_cold: handler.is_cold(address, Some(index)), + target_is_cold: handler.is_cold(address, Some(index))?, } } Opcode::LOG0 if !is_static => GasCost::Log { @@ -602,7 +602,7 @@ pub fn dynamic_opcode_cost( storage_target = StorageTarget::Address(target); GasCost::Suicide { value: handler.balance(address), - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), already_removed: handler.deleted(address), } @@ -616,7 +616,7 @@ pub fn dynamic_opcode_cost( GasCost::Call { value: U256::from_big_endian(&stack.peek(2)?[..]), gas: U256::from_big_endian(&stack.peek(0)?[..]), - target_is_cold: handler.is_cold(target, None), + target_is_cold: handler.is_cold(target, None)?, target_exists: handler.exists(target), } } diff --git a/runtime/src/handler.rs b/runtime/src/handler.rs index 4c3aea80a..e7e25182d 100644 --- a/runtime/src/handler.rs +++ b/runtime/src/handler.rs @@ -71,7 +71,7 @@ pub trait Handler { /// References: /// * /// * - fn is_cold(&self, address: H160, index: Option) -> bool; + fn is_cold(&mut self, address: H160, index: Option) -> Result; /// Set storage value of address at index. fn set_storage(&mut self, address: H160, index: H256, value: H256) -> Result<(), ExitError>; diff --git a/src/executor/stack/executor.rs b/src/executor/stack/executor.rs index add4675b5..05d41c3ee 100644 --- a/src/executor/stack/executor.rs +++ b/src/executor/stack/executor.rs @@ -1,6 +1,6 @@ use crate::backend::Backend; use crate::executor::stack::precompile::{ - PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet, + IsPrecompileResult, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileSet, }; use crate::executor::stack::tagged_runtime::{RuntimeKind, TaggedRuntime}; use crate::gasometer::{self, Gasometer, StorageTarget}; @@ -1051,11 +1051,30 @@ impl<'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Handler } } - fn is_cold(&self, address: H160, maybe_index: Option) -> bool { - match maybe_index { - None => !self.precompile_set.is_precompile(address) && self.state.is_cold(address), + fn is_cold(&mut self, address: H160, maybe_index: Option) -> Result { + Ok(match maybe_index { + None => { + let is_precompile = match self + .precompile_set + .is_precompile(address, self.state.metadata().gasometer.gas()) + { + IsPrecompileResult::Answer { + is_precompile, + extra_cost, + } => { + self.state + .metadata_mut() + .gasometer + .record_cost(extra_cost)?; + is_precompile + } + IsPrecompileResult::OutOfGas => return Err(ExitError::OutOfGas), + }; + + !is_precompile && self.state.is_cold(address) + } Some(index) => self.state.is_storage_cold(address, index), - } + }) } fn gas_left(&self) -> U256 { @@ -1286,10 +1305,15 @@ impl<'inner, 'config, 'precompiles, S: StackState<'config>, P: PrecompileSet> Pr // Since we don't go through opcodes we need manually record the call // cost. Not doing so will make the code panic as recording the call stipend // will do an underflow. + let target_is_cold = match self.executor.is_cold(code_address, None) { + Ok(x) => x, + Err(err) => return (ExitReason::Error(err), Vec::new()), + }; + let gas_cost = crate::gasometer::GasCost::Call { value: transfer.clone().map(|x| x.value).unwrap_or_else(U256::zero), gas: U256::from(gas_limit.unwrap_or(u64::MAX)), - target_is_cold: self.executor.is_cold(code_address, None), + target_is_cold, target_exists: self.executor.exists(code_address), }; diff --git a/src/executor/stack/precompile.rs b/src/executor/stack/precompile.rs index bda25b33c..25fbee35a 100644 --- a/src/executor/stack/precompile.rs +++ b/src/executor/stack/precompile.rs @@ -83,7 +83,15 @@ pub trait PrecompileSet { /// Check if the given address is a precompile. Should only be called to /// perform the check while not executing the precompile afterward, since /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160) -> bool; + fn is_precompile(&self, address: H160, remaining_gas: u64) -> IsPrecompileResult; +} + +pub enum IsPrecompileResult { + Answer { + is_precompile: bool, + extra_cost: u64, + }, + OutOfGas, } impl PrecompileSet for () { @@ -91,8 +99,11 @@ impl PrecompileSet for () { None } - fn is_precompile(&self, _: H160) -> bool { - false + fn is_precompile(&self, _: H160, _: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: false, + extra_cost: 0, + } } } @@ -129,7 +140,10 @@ impl PrecompileSet for BTreeMap { /// Check if the given address is a precompile. Should only be called to /// perform the check while not executing the precompile afterward, since /// `execute` already performs a check internally. - fn is_precompile(&self, address: H160) -> bool { - self.contains_key(&address) + fn is_precompile(&self, address: H160, _: u64) -> IsPrecompileResult { + IsPrecompileResult::Answer { + is_precompile: self.contains_key(&address), + extra_cost: 0, + } } }