From f1edd3d6834b6fa8c7cd9921b9e567e281db64aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 11 Jul 2016 12:51:31 +0200 Subject: [PATCH] Optimizing mem gas cost --- ethcore/src/evm/evm.rs | 70 ++++++++++++++++++++++++ ethcore/src/evm/interpreter/gasometer.rs | 18 +++--- evmbin/src/main.rs | 1 + 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/ethcore/src/evm/evm.rs b/ethcore/src/evm/evm.rs index 3ec943f18..a300650f3 100644 --- a/ethcore/src/evm/evm.rs +++ b/ethcore/src/evm/evm.rs @@ -107,6 +107,11 @@ pub trait CostType: ops::Mul + ops::Div + ops::Add (Self, bool); /// Multiple with overflow fn overflow_mul(self, other: Self) -> (Self, bool); + /// Divide with overflow + fn overflow_div(self, other: Self) -> (Self, bool); + /// Single-step full multiplication and division: `self*other/div` + /// Should not overflow on intermediate steps + fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool); } impl CostType for U256 { @@ -129,6 +134,21 @@ impl CostType for U256 { fn overflow_mul(self, other: Self) -> (Self, bool) { Uint::overflowing_mul(self, other) } + + fn overflow_div(self, other: Self) -> (Self, bool) { + Uint::overflowing_div(self, other) + } + + fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool) { + let x = self.full_mul(other); + let (U512(parts), o) = Uint::overflowing_div(x, U512::from(div)); + let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0; + + ( + U256([parts[0], parts[1], parts[2], parts[3]]), + o | overflow + ) + } } impl CostType for usize { @@ -154,6 +174,18 @@ impl CostType for usize { fn overflow_mul(self, other: Self) -> (Self, bool) { self.overflowing_mul(other) } + + fn overflow_div(self, other: Self) -> (Self, bool) { + self.overflowing_div(other) + } + + fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool) { + let (c, o) = U128::from(self).overflowing_mul(U128::from(other)); + let (U128(parts), o1) = c.overflowing_div(U128::from(div)); + let result = parts[0] as usize; + let overflow = o | o1 | (parts[1] > 0) | (parts[0] > result as u64); + (result, overflow) + } } /// Evm interface @@ -164,3 +196,41 @@ pub trait Evm { /// to compute the final gas left. fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result; } + + +#[test] +fn should_calculate_overflow_mul_div_without_overflow() { + // given + let num = 10_000_000; + + // when + let (res1, o1) = U256::from(num).overflow_mul_div(U256::from(num), U256::from(num)); + let (res2, o2) = num.overflow_mul_div(num, num); + + // then + assert_eq!(res1, U256::from(num)); + assert!(!o1); + assert_eq!(res2, num); + assert!(!o2); +} + +#[test] +fn should_calculate_overflow_mul_div_with_overflow() { + // given + let max = ::std::u64::MAX; + let num1 = U256([max, max, max, max]); + let num2 = ::std::usize::MAX; + + // when + let (res1, o1) = num1.overflow_mul_div(num1, num1 - U256::from(2)); + let (res2, o2) = num2.overflow_mul_div(num2, num2 - 2); + + // then + // (x+2)^2/x = (x^2 + 4x + 4)/x = x + 4 + 4/x ~ (MAX-2) + 4 + 0 = 1 + assert_eq!(res2, 1); + assert!(o2); + + assert_eq!(res1, U256::from(1)); + assert!(o1); +} + diff --git a/ethcore/src/evm/interpreter/gasometer.rs b/ethcore/src/evm/interpreter/gasometer.rs index 069d70e19..0fc349a27 100644 --- a/ethcore/src/evm/interpreter/gasometer.rs +++ b/ethcore/src/evm/interpreter/gasometer.rs @@ -68,6 +68,9 @@ impl Gasometer { let default_gas = Gas::from(schedule.tier_step_gas[tier]); let cost = match instruction { + instructions::JUMPDEST => { + InstructionCost::Gas(Gas::from(1)) + }, instructions::SSTORE => { let address = H256::from(stack.peek(0)); let newval = stack.peek(1); @@ -106,9 +109,6 @@ impl Gasometer { instructions::EXTCODECOPY => { InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3)))) }, - instructions::JUMPDEST => { - InstructionCost::Gas(Gas::from(1)) - }, instructions::LOG0...instructions::LOG4 => { let no_of_topics = instructions::get_log_topics(instruction); let log_gas = schedule.log_gas + schedule.log_topic_gas * no_of_topics; @@ -199,14 +199,12 @@ impl Gasometer { let s = mem_size >> 5; // s * memory_gas + s * s / quad_coeff_div let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas))); - // We need to go to U512 to calculate s*s/quad_coeff_div - let b = U512::from(s.as_u256()) * U512::from(s.as_u256()) / U512::from(schedule.quad_coeff_div); - if b > U512::from(!U256::zero()) { - Err(evm::Error::OutOfGas) - } else { - Ok(overflowing!(a.overflow_add(try!(Gas::from_u256(U256::from(b)))))) - } + + // Calculate s*s/quad_coeff_div + let b = overflowing!(s.overflow_mul_div(s, Gas::from(schedule.quad_coeff_div))); + Ok(overflowing!(a.overflow_add(b))) }; + 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; diff --git a/evmbin/src/main.rs b/evmbin/src/main.rs index 240a02ccf..94684129c 100644 --- a/evmbin/src/main.rs +++ b/evmbin/src/main.rs @@ -17,6 +17,7 @@ //! Parity EVM interpreter binary. #![warn(missing_docs)] +#![allow(dead_code)] extern crate ethcore; extern crate rustc_serialize; extern crate docopt;