From 8a5c9a8c709ebb92fbcf7a3d085d6e2a1046fcf9 Mon Sep 17 00:00:00 2001 From: Wei Tang Date: Thu, 30 Aug 2018 01:13:45 +0800 Subject: [PATCH] evmbin: Fix gas_used issue in state root mismatch and handle output better (#9418) * Fix gas used in staterootmismatch, and print full state root hash * Write trace info for stdjson to stderr * Fix tests * Remove struct trait bound --- evmbin/src/display/std_json.rs | 74 +++++++++++++++++++++++----------- evmbin/src/info.rs | 29 ++++++------- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/evmbin/src/display/std_json.rs b/evmbin/src/display/std_json.rs index ebf099bcf..6bbace4a6 100644 --- a/evmbin/src/display/std_json.rs +++ b/evmbin/src/display/std_json.rs @@ -28,44 +28,60 @@ use info as vm; pub trait Writer: io::Write + Send + Sized { fn clone(&self) -> Self; + fn default() -> Self; } impl Writer for io::Stdout { fn clone(&self) -> Self { io::stdout() } + + fn default() -> Self { + io::stdout() + } +} + +impl Writer for io::Stderr { + fn clone(&self) -> Self { + io::stderr() + } + + fn default() -> Self { + io::stderr() + } } /// JSON formatting informant. -pub struct Informant { +pub struct Informant { code: Vec, instruction: u8, depth: usize, stack: Vec, storage: HashMap, - sink: T, + trace_sink: Trace, + out_sink: Out, } impl Default for Informant { fn default() -> Self { - Self::new(io::stdout()) + Self::new(io::stderr(), io::stdout()) } } -impl Informant { - pub fn new(sink: T) -> Self { +impl Informant { + pub fn new(trace_sink: Trace, out_sink: Out) -> Self { Informant { code: Default::default(), instruction: Default::default(), depth: Default::default(), stack: Default::default(), storage: Default::default(), - sink, + trace_sink, out_sink } } } -impl Informant { +impl Informant { fn stack(&self) -> String { let items = self.stack.iter().map(|i| format!("\"0x{:x}\"", i)).collect::>(); format!("[{}]", items.join(",")) @@ -79,10 +95,10 @@ impl Informant { } } -impl vm::Informant for Informant { +impl vm::Informant for Informant { fn before_test(&mut self, name: &str, action: &str) { writeln!( - &mut self.sink, + &mut self.out_sink, "{{\"test\":\"{name}\",\"action\":\"{action}\"}}", name = name, action = action, @@ -91,30 +107,38 @@ impl vm::Informant for Informant { fn set_gas(&mut self, _gas: U256) {} - fn finish(result: vm::RunResult) { + fn finish(result: vm::RunResult<::Output>) { + let mut trace_sink = Trace::default(); + let mut out_sink = Out::default(); + match result { Ok(success) => { - println!("{{\"stateRoot\":\"{:?}\"}}", success.state_root); - println!( - "{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", + writeln!( + &mut trace_sink, + "{{\"stateRoot\":\"{:?}\"}}", success.state_root + ).expect("The sink must be writeable."); + writeln!( + &mut out_sink, + "{{\"output\":\"0x{output}\",\"gasUsed\":\"0x{gas:x}\",\"time\":{time}}}", output = success.output.to_hex(), gas = success.gas_used, time = display::as_micros(&success.time), - ); + ).expect("The sink must be writeable."); }, Err(failure) => { - println!( - "{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", + writeln!( + &mut out_sink, + "{{\"error\":\"{error}\",\"gasUsed\":\"0x{gas:x}\",\"time\":{time}}}", error = failure.error, gas = failure.gas_used, time = display::as_micros(&failure.time), - ) + ).expect("The sink must be writeable."); }, } } } -impl trace::VMTracer for Informant { +impl trace::VMTracer for Informant { type Output = (); fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { @@ -124,7 +148,7 @@ impl trace::VMTracer for Informant { let stack = self.stack(); writeln!( - &mut self.sink, + &mut self.trace_sink, "{{\"pc\":{pc},\"op\":{op},\"opName\":\"{name}\",\"gas\":\"0x{gas:x}\",\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}", pc = pc, op = instruction, @@ -155,7 +179,7 @@ impl trace::VMTracer for Informant { } fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized { - let mut vm = Informant::new(self.sink.clone()); + let mut vm = Informant::new(self.trace_sink.clone(), self.out_sink.clone()); vm.depth = self.depth + 1; vm.code = code.to_vec(); vm @@ -177,6 +201,7 @@ pub mod tests { impl Writer for TestWriter { fn clone(&self) -> Self { Clone::clone(self) } + fn default() -> Self { Default::default() } } impl io::Write for TestWriter { @@ -189,10 +214,11 @@ pub mod tests { } } - pub fn informant() -> (Informant, Arc>>) { - let writer = TestWriter::default(); - let res = writer.0.clone(); - (Informant::new(writer), res) + pub fn informant() -> (Informant, Arc>>) { + let trace_writer: TestWriter = Default::default(); + let out_writer: TestWriter = Default::default(); + let res = trace_writer.0.clone(); + (Informant::new(trace_writer, out_writer), res) } #[test] diff --git a/evmbin/src/info.rs b/evmbin/src/info.rs index 080c0c7ff..d9ee6729e 100644 --- a/evmbin/src/info.rs +++ b/evmbin/src/info.rs @@ -81,10 +81,11 @@ pub fn run_action( } } run(spec, params.gas, spec.genesis_state(), |mut client| { - let result = client - .call(params, &mut trace::NoopTracer, &mut informant) - .map(|r| (0.into(), r.gas_left, r.return_data.to_vec())); - (result, informant.drain()) + let result = match client.call(params, &mut trace::NoopTracer, &mut informant) { + Ok(r) => (Ok((0.into(), r.return_data.to_vec())), Some(r.gas_left)), + Err(err) => (Err(err), None), + }; + (result.0, result.1, informant.drain()) }) } @@ -116,20 +117,20 @@ pub fn run_transaction( let result = run(&spec, env_info.gas_limit, pre_state, |mut client| { let result = client.transact(env_info, transaction, trace::NoopTracer, informant); match result { - TransactResult::Ok { state_root, .. } if state_root != post_root => { + TransactResult::Ok { state_root, gas_left, .. } if state_root != post_root => { (Err(EvmTestError::PostCondition(format!( - "State root mismatch (got: {}, expected: {})", + "State root mismatch (got: 0x{:x}, expected: 0x{:x})", state_root, post_root, - ))), None) + ))), Some(gas_left), None) }, TransactResult::Ok { state_root, gas_left, output, vm_trace, .. } => { - (Ok((state_root, gas_left, output)), vm_trace) + (Ok((state_root, output)), Some(gas_left), vm_trace) }, TransactResult::Err { error, .. } => { (Err(EvmTestError::PostCondition(format!( "Unexpected execution error: {:?}", error - ))), None) + ))), None, None) }, } }); @@ -144,7 +145,7 @@ pub fn run<'a, F, X>( pre_state: &'a pod_state::PodState, run: F, ) -> RunResult where - F: FnOnce(EvmTestClient) -> (Result<(H256, U256, Vec), EvmTestError>, Option), + F: FnOnce(EvmTestClient) -> (Result<(H256, Vec), EvmTestError>, Option, Option), { let test_client = EvmTestClient::from_pod_state(spec, pre_state.clone()) .map_err(|error| Failure { @@ -159,15 +160,15 @@ pub fn run<'a, F, X>( let time = start.elapsed(); match result { - (Ok((state_root, gas_left, output)), traces) => Ok(Success { + (Ok((state_root, output)), gas_left, traces) => Ok(Success { state_root, - gas_used: initial_gas - gas_left, + gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas), output, time, traces, }), - (Err(error), traces) => Err(Failure { - gas_used: initial_gas, + (Err(error), gas_left, traces) => Err(Failure { + gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas), error, time, traces,