2018-06-04 10:19:50 +02:00
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
2016-07-05 15:15:44 +02:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
2017-07-29 17:12:07 +02:00
use std ::cmp ;
2018-01-10 13:35:18 +01:00
use ethereum_types ::{ U256 , H256 } ;
2016-07-05 15:15:44 +02:00
use super ::u256_to_address ;
2017-07-12 13:09:17 +02:00
2017-08-01 12:37:57 +02:00
use { evm , vm } ;
2017-07-12 13:09:17 +02:00
use instructions ::{ self , Instruction , InstructionInfo } ;
use interpreter ::stack ::Stack ;
2017-08-01 12:37:57 +02:00
use vm ::Schedule ;
2016-07-05 15:15:44 +02:00
macro_rules ! overflowing {
( $x : expr ) = > { {
let ( v , overflow ) = $x ;
2017-08-01 12:37:57 +02:00
if overflow { return Err ( vm ::Error ::OutOfGas ) ; }
2016-07-05 15:15:44 +02:00
v
} }
}
2017-07-12 13:09:17 +02:00
enum Request < Cost : ::evm ::CostType > {
2016-07-05 15:15:44 +02:00
Gas ( Cost ) ,
2016-10-27 19:29:23 +02:00
GasMem ( Cost , Cost ) ,
GasMemProvide ( Cost , Cost , Option < U256 > ) ,
2016-07-05 15:15:44 +02:00
GasMemCopy ( Cost , Cost , Cost )
}
2017-07-12 13:09:17 +02:00
pub struct InstructionRequirements < Cost > {
2016-10-27 19:29:23 +02:00
pub gas_cost : Cost ,
pub provide_gas : Option < Cost > ,
pub memory_total_gas : Cost ,
pub memory_required_size : usize ,
}
2017-07-12 13:09:17 +02:00
pub struct Gasometer < Gas > {
2016-07-05 15:15:44 +02:00
pub current_gas : Gas ,
2016-07-30 15:38:44 +02:00
pub current_mem_gas : Gas ,
2016-07-05 15:15:44 +02:00
}
2017-07-12 13:09:17 +02:00
impl < Gas : evm ::CostType > Gasometer < Gas > {
2016-07-05 15:15:44 +02:00
pub fn new ( current_gas : Gas ) -> Self {
Gasometer {
current_gas : current_gas ,
2016-07-30 15:38:44 +02:00
current_mem_gas : Gas ::from ( 0 ) ,
2016-07-05 15:15:44 +02:00
}
}
2017-08-01 12:37:57 +02:00
pub fn verify_gas ( & self , gas_cost : & Gas ) -> vm ::Result < ( ) > {
2016-07-05 15:15:44 +02:00
match & self . current_gas < gas_cost {
2017-08-01 12:37:57 +02:00
true = > Err ( vm ::Error ::OutOfGas ) ,
2016-07-05 15:15:44 +02:00
false = > Ok ( ( ) )
}
}
2016-10-15 14:39:15 +02:00
/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
/// and that we `requested` some.
2017-08-01 12:37:57 +02:00
pub fn gas_provided ( & self , schedule : & Schedule , needed : Gas , requested : Option < U256 > ) -> vm ::Result < Gas > {
2016-10-27 19:29:23 +02:00
// Try converting requested gas to `Gas` (`U256/u64`)
// but in EIP150 even if we request more we should never fail from OOG
let requested = requested . map ( Gas ::from_u256 ) ;
2016-10-15 14:39:15 +02:00
match schedule . sub_gas_cap_divisor {
Some ( cap_divisor ) if self . current_gas > = needed = > {
let gas_remaining = self . current_gas - needed ;
2016-10-27 19:29:23 +02:00
let max_gas_provided = match cap_divisor {
64 = > gas_remaining - ( gas_remaining > > 6 ) ,
cap_divisor = > gas_remaining - gas_remaining / Gas ::from ( cap_divisor ) ,
} ;
2016-10-24 18:35:25 +02:00
if let Some ( Ok ( r ) ) = requested {
2017-07-29 17:12:07 +02:00
Ok ( cmp ::min ( r , max_gas_provided ) )
2016-10-15 14:39:15 +02:00
} else {
Ok ( max_gas_provided )
}
} ,
_ = > {
2016-10-24 18:35:25 +02:00
if let Some ( r ) = requested {
2016-10-15 14:39:15 +02:00
r
} else if self . current_gas > = needed {
Ok ( self . current_gas - needed )
} else {
Ok ( 0. into ( ) )
}
2016-10-27 19:29:23 +02:00
} ,
2016-10-15 14:39:15 +02:00
}
}
/// Determine how much gas is used by the given instruction, given the machine's state.
2016-10-24 18:35:25 +02:00
///
2016-10-15 14:39:15 +02:00
/// We guarantee that the final element of the returned tuple (`provided`) will be `Some`
/// iff the `instruction` is one of `CREATE`, or any of the `CALL` variants. In this case,
2016-10-24 18:35:25 +02:00
/// it will be the amount of gas that the current context provides to the child context.
2016-10-27 19:29:23 +02:00
pub fn requirements (
2016-07-05 15:15:44 +02:00
& mut self ,
2017-08-01 12:37:57 +02:00
ext : & vm ::Ext ,
2016-07-05 15:15:44 +02:00
instruction : Instruction ,
info : & InstructionInfo ,
stack : & Stack < U256 > ,
current_mem_size : usize ,
2017-08-01 12:37:57 +02:00
) -> vm ::Result < InstructionRequirements < Gas > > {
2016-07-05 15:15:44 +02:00
let schedule = ext . schedule ( ) ;
2018-06-27 13:33:32 +02:00
let tier = info . tier . idx ( ) ;
2016-07-05 15:15:44 +02:00
let default_gas = Gas ::from ( schedule . tier_step_gas [ tier ] ) ;
let cost = match instruction {
2016-07-12 09:49:16 +02:00
instructions ::JUMPDEST = > {
2016-10-27 19:29:23 +02:00
Request ::Gas ( Gas ::from ( 1 ) )
2016-07-12 09:49:16 +02:00
} ,
2016-07-05 15:15:44 +02:00
instructions ::SSTORE = > {
let address = H256 ::from ( stack . peek ( 0 ) ) ;
let newval = stack . peek ( 1 ) ;
2017-02-26 13:10:50 +01:00
let val = U256 ::from ( & * ext . storage_at ( & address ) ? ) ;
2016-07-05 15:15:44 +02:00
2018-09-07 12:51:08 +02:00
let gas = if schedule . eip1283 {
let orig = U256 ::from ( & * ext . initial_storage_at ( & address ) ? ) ;
calculate_eip1283_sstore_gas ( schedule , & orig , & val , & newval )
2016-07-05 15:15:44 +02:00
} else {
2018-09-07 12:51:08 +02:00
if val . is_zero ( ) & & ! newval . is_zero ( ) {
schedule . sstore_set_gas
} else {
// Refund for below case is added when actually executing sstore
// !is_zero(&val) && is_zero(newval)
schedule . sstore_reset_gas
}
2016-07-05 15:15:44 +02:00
} ;
2016-10-27 19:29:23 +02:00
Request ::Gas ( Gas ::from ( gas ) )
2016-07-05 15:15:44 +02:00
} ,
instructions ::SLOAD = > {
2016-10-27 19:29:23 +02:00
Request ::Gas ( Gas ::from ( schedule . sload_gas ) )
2016-07-05 15:15:44 +02:00
} ,
2016-10-15 14:39:15 +02:00
instructions ::BALANCE = > {
2016-10-27 19:29:23 +02:00
Request ::Gas ( Gas ::from ( schedule . balance_gas ) )
2016-10-15 14:39:15 +02:00
} ,
instructions ::EXTCODESIZE = > {
2016-10-27 19:29:23 +02:00
Request ::Gas ( Gas ::from ( schedule . extcodesize_gas ) )
2016-10-15 14:39:15 +02:00
} ,
2018-07-31 07:27:57 +02:00
instructions ::EXTCODEHASH = > {
Request ::Gas ( Gas ::from ( schedule . extcodehash_gas ) )
} ,
2016-10-15 14:39:15 +02:00
instructions ::SUICIDE = > {
let mut gas = Gas ::from ( schedule . suicide_gas ) ;
2017-02-26 13:10:50 +01:00
let is_value_transfer = ! ext . origin_balance ( ) ? . is_zero ( ) ;
2016-10-15 14:39:15 +02:00
let address = u256_to_address ( stack . peek ( 0 ) ) ;
2016-11-03 22:22:25 +01:00
if (
2017-02-26 13:10:50 +01:00
! schedule . no_empty & & ! ext . exists ( & address ) ?
2016-11-03 22:22:25 +01:00
) | | (
2017-02-26 13:10:50 +01:00
schedule . no_empty & & is_value_transfer & & ! ext . exists_and_not_null ( & address ) ?
2016-11-03 22:22:25 +01:00
) {
2016-10-15 14:39:15 +02:00
gas = overflowing! ( gas . overflow_add ( schedule . suicide_to_new_account_cost . into ( ) ) ) ;
}
2016-10-27 19:29:23 +02:00
Request ::Gas ( gas )
2016-10-15 14:39:15 +02:00
} ,
2016-07-05 15:15:44 +02:00
instructions ::MSTORE | instructions ::MLOAD = > {
2016-12-27 12:53:56 +01:00
Request ::GasMem ( default_gas , mem_needed_const ( stack . peek ( 0 ) , 32 ) ? )
2016-07-05 15:15:44 +02:00
} ,
instructions ::MSTORE8 = > {
2016-12-27 12:53:56 +01:00
Request ::GasMem ( default_gas , mem_needed_const ( stack . peek ( 0 ) , 1 ) ? )
2016-07-05 15:15:44 +02:00
} ,
2017-05-23 15:49:17 +02:00
instructions ::RETURN | instructions ::REVERT = > {
2016-12-27 12:53:56 +01:00
Request ::GasMem ( default_gas , mem_needed ( stack . peek ( 0 ) , stack . peek ( 1 ) ) ? )
2016-07-05 15:15:44 +02:00
} ,
instructions ::SHA3 = > {
2016-12-27 12:53:56 +01:00
let w = overflowing! ( add_gas_usize ( Gas ::from_u256 ( * stack . peek ( 1 ) ) ? , 31 ) ) ;
2016-07-05 15:15:44 +02:00
let words = w > > 5 ;
let gas = Gas ::from ( schedule . sha3_gas ) + ( Gas ::from ( schedule . sha3_word_gas ) * words ) ;
2016-12-27 12:53:56 +01:00
Request ::GasMem ( gas , mem_needed ( stack . peek ( 0 ) , stack . peek ( 1 ) ) ? )
2016-07-05 15:15:44 +02:00
} ,
2017-06-06 17:47:12 +02:00
instructions ::CALLDATACOPY | instructions ::CODECOPY | instructions ::RETURNDATACOPY = > {
2016-12-27 12:53:56 +01:00
Request ::GasMemCopy ( default_gas , mem_needed ( stack . peek ( 0 ) , stack . peek ( 2 ) ) ? , Gas ::from_u256 ( * stack . peek ( 2 ) ) ? )
2016-07-05 15:15:44 +02:00
} ,
instructions ::EXTCODECOPY = > {
2016-12-27 12:53:56 +01:00
Request ::GasMemCopy ( schedule . extcodecopy_base_gas . into ( ) , mem_needed ( stack . peek ( 1 ) , stack . peek ( 3 ) ) ? , Gas ::from_u256 ( * stack . peek ( 3 ) ) ? )
2016-07-05 15:15:44 +02:00
} ,
2018-06-27 13:33:32 +02:00
instructions ::LOG0 | instructions ::LOG1 | instructions ::LOG2 | instructions ::LOG3 | instructions ::LOG4 = > {
let no_of_topics = instruction . log_topics ( ) . expect ( " log_topics always return some for LOG* instructions; qed " ) ;
2016-07-05 15:15:44 +02:00
let log_gas = schedule . log_gas + schedule . log_topic_gas * no_of_topics ;
2016-12-27 12:53:56 +01:00
let data_gas = overflowing! ( Gas ::from_u256 ( * stack . peek ( 1 ) ) ? . overflow_mul ( Gas ::from ( schedule . log_data_gas ) ) ) ;
2016-07-05 15:15:44 +02:00
let gas = overflowing! ( data_gas . overflow_add ( Gas ::from ( log_gas ) ) ) ;
2016-12-27 12:53:56 +01:00
Request ::GasMem ( gas , mem_needed ( stack . peek ( 0 ) , stack . peek ( 1 ) ) ? )
2016-07-05 15:15:44 +02:00
} ,
instructions ::CALL | instructions ::CALLCODE = > {
2016-10-15 14:39:15 +02:00
let mut gas = Gas ::from ( schedule . call_gas ) ;
2016-07-05 15:15:44 +02:00
let mem = cmp ::max (
2016-12-27 12:53:56 +01:00
mem_needed ( stack . peek ( 5 ) , stack . peek ( 6 ) ) ? ,
mem_needed ( stack . peek ( 3 ) , stack . peek ( 4 ) ) ?
2016-07-05 15:15:44 +02:00
) ;
let address = u256_to_address ( stack . peek ( 1 ) ) ;
2016-11-03 22:22:25 +01:00
let is_value_transfer = ! stack . peek ( 2 ) . is_zero ( ) ;
2016-11-28 13:20:49 +01:00
if instruction = = instructions ::CALL & & (
2017-02-26 13:10:50 +01:00
( ! schedule . no_empty & & ! ext . exists ( & address ) ? )
2016-11-28 13:20:49 +01:00
| |
2017-02-26 13:10:50 +01:00
( schedule . no_empty & & is_value_transfer & & ! ext . exists_and_not_null ( & address ) ? )
2016-11-28 13:20:49 +01:00
) {
gas = overflowing! ( gas . overflow_add ( schedule . call_new_account_gas . into ( ) ) ) ;
}
2016-07-05 15:15:44 +02:00
2016-11-03 22:22:25 +01:00
if is_value_transfer {
2016-10-15 14:39:15 +02:00
gas = overflowing! ( gas . overflow_add ( schedule . call_value_transfer_gas . into ( ) ) ) ;
2016-11-28 13:20:49 +01:00
}
2016-07-05 15:15:44 +02:00
2016-10-27 19:29:23 +02:00
let requested = * stack . peek ( 0 ) ;
2016-10-15 14:39:15 +02:00
2016-10-27 19:29:23 +02:00
Request ::GasMemProvide ( gas , mem , Some ( requested ) )
2016-07-05 15:15:44 +02:00
} ,
2017-06-19 11:41:46 +02:00
instructions ::DELEGATECALL | instructions ::STATICCALL = > {
2016-10-27 19:29:23 +02:00
let gas = Gas ::from ( schedule . call_gas ) ;
2016-07-05 15:15:44 +02:00
let mem = cmp ::max (
2016-12-27 12:53:56 +01:00
mem_needed ( stack . peek ( 4 ) , stack . peek ( 5 ) ) ? ,
mem_needed ( stack . peek ( 2 ) , stack . peek ( 3 ) ) ?
2016-07-05 15:15:44 +02:00
) ;
2016-10-27 19:29:23 +02:00
let requested = * stack . peek ( 0 ) ;
2016-10-15 14:39:15 +02:00
2016-10-27 19:29:23 +02:00
Request ::GasMemProvide ( gas , mem , Some ( requested ) )
2016-07-05 15:15:44 +02:00
} ,
2017-05-05 16:00:40 +02:00
instructions ::CREATE | instructions ::CREATE2 = > {
2016-10-27 19:29:23 +02:00
let gas = Gas ::from ( schedule . create_gas ) ;
2018-09-11 17:47:26 +02:00
let mem = mem_needed ( stack . peek ( 1 ) , stack . peek ( 2 ) ) ? ;
2016-10-15 14:39:15 +02:00
2016-10-27 19:29:23 +02:00
Request ::GasMemProvide ( gas , mem , None )
2016-07-05 15:15:44 +02:00
} ,
instructions ::EXP = > {
let expon = stack . peek ( 1 ) ;
let bytes = ( ( expon . bits ( ) + 7 ) / 8 ) as usize ;
let gas = Gas ::from ( schedule . exp_gas + schedule . exp_byte_gas * bytes ) ;
2016-10-27 19:29:23 +02:00
Request ::Gas ( gas )
2016-07-05 15:15:44 +02:00
} ,
2017-05-30 11:52:33 +02:00
instructions ::BLOCKHASH = > {
Request ::Gas ( Gas ::from ( schedule . blockhash_gas ) )
} ,
2016-10-27 19:29:23 +02:00
_ = > Request ::Gas ( default_gas ) ,
2016-07-05 15:15:44 +02:00
} ;
2016-10-27 19:29:23 +02:00
Ok ( match cost {
Request ::Gas ( gas ) = > {
InstructionRequirements {
gas_cost : gas ,
provide_gas : None ,
memory_required_size : 0 ,
memory_total_gas : self . current_mem_gas ,
}
2016-07-05 15:15:44 +02:00
} ,
2016-10-27 19:29:23 +02:00
Request ::GasMem ( gas , mem_size ) = > {
2016-12-27 12:53:56 +01:00
let ( mem_gas_cost , new_mem_gas , new_mem_size ) = self . mem_gas_cost ( schedule , current_mem_size , & mem_size ) ? ;
2016-07-30 15:38:44 +02:00
let gas = overflowing! ( gas . overflow_add ( mem_gas_cost ) ) ;
2016-10-27 19:29:23 +02:00
InstructionRequirements {
gas_cost : gas ,
provide_gas : None ,
memory_required_size : new_mem_size ,
memory_total_gas : new_mem_gas ,
}
} ,
Request ::GasMemProvide ( gas , mem_size , requested ) = > {
2016-12-27 12:53:56 +01:00
let ( mem_gas_cost , new_mem_gas , new_mem_size ) = self . mem_gas_cost ( schedule , current_mem_size , & mem_size ) ? ;
2016-10-27 19:29:23 +02:00
let gas = overflowing! ( gas . overflow_add ( mem_gas_cost ) ) ;
2016-12-27 12:53:56 +01:00
let provided = self . gas_provided ( schedule , gas , requested ) ? ;
2016-10-27 19:29:23 +02:00
let total_gas = overflowing! ( gas . overflow_add ( provided ) ) ;
InstructionRequirements {
gas_cost : total_gas ,
provide_gas : Some ( provided ) ,
memory_required_size : new_mem_size ,
memory_total_gas : new_mem_gas ,
}
2016-07-05 15:15:44 +02:00
} ,
2016-10-27 19:29:23 +02:00
Request ::GasMemCopy ( gas , mem_size , copy ) = > {
2016-12-27 12:53:56 +01:00
let ( mem_gas_cost , new_mem_gas , new_mem_size ) = self . mem_gas_cost ( schedule , current_mem_size , & mem_size ) ? ;
2016-07-30 15:38:44 +02:00
let copy = overflowing! ( add_gas_usize ( copy , 31 ) ) > > 5 ;
let copy_gas = Gas ::from ( schedule . copy_gas ) * copy ;
2016-07-05 15:15:44 +02:00
let gas = overflowing! ( gas . overflow_add ( copy_gas ) ) ;
2016-07-30 15:38:44 +02:00
let gas = overflowing! ( gas . overflow_add ( mem_gas_cost ) ) ;
2016-10-27 19:29:23 +02:00
InstructionRequirements {
gas_cost : gas ,
provide_gas : None ,
memory_required_size : new_mem_size ,
memory_total_gas : new_mem_gas ,
}
} ,
} )
2016-07-05 15:15:44 +02:00
}
2017-08-01 12:37:57 +02:00
fn mem_gas_cost ( & self , schedule : & Schedule , current_mem_size : usize , mem_size : & Gas ) -> vm ::Result < ( Gas , Gas , usize ) > {
2016-07-05 15:15:44 +02:00
let gas_for_mem = | mem_size : Gas | {
let s = mem_size > > 5 ;
// s * memory_gas + s * s / quad_coeff_div
let a = overflowing! ( s . overflow_mul ( Gas ::from ( schedule . memory_gas ) ) ) ;
2016-07-12 09:49:16 +02:00
// Calculate s*s/quad_coeff_div
2016-10-27 19:29:23 +02:00
assert_eq! ( schedule . quad_coeff_div , 512 ) ;
2016-07-30 15:38:44 +02:00
let b = overflowing! ( s . overflow_mul_shr ( s , 9 ) ) ;
2016-07-12 09:49:16 +02:00
Ok ( overflowing! ( a . overflow_add ( b ) ) )
2016-07-05 15:15:44 +02:00
} ;
2016-07-12 09:49:16 +02:00
2016-07-05 15:15:44 +02:00
let current_mem_size = Gas ::from ( current_mem_size ) ;
let req_mem_size_rounded = ( overflowing! ( mem_size . overflow_add ( Gas ::from ( 31 as usize ) ) ) > > 5 ) < < 5 ;
2016-07-30 15:38:44 +02:00
let ( mem_gas_cost , new_mem_gas ) = if req_mem_size_rounded > current_mem_size {
2016-12-27 12:53:56 +01:00
let new_mem_gas = gas_for_mem ( req_mem_size_rounded ) ? ;
2016-07-30 15:38:44 +02:00
( new_mem_gas - self . current_mem_gas , new_mem_gas )
2016-07-05 15:15:44 +02:00
} else {
2016-07-30 15:38:44 +02:00
( Gas ::from ( 0 ) , self . current_mem_gas )
2016-07-05 15:15:44 +02:00
} ;
2016-07-30 15:38:44 +02:00
Ok ( ( mem_gas_cost , new_mem_gas , req_mem_size_rounded . as_usize ( ) ) )
2016-07-05 15:15:44 +02:00
}
}
2016-07-30 15:38:44 +02:00
#[ inline ]
2017-08-01 12:37:57 +02:00
fn mem_needed_const < Gas : evm ::CostType > ( mem : & U256 , add : usize ) -> vm ::Result < Gas > {
2016-07-30 15:38:44 +02:00
Gas ::from_u256 ( overflowing! ( mem . overflowing_add ( U256 ::from ( add ) ) ) )
}
#[ inline ]
2017-08-01 12:37:57 +02:00
fn mem_needed < Gas : evm ::CostType > ( offset : & U256 , size : & U256 ) -> vm ::Result < Gas > {
2016-07-30 15:38:44 +02:00
if size . is_zero ( ) {
return Ok ( Gas ::from ( 0 ) ) ;
}
Gas ::from_u256 ( overflowing! ( offset . overflowing_add ( * size ) ) )
}
2016-07-05 15:15:44 +02:00
#[ inline ]
2017-07-12 13:09:17 +02:00
fn add_gas_usize < Gas : evm ::CostType > ( value : Gas , num : usize ) -> ( Gas , bool ) {
2016-07-05 15:15:44 +02:00
value . overflow_add ( Gas ::from ( num ) )
}
2018-09-07 12:51:08 +02:00
#[ inline ]
fn calculate_eip1283_sstore_gas < Gas : evm ::CostType > ( schedule : & Schedule , original : & U256 , current : & U256 , new : & U256 ) -> Gas {
Gas ::from (
if current = = new {
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
schedule . sload_gas
} else {
// 2. If current value does not equal new value
if original = = current {
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
if original . is_zero ( ) {
// 2.1.1. If original value is 0, 20000 gas is deducted.
schedule . sstore_set_gas
} else {
// 2.1.2. Otherwise, 5000 gas is deducted.
schedule . sstore_reset_gas
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
}
} else {
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
schedule . sload_gas
// 2.2.1. If original value is not 0
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
}
}
)
}
pub fn handle_eip1283_sstore_clears_refund ( ext : & mut vm ::Ext , original : & U256 , current : & U256 , new : & U256 ) {
let sstore_clears_schedule = U256 ::from ( ext . schedule ( ) . sstore_refund_gas ) ;
if current = = new {
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
} else {
// 2. If current value does not equal new value
if original = = current {
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
if original . is_zero ( ) {
// 2.1.1. If original value is 0, 20000 gas is deducted.
} else {
// 2.1.2. Otherwise, 5000 gas is deducted.
if new . is_zero ( ) {
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
ext . add_sstore_refund ( sstore_clears_schedule ) ;
}
}
} else {
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
if ! original . is_zero ( ) {
// 2.2.1. If original value is not 0
if current . is_zero ( ) {
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
ext . sub_sstore_refund ( sstore_clears_schedule ) ;
} else if new . is_zero ( ) {
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
ext . add_sstore_refund ( sstore_clears_schedule ) ;
}
}
if original = = new {
// 2.2.2. If original value equals new value (this storage slot is reset)
if original . is_zero ( ) {
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
let refund = U256 ::from ( ext . schedule ( ) . sstore_set_gas - ext . schedule ( ) . sload_gas ) ;
ext . add_sstore_refund ( refund ) ;
} else {
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
let refund = U256 ::from ( ext . schedule ( ) . sstore_reset_gas - ext . schedule ( ) . sload_gas ) ;
ext . add_sstore_refund ( refund ) ;
}
}
}
}
}
2016-07-05 15:15:44 +02:00
#[ test ]
fn test_mem_gas_cost ( ) {
// given
let gasometer = Gasometer ::< U256 > ::new ( U256 ::zero ( ) ) ;
2017-07-12 13:09:17 +02:00
let schedule = Schedule ::default ( ) ;
2016-07-05 15:15:44 +02:00
let current_mem_size = 5 ;
let mem_size = ! U256 ::zero ( ) ;
// when
let result = gasometer . mem_gas_cost ( & schedule , current_mem_size , & mem_size ) ;
// then
2016-11-28 13:20:49 +01:00
if result . is_ok ( ) {
2016-07-05 15:15:44 +02:00
assert! ( false , " Should fail with OutOfGas " ) ;
}
}
#[ test ]
fn test_calculate_mem_cost ( ) {
// given
let gasometer = Gasometer ::< usize > ::new ( 0 ) ;
2017-07-12 13:09:17 +02:00
let schedule = Schedule ::default ( ) ;
2016-07-05 15:15:44 +02:00
let current_mem_size = 0 ;
let mem_size = 5 ;
// when
2016-07-30 15:38:44 +02:00
let ( mem_cost , new_mem_gas , mem_size ) = gasometer . mem_gas_cost ( & schedule , current_mem_size , & mem_size ) . unwrap ( ) ;
2016-07-05 15:15:44 +02:00
// then
assert_eq! ( mem_cost , 3 ) ;
2016-07-30 15:38:44 +02:00
assert_eq! ( new_mem_gas , 3 ) ;
2016-07-05 15:15:44 +02:00
assert_eq! ( mem_size , 32 ) ;
}