Optimizing mem gas cost

This commit is contained in:
Tomasz Drwięga 2016-07-11 12:51:31 +02:00
parent edaf24a2ce
commit f1edd3d683
3 changed files with 79 additions and 10 deletions

View File

@ -107,6 +107,11 @@ pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Out
fn overflow_add(self, other: Self) -> (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<GasLeft>;
}
#[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);
}

View File

@ -68,6 +68,9 @@ impl<Gas: CostType> Gasometer<Gas> {
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<Gas: CostType> Gasometer<Gas> {
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<Gas: CostType> Gasometer<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)));
// 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;

View File

@ -17,6 +17,7 @@
//! Parity EVM interpreter binary.
#![warn(missing_docs)]
#![allow(dead_code)]
extern crate ethcore;
extern crate rustc_serialize;
extern crate docopt;