From c66aa52166ab04f51ca90ded67574070e9357622 Mon Sep 17 00:00:00 2001 From: Tomusdrw Date: Tue, 26 Jan 2016 10:15:55 +0100 Subject: [PATCH] Spawning new thread when we are reaching stack limit --- Cargo.toml | 1 + src/evm/jit.rs | 19 ++++++++++++------- src/evm/tests.rs | 1 + src/executive.rs | 36 +++++++++++++++++++++++++++++------- src/lib.rs | 1 + src/tests/executive.rs | 4 ++-- src/tests/state.rs | 10 +++++----- 7 files changed, 51 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 54b1b406e..ce49d0dd4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ num_cpus = "0.2" docopt = "0.6" docopt_macros = "0.6" ctrlc = "1.0" +crossbeam = "0.1.5" clippy = "0.0.37" [features] diff --git a/src/evm/jit.rs b/src/evm/jit.rs index 9f990155d..e073a380d 100644 --- a/src/evm/jit.rs +++ b/src/evm/jit.rs @@ -64,7 +64,7 @@ impl IntoJit for H256 { for i in 0..self.bytes().len() { let rev = self.bytes().len() - 1 - i; let pos = rev / 8; - ret[pos] += (self.bytes()[i] as u64) << (rev % 8) * 8; + ret[pos] += (self.bytes()[i] as u64) << ((rev % 8) * 8); } evmjit::I256 { words: ret } } @@ -218,9 +218,11 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { } } - match self.ext.call(&call_gas, + match self.ext.call( + &call_gas, + &self.address, &receive_address, - &value, + Some(value), unsafe { slice::from_raw_parts(in_beg, in_size as usize) }, &code_address, unsafe { slice::from_raw_parts_mut(out_beg, out_size as usize) }) { @@ -262,7 +264,7 @@ impl<'a> evmjit::Ext for ExtAdapter<'a> { } let bytes_ref: &[u8] = slice::from_raw_parts(beg, size as usize); - self.ext.log(topics, bytes_ref.to_vec()); + self.ext.log(topics, bytes_ref); } } @@ -287,8 +289,8 @@ impl evm::Evm for JitEvm { assert!(params.gas <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); assert!(params.gas_price <= U256::from(i64::max_value() as u64), "evmjit max gas is 2 ^ 63"); - let call_data = params.data.unwrap_or(vec![]); - let code = params.code.unwrap_or(vec![]); + let call_data = params.data.unwrap_or_else(Vec::new); + let code = params.code.unwrap_or_else(Vec::new); let mut data = evmjit::RuntimeDataHandle::new(); data.gas = params.gas.low_u64() as i64; @@ -303,7 +305,10 @@ impl evm::Evm for JitEvm { data.address = params.address.into_jit(); data.caller = params.sender.into_jit(); data.origin = params.origin.into_jit(); - data.call_value = params.value.into_jit(); + data.call_value = match params.value { + ActionValue::Transfer(val) => val.into_jit(), + ActionValue::Apparent(val) => val.into_jit() + }; data.author = ext.env_info().author.clone().into_jit(); data.difficulty = ext.env_info().difficulty.into_jit(); diff --git a/src/evm/tests.rs b/src/evm/tests.rs index ad81cf877..d448ccb3a 100644 --- a/src/evm/tests.rs +++ b/src/evm/tests.rs @@ -215,6 +215,7 @@ fn test_origin(factory: super::Factory) { assert_eq!(ext.store.get(&H256::new()).unwrap(), &H256::from_str("000000000000000000000000cd1722f2947def4cf144679da39c4c32bdc35681").unwrap()); } +// TODO [todr] Fails with Signal 11 on JIT evm_test!{test_sender: test_sender_jit, test_sender_int} fn test_sender(factory: super::Factory) { let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); diff --git a/src/executive.rs b/src/executive.rs index f5952e530..d5446b0e9 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -5,6 +5,12 @@ use engine::*; use evm::{self, Ext}; use externalities::*; use substate::*; +use crossbeam; + +/// Max depth to avoid stack overflow (when it's reached we start a new thread with VM) +/// 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 = 128; /// Returns new address created from address and given nonce. pub fn contract_address(address: &Address, nonce: &U256) -> Address { @@ -161,12 +167,32 @@ impl<'a> Executive<'a> { Ok(try!(self.finalize(t, substate, res))) } + fn exec_vm(&mut self, params: ActionParams, unconfirmed_substate: &mut Substate, output_policy: OutputPolicy) -> evm::Result { + // Ordinary execution - keep VM in same thread + if (self.depth + 1) % MAX_VM_DEPTH_FOR_THREAD != 0 { + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy); + let vm_factory = self.engine.vm_factory(); + return vm_factory.create().exec(params, &mut ext); + } + + // Start in new thread to reset stack + // TODO [todr] No thread builder yet, so we need to reset once for a while + // https://github.com/aturon/crossbeam/issues/16 + crossbeam::scope(|scope| { + let mut ext = self.as_externalities(OriginInfo::from(¶ms), unconfirmed_substate, output_policy); + let vm_factory = self.engine.vm_factory(); + + scope.spawn(move || { + vm_factory.create().exec(params, &mut ext) + }) + }).join() + } + /// Calls contract function with given contract params. /// NOTE. It does not finalize the transaction (doesn't do refunds, nor suicides). /// Modifies the substate and the output. /// Returns either gas_left or `evm::Error`. pub fn call(&mut self, params: ActionParams, substate: &mut Substate, mut output: BytesRef) -> evm::Result { - println!("Calling executive. Sender: {}", params.sender); // backup used in case of running out of gas let backup = self.state.clone(); @@ -201,8 +227,7 @@ impl<'a> Executive<'a> { let mut unconfirmed_substate = Substate::new(); let res = { - let mut ext = self.as_externalities(OriginInfo::from(¶ms), &mut unconfirmed_substate, OutputPolicy::Return(output)); - self.engine.vm_factory().create().exec(params, &mut ext) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output)) }; trace!("exec: sstore-clears={}\n", unconfirmed_substate.sstore_clears_count); @@ -235,8 +260,7 @@ impl<'a> Executive<'a> { } let res = { - let mut ext = self.as_externalities(OriginInfo::from(¶ms), &mut unconfirmed_substate, OutputPolicy::InitContract); - self.engine.vm_factory().create().exec(params, &mut ext) + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract) }; self.enact_result(&res, substate, unconfirmed_substate, backup); res @@ -277,7 +301,6 @@ impl<'a> Executive<'a> { match result { Err(evm::Error::Internal) => Err(ExecutionError::Internal), - // TODO [ToDr] BadJumpDestination @debris - how to handle that? Err(_) => { Ok(Executed { gas: t.gas, @@ -302,7 +325,6 @@ impl<'a> Executive<'a> { } fn enact_result(&mut self, result: &evm::Result, substate: &mut Substate, un_substate: Substate, backup: State) { - // TODO: handle other evm::Errors same as OutOfGas once they are implemented match *result { Err(evm::Error::OutOfGas) | Err(evm::Error::BadJumpDestination {..}) diff --git a/src/lib.rs b/src/lib.rs index 262f8682f..8dd02b3bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,6 +90,7 @@ extern crate num_cpus; extern crate evmjit; #[macro_use] extern crate ethcore_util as util; +extern crate crossbeam; // NOTE: Add doc parser exception for these pub declarations. diff --git a/src/tests/executive.rs b/src/tests/executive.rs index a94fd1605..1df1b7eec 100644 --- a/src/tests/executive.rs +++ b/src/tests/executive.rs @@ -271,8 +271,8 @@ fn do_json_test_for(vm: &VMType, json_data: &[u8]) -> Vec { declare_test!{ExecutiveTests_vmArithmeticTest, "VMTests/vmArithmeticTest"} declare_test!{ExecutiveTests_vmBitwiseLogicOperationTest, "VMTests/vmBitwiseLogicOperationTest"} -// this one crashes with some vm internal error. Separately they pass. -declare_test!{ignore => ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} +declare_test!{ExecutiveTests_vmBlockInfoTest, "VMTests/vmBlockInfoTest"} + // TODO [todr] Fails with Signal 11 when using JIT declare_test!{ExecutiveTests_vmEnvironmentalInfoTest, "VMTests/vmEnvironmentalInfoTest"} declare_test!{ExecutiveTests_vmIOandFlowOperationsTest, "VMTests/vmIOandFlowOperationsTest"} declare_test!{heavy => ExecutiveTests_vmInputLimits, "VMTests/vmInputLimits"} diff --git a/src/tests/state.rs b/src/tests/state.rs index 75b9b2493..325a8b646 100644 --- a/src/tests/state.rs +++ b/src/tests/state.rs @@ -73,7 +73,7 @@ fn do_json_test(json_data: &[u8]) -> Vec { declare_test!{StateTests_stBlockHashTest, "StateTests/stBlockHashTest"} declare_test!{StateTests_stCallCodes, "StateTests/stCallCodes"} -declare_test!{ignore => StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} //<< Out of stack +declare_test!{StateTests_stCallCreateCallCodeTest, "StateTests/stCallCreateCallCodeTest"} declare_test!{StateTests_stDelegatecallTest, "StateTests/stDelegatecallTest"} declare_test!{StateTests_stExample, "StateTests/stExample"} declare_test!{StateTests_stInitCodeTest, "StateTests/stInitCodeTest"} @@ -81,12 +81,12 @@ declare_test!{StateTests_stLogTests, "StateTests/stLogTests"} declare_test!{heavy => StateTests_stMemoryStressTest, "StateTests/stMemoryStressTest"} declare_test!{heavy => StateTests_stMemoryTest, "StateTests/stMemoryTest"} declare_test!{StateTests_stPreCompiledContracts, "StateTests/stPreCompiledContracts"} -declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"} //<< Too long -declare_test!{ignore => StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"} //<< Out of stack +declare_test!{heavy => StateTests_stQuadraticComplexityTest, "StateTests/stQuadraticComplexityTest"} +declare_test!{StateTests_stRecursiveCreate, "StateTests/stRecursiveCreate"} declare_test!{StateTests_stRefundTest, "StateTests/stRefundTest"} declare_test!{StateTests_stSolidityTest, "StateTests/stSolidityTest"} -declare_test!{ignore => StateTests_stSpecialTest, "StateTests/stSpecialTest"} //<< Out of Stack -declare_test!{ignore => StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"} //<< Out of stack +declare_test!{StateTests_stSpecialTest, "StateTests/stSpecialTest"} +declare_test!{StateTests_stSystemOperationsTest, "StateTests/stSystemOperationsTest"} declare_test!{StateTests_stTransactionTest, "StateTests/stTransactionTest"} declare_test!{StateTests_stTransitionTest, "StateTests/stTransitionTest"} declare_test!{StateTests_stWalletTest, "StateTests/stWalletTest"}