Gas for mem optimization (#1768)
* Optimizing mem_gas_cost calculations * Memoizing current mem gas cost * Optimizing jump destinations
This commit is contained in:
parent
85c471b905
commit
f56b89010d
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -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)",
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
@ -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
20
evmbin/Cargo.lock
generated
@ -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)",
|
||||||
|
@ -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!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user