diff --git a/ethcore/src/executive.rs b/ethcore/src/executive.rs index c862545f4..8c34b8e39 100644 --- a/ethcore/src/executive.rs +++ b/ethcore/src/executive.rs @@ -246,41 +246,49 @@ impl<'a> Executive<'a> { Err(evm::Error::OutOfGas) } } - } else if params.code.is_some() { + } else { // if destination is a contract, do normal message call - // don't trace is it's DELEGATECALL or CALLCODE. + // don't trace if it's DELEGATECALL or CALLCODE. let should_trace = if let ActionValue::Transfer(_) = params.value { - params.code_address == params.address + params.code_address == params.address && substate.subtraces.is_some() } else { false }; - // part of substate that may be reverted - let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); - // transaction tracing stuff. None if there's no tracing. - let mut trace_info = if should_trace { substate.subtraces.as_ref().map(|_| (TraceAction::from_call(¶ms), self.depth)) } else { None }; - let mut trace_output = trace_info.as_ref().map(|_| vec![]); + let (mut trace_info, mut trace_output) = if should_trace { + (Some((TraceAction::from_call(¶ms), self.depth)), Some(vec![])) + } else { (None, None) }; - let res = { - self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut())) - }; + if params.code.is_some() { + // part of substate that may be reverted + let mut unconfirmed_substate = Substate::new(substate.subtraces.is_some()); - trace!(target: "executive", "res={:?}", res); + let res = { + self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut())) + }; - // if there's tracing, make up trace_info's result with trace_output and some arithmetic. - if let Some((TraceAction::Call(ref mut c), _)) = trace_info { - c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, trace_output.expect("trace_info is Some: qed"))); + trace!(target: "executive", "res={:?}", res); + + // if there's tracing, make up trace_info's result with trace_output and some arithmetic. + if let Some((TraceAction::Call(ref mut c), _)) = trace_info { + c.result = res.as_ref().ok().map(|gas_left| (c.gas - *gas_left, trace_output.expect("trace_info is Some: so should_trace: qed"))); + } + + trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); + + self.enact_result(&res, substate, unconfirmed_substate, trace_info); + trace!(target: "executive", "enacted: substate={:?}\n", substate); + res + } else { + // otherwise it's just a basic transaction, only do tracing, if necessary. + trace!(target: "executive", "Basic message (send funds) should_trace={}", should_trace); + self.state.clear_snapshot(); + if let Some((TraceAction::Call(ref mut c), _)) = trace_info { + c.result = Some((x!(0), vec![])); + } + substate.accrue_trace(if should_trace {Some(vec![])} else {None}, trace_info); + Ok(x!(0)) } - - trace!(target: "executive", "substate={:?}; unconfirmed_substate={:?}\n", substate, unconfirmed_substate); - - self.enact_result(&res, substate, unconfirmed_substate, trace_info); - trace!(target: "executive", "enacted: substate={:?}\n", substate); - res - } else { - // otherwise, nothing - self.state.clear_snapshot(); - Ok(params.gas) } } @@ -537,6 +545,7 @@ mod tests { //let next_address = contract_address(&address, &U256::zero()); let mut params = ActionParams::default(); params.address = address.clone(); + params.code_address = address.clone(); params.sender = sender.clone(); params.origin = sender.clone(); params.gas = U256::from(100_000); @@ -634,6 +643,7 @@ mod tests { assert_eq!(substate.subtraces, expected_trace); assert_eq!(gas_left, U256::from(96_776)); } + evm_test!{test_create_contract_value_too_high: test_create_contract_value_too_high_jit, test_create_contract_value_too_high_int} fn test_create_contract_value_too_high(factory: Factory) { // code: diff --git a/ethcore/src/state.rs b/ethcore/src/state.rs index c2924d1bb..7f8454a85 100644 --- a/ethcore/src/state.rs +++ b/ethcore/src/state.rs @@ -469,6 +469,44 @@ fn should_trace_call_transaction() { assert_eq!(result.trace, expected_trace); } +#[test] +fn should_trace_basic_call_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = x!(1_000_000); + let engine = TestEngine::new(5, Factory::default()); + + let t = Transaction { + nonce: x!(0), + gas_price: x!(0), + gas: x!(100_000), + action: Action::Call(x!(0xa)), + value: x!(100), + data: vec![], + }.sign(&"".sha3()); + + state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = Some(Trace { + depth: 0, + action: TraceAction::Call(TraceCall { + from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), + to: x!(0xa), + value: x!(100), + gas: x!(79000), + input: vec![], + result: Some((x!(0), vec![])) + }), + subs: vec![] + }); + + assert_eq!(result.trace, expected_trace); +} + #[test] fn should_not_trace_call_transaction_to_builtin() { init_log(); @@ -705,6 +743,95 @@ fn should_trace_call_with_subcall_transaction() { assert_eq!(result.trace, expected_trace); } +#[test] +fn should_trace_call_with_basic_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = x!(1_000_000); + let engine = TestEngine::new(5, Factory::default()); + + let t = Transaction { + nonce: x!(0), + gas_price: x!(0), + gas: x!(100_000), + action: Action::Call(x!(0xa)), + value: x!(100), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&x!(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()); + state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = Some(Trace { + depth: 0, + action: TraceAction::Call(TraceCall { + from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), + to: x!(0xa), + value: x!(100), + gas: x!(79000), + input: vec![], + result: Some((x!(34061), vec![])) + }), + subs: vec![Trace { + depth: 1, + action: TraceAction::Call(TraceCall { + from: x!(0xa), + to: x!(0xb), + value: x!(69), + gas: x!(2300), + input: vec![], + result: Some((x!(0), vec![])) + }), + subs: vec![] + }] + }); + + assert_eq!(result.trace, expected_trace); +} + +#[test] +fn should_not_trace_call_with_invalid_basic_subcall_transaction() { + init_log(); + + let temp = RandomTempPath::new(); + let mut state = get_temp_state_in(temp.as_path()); + + let mut info = EnvInfo::default(); + info.gas_limit = x!(1_000_000); + let engine = TestEngine::new(5, Factory::default()); + + let t = Transaction { + nonce: x!(0), + gas_price: x!(0), + gas: x!(100_000), + action: Action::Call(x!(0xa)), + value: x!(100), + data: vec![], + }.sign(&"".sha3()); + + state.init_code(&x!(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()); // not enough funds. + state.add_balance(t.sender().as_ref().unwrap(), &x!(100)); + let result = state.apply(&info, &engine, &t, true).unwrap(); + let expected_trace = Some(Trace { + depth: 0, + action: TraceAction::Call(TraceCall { + from: x!("9cce34f7ab185c7aba1b7c8140d620b4bda941d6"), + to: x!(0xa), + value: x!(100), + gas: x!(79000), + input: vec![], + result: Some((x!(31761), vec![])) + }), + subs: vec![] + }); + + assert_eq!(result.trace, expected_trace); +} + #[test] fn should_trace_failed_subcall_transaction() { init_log();