Gas for mem optimization (#1768)

* Optimizing mem_gas_cost calculations

* Memoizing current mem gas cost

* Optimizing jump destinations
This commit is contained in:
Tomasz Drwięga 2016-07-30 15:38:44 +02:00 committed by Gav Wood
parent 85c471b905
commit f56b89010d
11 changed files with 139 additions and 88 deletions

14
Cargo.lock generated
View File

@ -77,6 +77,19 @@ dependencies = [
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "bit-set"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-vec"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "0.3.3" version = "0.3.3"
@ -243,6 +256,7 @@ dependencies = [
name = "ethcore" name = "ethcore"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -17,23 +17,25 @@ env_logger = "0.3"
rustc-serialize = "0.3" rustc-serialize = "0.3"
heapsize = "0.3" heapsize = "0.3"
rust-crypto = "0.2.34" rust-crypto = "0.2.34"
time = "0.1"
ethcore-util = { path = "../util" }
evmjit = { path = "../evmjit", optional = true }
ethash = { path = "../ethash" }
num_cpus = "0.2" num_cpus = "0.2"
clippy = { version = "0.0.79", optional = true}
crossbeam = "0.2.9" crossbeam = "0.2.9"
lazy_static = "0.2" lazy_static = "0.2"
bloomchain = "0.1"
rayon = "0.3.1"
semver = "0.2"
bit-set = "0.4"
time = "0.1"
evmjit = { path = "../evmjit", optional = true }
clippy = { version = "0.0.79", optional = true}
ethash = { path = "../ethash" }
ethcore-util = { path = "../util" }
ethcore-devtools = { path = "../devtools" } ethcore-devtools = { path = "../devtools" }
ethjson = { path = "../json" } ethjson = { path = "../json" }
bloomchain = "0.1"
ethcore-ipc = { path = "../ipc/rpc" } ethcore-ipc = { path = "../ipc/rpc" }
rayon = "0.3.1"
ethstore = { path = "../ethstore" } ethstore = { path = "../ethstore" }
semver = "0.2"
ethcore-ipc-nano = { path = "../ipc/nano" } ethcore-ipc-nano = { path = "../ipc/nano" }
[dependencies.hyper] [dependencies.hyper]
git = "https://github.com/ethcore/hyper" git = "https://github.com/ethcore/hyper"
default-features = false default-features = false

View File

@ -107,9 +107,9 @@ pub trait CostType: ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Out
fn overflow_add(self, other: Self) -> (Self, bool); fn overflow_add(self, other: Self) -> (Self, bool);
/// Multiple with overflow /// Multiple with overflow
fn overflow_mul(self, other: Self) -> (Self, bool); fn overflow_mul(self, other: Self) -> (Self, bool);
/// Single-step full multiplication and division: `self*other/div` /// Single-step full multiplication and shift: `(self*other) >> shr`
/// Should not overflow on intermediate steps /// Should not overflow on intermediate steps
fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool); fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool);
} }
impl CostType for U256 { impl CostType for U256 {
@ -133,14 +133,14 @@ impl CostType for U256 {
Uint::overflowing_mul(self, other) Uint::overflowing_mul(self, other)
} }
fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool) { fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
let x = self.full_mul(other); let x = self.full_mul(other);
let (U512(parts), o) = Uint::overflowing_div(x, U512::from(div)); let U512(parts) = x;
let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0; let overflow = (parts[4] | parts[5] | parts[6] | parts[7]) > 0;
let U512(parts) = x >> shr;
( (
U256([parts[0], parts[1], parts[2], parts[3]]), U256([parts[0], parts[1], parts[2], parts[3]]),
o | overflow overflow
) )
} }
} }
@ -169,11 +169,13 @@ impl CostType for usize {
self.overflowing_mul(other) self.overflowing_mul(other)
} }
fn overflow_mul_div(self, other: Self, div: Self) -> (Self, bool) { fn overflow_mul_shr(self, other: Self, shr: usize) -> (Self, bool) {
let (c, o) = U128::from(self).overflowing_mul(U128::from(other)); let (c, o) = U128::from(self).overflowing_mul(U128::from(other));
let (U128(parts), o1) = c.overflowing_div(U128::from(div)); let U128(parts) = c;
let overflow = o | (parts[1] > 0);
let U128(parts) = c >> shr;
let result = parts[0] as usize; let result = parts[0] as usize;
let overflow = o | o1 | (parts[1] > 0) | (parts[0] > result as u64); let overflow = overflow | (parts[0] > result as u64);
(result, overflow) (result, overflow)
} }
} }
@ -189,13 +191,13 @@ pub trait Evm {
#[test] #[test]
fn should_calculate_overflow_mul_div_without_overflow() { fn should_calculate_overflow_mul_shr_without_overflow() {
// given // given
let num = 10_000_000; let num = 1048576;
// when // when
let (res1, o1) = U256::from(num).overflow_mul_div(U256::from(num), U256::from(num)); let (res1, o1) = U256::from(num).overflow_mul_shr(U256::from(num), 20);
let (res2, o2) = num.overflow_mul_div(num, num); let (res2, o2) = num.overflow_mul_shr(num, 20);
// then // then
assert_eq!(res1, U256::from(num)); assert_eq!(res1, U256::from(num));
@ -205,22 +207,21 @@ fn should_calculate_overflow_mul_div_without_overflow() {
} }
#[test] #[test]
fn should_calculate_overflow_mul_div_with_overflow() { fn should_calculate_overflow_mul_shr_with_overflow() {
// given // given
let max = ::std::u64::MAX; let max = ::std::u64::MAX;
let num1 = U256([max, max, max, max]); let num1 = U256([max, max, max, max]);
let num2 = ::std::usize::MAX; let num2 = ::std::usize::MAX;
// when // when
let (res1, o1) = num1.overflow_mul_div(num1, num1 - U256::from(2)); let (res1, o1) = num1.overflow_mul_shr(num1, 256);
let (res2, o2) = num2.overflow_mul_div(num2, num2 - 2); let (res2, o2) = num2.overflow_mul_shr(num2, 64);
// then // then
// (x+2)^2/x = (x^2 + 4x + 4)/x = x + 4 + 4/x ~ (MAX-2) + 4 + 0 = 1 assert_eq!(res2, num2 - 1);
assert_eq!(res2, 1);
assert!(o2); assert!(o2);
assert_eq!(res1, U256::from(1)); assert_eq!(res1, !U256::zero() - U256::one());
assert!(o1); assert!(o1);
} }

View File

@ -18,8 +18,8 @@
use util::common::*; use util::common::*;
use evm::{self, Schedule}; use evm::{self, Schedule};
use types::executed::CallType;
use env_info::*; use env_info::*;
use types::executed::CallType;
/// Result of externalities create function. /// Result of externalities create function.
pub enum ContractCreateResult { pub enum ContractCreateResult {

View File

@ -37,6 +37,7 @@ enum InstructionCost<Cost: CostType> {
pub struct Gasometer<Gas: CostType> { pub struct Gasometer<Gas: CostType> {
pub current_gas: Gas, pub current_gas: Gas,
pub current_mem_gas: Gas,
} }
impl<Gas: CostType> Gasometer<Gas> { impl<Gas: CostType> Gasometer<Gas> {
@ -44,6 +45,7 @@ impl<Gas: CostType> Gasometer<Gas> {
pub fn new(current_gas: Gas) -> Self { pub fn new(current_gas: Gas) -> Self {
Gasometer { Gasometer {
current_gas: current_gas, current_gas: current_gas,
current_mem_gas: Gas::from(0),
} }
} }
@ -62,7 +64,7 @@ impl<Gas: CostType> Gasometer<Gas> {
info: &InstructionInfo, info: &InstructionInfo,
stack: &Stack<U256>, stack: &Stack<U256>,
current_mem_size: usize, current_mem_size: usize,
) -> evm::Result<(Gas, usize)> { ) -> evm::Result<(Gas, Gas, usize)> {
let schedule = ext.schedule(); let schedule = ext.schedule();
let tier = instructions::get_tier_idx(info.tier); let tier = instructions::get_tier_idx(info.tier);
let default_gas = Gas::from(schedule.tier_step_gas[tier]); let default_gas = Gas::from(schedule.tier_step_gas[tier]);
@ -76,11 +78,11 @@ impl<Gas: CostType> Gasometer<Gas> {
let newval = stack.peek(1); let newval = stack.peek(1);
let val = U256::from(ext.storage_at(&address).as_slice()); let val = U256::from(ext.storage_at(&address).as_slice());
let gas = if U256::zero() == val && &U256::zero() != newval { let gas = if val.is_zero() && !newval.is_zero() {
schedule.sstore_set_gas schedule.sstore_set_gas
} else { } else {
// Refund for below case is added when actually executing sstore // Refund for below case is added when actually executing sstore
// !self.is_zero(&val) && self.is_zero(newval) // !is_zero(&val) && is_zero(newval)
schedule.sstore_reset_gas schedule.sstore_reset_gas
}; };
InstructionCost::Gas(Gas::from(gas)) InstructionCost::Gas(Gas::from(gas))
@ -89,25 +91,25 @@ impl<Gas: CostType> Gasometer<Gas> {
InstructionCost::Gas(Gas::from(schedule.sload_gas)) InstructionCost::Gas(Gas::from(schedule.sload_gas))
}, },
instructions::MSTORE | instructions::MLOAD => { instructions::MSTORE | instructions::MLOAD => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 32))) InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 32)))
}, },
instructions::MSTORE8 => { instructions::MSTORE8 => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed_const(stack.peek(0), 1))) InstructionCost::GasMem(default_gas, try!(mem_needed_const(stack.peek(0), 1)))
}, },
instructions::RETURN => { instructions::RETURN => {
InstructionCost::GasMem(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) InstructionCost::GasMem(default_gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
}, },
instructions::SHA3 => { instructions::SHA3 => {
let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31)); let w = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(1))), 31));
let words = w >> 5; let words = w >> 5;
let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words); let gas = Gas::from(schedule.sha3_gas) + (Gas::from(schedule.sha3_word_gas) * words);
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
}, },
instructions::CALLDATACOPY | instructions::CODECOPY => { instructions::CALLDATACOPY | instructions::CODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2)))) InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(0), stack.peek(2))), try!(Gas::from_u256(*stack.peek(2))))
}, },
instructions::EXTCODECOPY => { instructions::EXTCODECOPY => {
InstructionCost::GasMemCopy(default_gas, try!(self.mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3)))) InstructionCost::GasMemCopy(default_gas, try!(mem_needed(stack.peek(1), stack.peek(3))), try!(Gas::from_u256(*stack.peek(3))))
}, },
instructions::LOG0...instructions::LOG4 => { instructions::LOG0...instructions::LOG4 => {
let no_of_topics = instructions::get_log_topics(instruction); let no_of_topics = instructions::get_log_topics(instruction);
@ -115,13 +117,13 @@ impl<Gas: CostType> Gasometer<Gas> {
let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas))); let data_gas = overflowing!(try!(Gas::from_u256(*stack.peek(1))).overflow_mul(Gas::from(schedule.log_data_gas)));
let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas))); let gas = overflowing!(data_gas.overflow_add(Gas::from(log_gas)));
InstructionCost::GasMem(gas, try!(self.mem_needed(stack.peek(0), stack.peek(1)))) InstructionCost::GasMem(gas, try!(mem_needed(stack.peek(0), stack.peek(1))))
}, },
instructions::CALL | instructions::CALLCODE => { instructions::CALL | instructions::CALLCODE => {
let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas)); let mut gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
let mem = cmp::max( let mem = cmp::max(
try!(self.mem_needed(stack.peek(5), stack.peek(6))), try!(mem_needed(stack.peek(5), stack.peek(6))),
try!(self.mem_needed(stack.peek(3), stack.peek(4))) try!(mem_needed(stack.peek(3), stack.peek(4)))
); );
let address = u256_to_address(stack.peek(1)); let address = u256_to_address(stack.peek(1));
@ -130,7 +132,7 @@ impl<Gas: CostType> Gasometer<Gas> {
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas))); gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_new_account_gas)));
}; };
if stack.peek(2) > &U256::zero() { if !stack.peek(2).is_zero() {
gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas))); gas = overflowing!(gas.overflow_add(Gas::from(schedule.call_value_transfer_gas)));
}; };
@ -139,14 +141,14 @@ impl<Gas: CostType> Gasometer<Gas> {
instructions::DELEGATECALL => { instructions::DELEGATECALL => {
let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas)); let gas = overflowing!(add_gas_usize(try!(Gas::from_u256(*stack.peek(0))), schedule.call_gas));
let mem = cmp::max( let mem = cmp::max(
try!(self.mem_needed(stack.peek(4), stack.peek(5))), try!(mem_needed(stack.peek(4), stack.peek(5))),
try!(self.mem_needed(stack.peek(2), stack.peek(3))) try!(mem_needed(stack.peek(2), stack.peek(3)))
); );
InstructionCost::GasMem(gas, mem) InstructionCost::GasMem(gas, mem)
}, },
instructions::CREATE => { instructions::CREATE => {
let gas = Gas::from(schedule.create_gas); let gas = Gas::from(schedule.create_gas);
let mem = try!(self.mem_needed(stack.peek(1), stack.peek(2))); let mem = try!(mem_needed(stack.peek(1), stack.peek(2)));
InstructionCost::GasMem(gas, mem) InstructionCost::GasMem(gas, mem)
}, },
instructions::EXP => { instructions::EXP => {
@ -160,66 +162,65 @@ impl<Gas: CostType> Gasometer<Gas> {
match cost { match cost {
InstructionCost::Gas(gas) => { InstructionCost::Gas(gas) => {
Ok((gas, 0)) Ok((gas, self.current_mem_gas, 0))
}, },
InstructionCost::GasMem(gas, mem_size) => { InstructionCost::GasMem(gas, mem_size) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
let gas = overflowing!(gas.overflow_add(mem_gas)); let gas = overflowing!(gas.overflow_add(mem_gas_cost));
Ok((gas, new_mem_size)) Ok((gas, new_mem_gas, new_mem_size))
}, },
InstructionCost::GasMemCopy(gas, mem_size, copy) => { InstructionCost::GasMemCopy(gas, mem_size, copy) => {
let (mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size)); let (mem_gas_cost, new_mem_gas, new_mem_size) = try!(self.mem_gas_cost(schedule, current_mem_size, &mem_size));
let copy = overflowing!(add_gas_usize(copy, 31)); let copy = overflowing!(add_gas_usize(copy, 31)) >> 5;
let copy_gas = Gas::from(schedule.copy_gas) * (copy / Gas::from(32 as usize)); let copy_gas = Gas::from(schedule.copy_gas) * copy;
let gas = overflowing!(gas.overflow_add(copy_gas)); let gas = overflowing!(gas.overflow_add(copy_gas));
let gas = overflowing!(gas.overflow_add(mem_gas)); let gas = overflowing!(gas.overflow_add(mem_gas_cost));
Ok((gas, new_mem_size)) Ok((gas, new_mem_gas, new_mem_size))
} }
} }
} }
fn is_zero(&self, val: &Gas) -> bool { fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
&Gas::from(0) == val
}
fn mem_needed_const(&self, mem: &U256, add: usize) -> evm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}
fn mem_needed(&self, offset: &U256, size: &U256) -> evm::Result<Gas> {
if self.is_zero(&try!(Gas::from_u256(*size))) {
return Ok(Gas::from(0));
}
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
}
fn mem_gas_cost(&self, schedule: &evm::Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, usize)> {
let gas_for_mem = |mem_size: Gas| { let gas_for_mem = |mem_size: Gas| {
let s = mem_size >> 5; let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div // s * memory_gas + s * s / quad_coeff_div
let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas))); let a = overflowing!(s.overflow_mul(Gas::from(schedule.memory_gas)));
// Calculate s*s/quad_coeff_div // Calculate s*s/quad_coeff_div
let b = overflowing!(s.overflow_mul_div(s, Gas::from(schedule.quad_coeff_div))); debug_assert_eq!(schedule.quad_coeff_div, 512);
let b = overflowing!(s.overflow_mul_shr(s, 9));
Ok(overflowing!(a.overflow_add(b))) Ok(overflowing!(a.overflow_add(b)))
}; };
let current_mem_size = Gas::from(current_mem_size); 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; let req_mem_size_rounded = (overflowing!(mem_size.overflow_add(Gas::from(31 as usize))) >> 5) << 5;
let mem_gas_cost = if req_mem_size_rounded > current_mem_size { let (mem_gas_cost, new_mem_gas) = if req_mem_size_rounded > current_mem_size {
let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded)); let new_mem_gas = try!(gas_for_mem(req_mem_size_rounded));
let current_mem_gas = try!(gas_for_mem(current_mem_size)); (new_mem_gas - self.current_mem_gas, new_mem_gas)
new_mem_gas - current_mem_gas
} else { } else {
Gas::from(0) (Gas::from(0), self.current_mem_gas)
}; };
Ok((mem_gas_cost, req_mem_size_rounded.as_usize())) Ok((mem_gas_cost, new_mem_gas, req_mem_size_rounded.as_usize()))
} }
} }
#[inline]
fn mem_needed_const<Gas: CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}
#[inline]
fn mem_needed<Gas: CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
if size.is_zero() {
return Ok(Gas::from(0));
}
Gas::from_u256(overflowing!(offset.overflowing_add(*size)))
}
#[inline] #[inline]
fn add_gas_usize<Gas: CostType>(value: Gas, num: usize) -> (Gas, bool) { fn add_gas_usize<Gas: CostType>(value: Gas, num: usize) -> (Gas, bool) {
value.overflow_add(Gas::from(num)) value.overflow_add(Gas::from(num))
@ -251,9 +252,10 @@ fn test_calculate_mem_cost() {
let mem_size = 5; let mem_size = 5;
// when // when
let (mem_cost, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap(); let (mem_cost, new_mem_gas, mem_size) = gasometer.mem_gas_cost(&schedule, current_mem_size, &mem_size).unwrap();
// then // then
assert_eq!(mem_cost, 3); assert_eq!(mem_cost, 3);
assert_eq!(new_mem_gas, 3);
assert_eq!(mem_size, 32); assert_eq!(mem_size, 32);
} }

View File

@ -41,6 +41,7 @@ use common::*;
use types::executed::CallType; use types::executed::CallType;
use super::instructions::{self, Instruction, InstructionInfo}; use super::instructions::{self, Instruction, InstructionInfo};
use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType}; use evm::{self, MessageCallResult, ContractCreateResult, GasLeft, CostType};
use bit_set::BitSet;
#[cfg(feature = "evm-debug")] #[cfg(feature = "evm-debug")]
fn color(instruction: Instruction, name: &'static str) -> String { fn color(instruction: Instruction, name: &'static str) -> String {
@ -115,12 +116,13 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
try!(self.verify_instruction(ext, instruction, &info, &stack)); try!(self.verify_instruction(ext, instruction, &info, &stack));
// Calculate gas cost // Calculate gas cost
let (gas_cost, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size())); let (gas_cost, mem_gas, mem_size) = try!(gasometer.get_gas_cost_mem(ext, instruction, &info, &stack, self.mem.size()));
// TODO: make compile-time removable if too much of a performance hit. // TODO: make compile-time removable if too much of a performance hit.
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256()); let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &gas_cost.as_u256());
try!(gasometer.verify_gas(&gas_cost)); try!(gasometer.verify_gas(&gas_cost));
self.mem.expand(mem_size); self.mem.expand(mem_size);
gasometer.current_mem_gas = mem_gas;
gasometer.current_gas = gasometer.current_gas - gas_cost; gasometer.current_gas = gasometer.current_gas - gas_cost;
evm_debug!({ evm_debug!({
@ -540,10 +542,10 @@ impl<Cost: CostType> Interpreter<Cost> {
} }
} }
fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &HashSet<usize>) -> evm::Result<usize> { fn verify_jump(&self, jump_u: U256, valid_jump_destinations: &BitSet) -> evm::Result<usize> {
let jump = jump_u.low_u64() as usize; let jump = jump_u.low_u64() as usize;
if valid_jump_destinations.contains(&jump) && jump_u < U256::from(!0 as usize) { if valid_jump_destinations.contains(jump) && U256::from(jump) == jump_u {
Ok(jump) Ok(jump)
} else { } else {
Err(evm::Error::BadJumpDestination { Err(evm::Error::BadJumpDestination {
@ -765,8 +767,8 @@ impl<Cost: CostType> Interpreter<Cost> {
Ok(()) Ok(())
} }
fn find_jump_destinations(&self, code: &[u8]) -> HashSet<CodePosition> { fn find_jump_destinations(&self, code: &[u8]) -> BitSet {
let mut jump_dests = HashSet::new(); let mut jump_dests = BitSet::with_capacity(code.len());
let mut position = 0; let mut position = 0;
while position < code.len() { while position < code.len() {
@ -818,5 +820,5 @@ fn test_find_jump_destinations() {
let valid_jump_destinations = interpreter.find_jump_destinations(&code); let valid_jump_destinations = interpreter.find_jump_destinations(&code);
// then // then
assert!(valid_jump_destinations.contains(&66)); assert!(valid_jump_destinations.contains(66));
} }

View File

@ -35,3 +35,4 @@ pub use self::evm::{Evm, Error, Finalize, GasLeft, Result, CostType};
pub use self::ext::{Ext, ContractCreateResult, MessageCallResult}; pub use self::ext::{Ext, ContractCreateResult, MessageCallResult};
pub use self::factory::{Factory, VMType}; pub use self::factory::{Factory, VMType};
pub use self::schedule::Schedule; pub use self::schedule::Schedule;
pub use types::executed::CallType;

View File

@ -125,7 +125,18 @@ impl Schedule {
tx_create_gas: tcg, tx_create_gas: tcg,
tx_data_zero_gas: 4, tx_data_zero_gas: 4,
tx_data_non_zero_gas: 68, tx_data_non_zero_gas: 68,
copy_gas: 3, copy_gas: 3,
} }
} }
} }
#[test]
#[cfg(test)]
fn schedule_evm_assumptions() {
let s1 = Schedule::new_frontier();
let s2 = Schedule::new_homestead();
// To optimize division we assume 2**9 for quad_coeff_div
assert_eq!(s1.quad_coeff_div, 512);
assert_eq!(s2.quad_coeff_div, 512);
}

View File

@ -95,6 +95,7 @@ pub extern crate ethstore;
extern crate semver; extern crate semver;
extern crate ethcore_ipc_nano as nanoipc; extern crate ethcore_ipc_nano as nanoipc;
extern crate ethcore_devtools as devtools; extern crate ethcore_devtools as devtools;
extern crate bit_set;
#[cfg(feature = "jit" )] extern crate evmjit; #[cfg(feature = "jit" )] extern crate evmjit;

20
evmbin/Cargo.lock generated
View File

@ -47,6 +47,19 @@ dependencies = [
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "bit-set"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bit-vec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bit-vec"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "0.3.3" version = "0.3.3"
@ -168,6 +181,7 @@ dependencies = [
name = "ethcore" name = "ethcore"
version = "1.3.0" version = "1.3.0"
dependencies = [ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bloomchain 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@ -253,7 +267,7 @@ dependencies = [
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"rocksdb 0.4.5", "rocksdb 0.4.5 (git+https://github.com/ethcore/rust-rocksdb)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -738,14 +752,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rocksdb" name = "rocksdb"
version = "0.4.5" version = "0.4.5"
source = "git+https://github.com/ethcore/rust-rocksdb#eadce7f74cfe92b99ce63a77af425b47857239b8"
dependencies = [ dependencies = [
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
"rocksdb-sys 0.3.0", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)",
] ]
[[package]] [[package]]
name = "rocksdb-sys" name = "rocksdb-sys"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/ethcore/rust-rocksdb#eadce7f74cfe92b99ce63a77af425b47857239b8"
dependencies = [ dependencies = [
"gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -19,7 +19,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use util::{U256, H256, Address, Bytes, FixedHash}; use util::{U256, H256, Address, Bytes, FixedHash};
use ethcore::client::EnvInfo; use ethcore::client::EnvInfo;
use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule}; use ethcore::evm::{self, Ext, ContractCreateResult, MessageCallResult, Schedule, CallType};
pub struct FakeExt { pub struct FakeExt {
schedule: Schedule, schedule: Schedule,
@ -67,7 +67,8 @@ impl Ext for FakeExt {
_value: Option<U256>, _value: Option<U256>,
_data: &[u8], _data: &[u8],
_code_address: &Address, _code_address: &Address,
_output: &mut [u8]) -> MessageCallResult { _output: &mut [u8],
_call_type: CallType) -> MessageCallResult {
unimplemented!(); unimplemented!();
} }