diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index ec1b1715e99..de75908b36f 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -26,10 +26,10 @@ use trace::{FlatTrace, Tracer, NoopTracer, ExecutiveTracer, VMTrace, VMTracer, E use crossbeam; pub use types::executed::{Executed, ExecutionResult}; -/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) +/// Roughly estimate what stack size each level of evm depth will use /// TODO [todr] We probably need some more sophisticated calculations here (limit on my machine 132) /// Maybe something like here: `https://github.com/ethereum/libethereum/blob/4db169b8504f2b87f7d5a481819cfb959fc65f6c/libethereum/ExtVM.cpp` -const MAX_VM_DEPTH_FOR_THREAD: usize = 64; +const STACK_SIZE_PER_DEPTH: usize = 24*1024; /// Returns new address created from address and given nonce. pub fn contract_address(address: &Address, nonce: &U256) -> Address { @@ -148,12 +148,13 @@ impl<'a> Executive<'a> { // TODO: we might need bigints here, or at least check overflows. let balance = self.state.balance(&sender); - let gas_cost = U512::from(t.gas) * U512::from(t.gas_price); + let gas_cost = t.gas.full_mul(t.gas_price); let total_cost = U512::from(t.value) + gas_cost; // avoid unaffordable transactions - if U512::from(balance) < total_cost { - return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: U512::from(balance) })); + let balance512 = U512::from(balance); + if balance512 < total_cost { + return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, got: balance512 })); } // NOTE: there can be no invalid transactions from this point. @@ -212,8 +213,11 @@ impl<'a> Executive<'a> { tracer: &mut T, vm_tracer: &mut V ) -> evm::Result where T: Tracer, V: VMTracer { + + let depth_threshold = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get() / STACK_SIZE_PER_DEPTH); + // Ordinary execution - keep VM in same thread - if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { + if (self.depth + 1) % depth_threshold != 0 { let vm_factory = self.vm_factory; let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy, tracer, vm_tracer); trace!(target: "executive", "ext.schedule.have_delegate_call: {}", ext.schedule().have_delegate_call); @@ -413,7 +417,7 @@ impl<'a> Executive<'a> { // real ammount to refund let gas_left_prerefund = match result { Ok(x) => x, _ => 0.into() }; - let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) / U256::from(2)); + let refunded = cmp::min(refunds_bound, (t.gas - gas_left_prerefund) >> 1); let gas_left = gas_left_prerefund + refunded; let gas_used = t.gas - gas_left; diff --git a/util/io/src/lib.rs b/util/io/src/lib.rs index b2a16e19bb2..082192dfaf0 100644 --- a/util/io/src/lib.rs +++ b/util/io/src/lib.rs @@ -68,6 +68,8 @@ mod panics; use mio::{EventLoop, Token}; use std::fmt; +pub use worker::LOCAL_STACK_SIZE; + #[derive(Debug)] /// IO Error pub enum IoError { diff --git a/util/io/src/worker.rs b/util/io/src/worker.rs index 0176c467cad..f4f63919f14 100644 --- a/util/io/src/worker.rs +++ b/util/io/src/worker.rs @@ -22,9 +22,19 @@ use crossbeam::sync::chase_lev; use service::{HandlerId, IoChannel, IoContext}; use IoHandler; use panics::*; +use std::cell::Cell; use std::sync::{Condvar as SCondvar, Mutex as SMutex}; +const STACK_SIZE: usize = 16*1024*1024; + +thread_local! { + /// Stack size + /// Should be modified if it is changed in Rust since it is no way + /// to know or get it + pub static LOCAL_STACK_SIZE: Cell = Cell::new(::std::env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()).unwrap_or(2 * 1024 * 1024)); +} + pub enum WorkType { Readable, Writable, @@ -66,8 +76,9 @@ impl Worker { deleting: deleting.clone(), wait_mutex: wait_mutex.clone(), }; - worker.thread = Some(thread::Builder::new().name(format!("IO Worker #{}", index)).spawn( + worker.thread = Some(thread::Builder::new().stack_size(STACK_SIZE).name(format!("IO Worker #{}", index)).spawn( move || { + LOCAL_STACK_SIZE.with(|val| val.set(STACK_SIZE)); panic_handler.catch_panic(move || { Worker::work_loop(stealer, channel.clone(), wait, wait_mutex.clone(), deleting) }).unwrap()