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
This commit is contained in:
Wei Tang 2018-08-30 01:13:45 +08:00 committed by Andronik Ordian
parent 3e4a525520
commit 8a5c9a8c70
2 changed files with 65 additions and 38 deletions

View File

@ -28,44 +28,60 @@ use info as vm;
pub trait Writer: io::Write + Send + Sized { pub trait Writer: io::Write + Send + Sized {
fn clone(&self) -> Self; fn clone(&self) -> Self;
fn default() -> Self;
} }
impl Writer for io::Stdout { impl Writer for io::Stdout {
fn clone(&self) -> Self { fn clone(&self) -> Self {
io::stdout() 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. /// JSON formatting informant.
pub struct Informant<T: Writer = io::Stdout> { pub struct Informant<Trace = io::Stderr, Out = io::Stdout> {
code: Vec<u8>, code: Vec<u8>,
instruction: u8, instruction: u8,
depth: usize, depth: usize,
stack: Vec<U256>, stack: Vec<U256>,
storage: HashMap<H256, H256>, storage: HashMap<H256, H256>,
sink: T, trace_sink: Trace,
out_sink: Out,
} }
impl Default for Informant { impl Default for Informant {
fn default() -> Self { fn default() -> Self {
Self::new(io::stdout()) Self::new(io::stderr(), io::stdout())
} }
} }
impl<T: Writer> Informant<T> { impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
pub fn new(sink: T) -> Self { pub fn new(trace_sink: Trace, out_sink: Out) -> Self {
Informant { Informant {
code: Default::default(), code: Default::default(),
instruction: Default::default(), instruction: Default::default(),
depth: Default::default(), depth: Default::default(),
stack: Default::default(), stack: Default::default(),
storage: Default::default(), storage: Default::default(),
sink, trace_sink, out_sink
} }
} }
} }
impl<T: Writer> Informant<T> { impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
fn stack(&self) -> String { fn stack(&self) -> String {
let items = self.stack.iter().map(|i| format!("\"0x{:x}\"", i)).collect::<Vec<_>>(); let items = self.stack.iter().map(|i| format!("\"0x{:x}\"", i)).collect::<Vec<_>>();
format!("[{}]", items.join(",")) format!("[{}]", items.join(","))
@ -79,10 +95,10 @@ impl<T: Writer> Informant<T> {
} }
} }
impl<T: Writer> vm::Informant for Informant<T> { impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
fn before_test(&mut self, name: &str, action: &str) { fn before_test(&mut self, name: &str, action: &str) {
writeln!( writeln!(
&mut self.sink, &mut self.out_sink,
"{{\"test\":\"{name}\",\"action\":\"{action}\"}}", "{{\"test\":\"{name}\",\"action\":\"{action}\"}}",
name = name, name = name,
action = action, action = action,
@ -91,30 +107,38 @@ impl<T: Writer> vm::Informant for Informant<T> {
fn set_gas(&mut self, _gas: U256) {} fn set_gas(&mut self, _gas: U256) {}
fn finish(result: vm::RunResult<Self::Output>) { fn finish(result: vm::RunResult<<Self as trace::VMTracer>::Output>) {
let mut trace_sink = Trace::default();
let mut out_sink = Out::default();
match result { match result {
Ok(success) => { Ok(success) => {
println!("{{\"stateRoot\":\"{:?}\"}}", success.state_root); writeln!(
println!( &mut trace_sink,
"{{\"output\":\"0x{output}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", "{{\"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(), output = success.output.to_hex(),
gas = success.gas_used, gas = success.gas_used,
time = display::as_micros(&success.time), time = display::as_micros(&success.time),
); ).expect("The sink must be writeable.");
}, },
Err(failure) => { Err(failure) => {
println!( writeln!(
"{{\"error\":\"{error}\",\"gasUsed\":\"{gas:x}\",\"time\":{time}}}", &mut out_sink,
"{{\"error\":\"{error}\",\"gasUsed\":\"0x{gas:x}\",\"time\":{time}}}",
error = failure.error, error = failure.error,
gas = failure.gas_used, gas = failure.gas_used,
time = display::as_micros(&failure.time), time = display::as_micros(&failure.time),
) ).expect("The sink must be writeable.");
}, },
} }
} }
} }
impl<T: Writer> trace::VMTracer for Informant<T> { impl<Trace: Writer, Out: Writer> trace::VMTracer for Informant<Trace, Out> {
type Output = (); type Output = ();
fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool { fn trace_next_instruction(&mut self, pc: usize, instruction: u8, current_gas: U256) -> bool {
@ -124,7 +148,7 @@ impl<T: Writer> trace::VMTracer for Informant<T> {
let stack = self.stack(); let stack = self.stack();
writeln!( 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\":{op},\"opName\":\"{name}\",\"gas\":\"0x{gas:x}\",\"stack\":{stack},\"storage\":{storage},\"depth\":{depth}}}",
pc = pc, pc = pc,
op = instruction, op = instruction,
@ -155,7 +179,7 @@ impl<T: Writer> trace::VMTracer for Informant<T> {
} }
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized { 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.depth = self.depth + 1;
vm.code = code.to_vec(); vm.code = code.to_vec();
vm vm
@ -177,6 +201,7 @@ pub mod tests {
impl Writer for TestWriter { impl Writer for TestWriter {
fn clone(&self) -> Self { Clone::clone(self) } fn clone(&self) -> Self { Clone::clone(self) }
fn default() -> Self { Default::default() }
} }
impl io::Write for TestWriter { impl io::Write for TestWriter {
@ -189,10 +214,11 @@ pub mod tests {
} }
} }
pub fn informant() -> (Informant<TestWriter>, Arc<Mutex<Vec<u8>>>) { pub fn informant() -> (Informant<TestWriter, TestWriter>, Arc<Mutex<Vec<u8>>>) {
let writer = TestWriter::default(); let trace_writer: TestWriter = Default::default();
let res = writer.0.clone(); let out_writer: TestWriter = Default::default();
(Informant::new(writer), res) let res = trace_writer.0.clone();
(Informant::new(trace_writer, out_writer), res)
} }
#[test] #[test]

View File

@ -81,10 +81,11 @@ pub fn run_action<T: Informant>(
} }
} }
run(spec, params.gas, spec.genesis_state(), |mut client| { run(spec, params.gas, spec.genesis_state(), |mut client| {
let result = client let result = match client.call(params, &mut trace::NoopTracer, &mut informant) {
.call(params, &mut trace::NoopTracer, &mut informant) Ok(r) => (Ok((0.into(), r.return_data.to_vec())), Some(r.gas_left)),
.map(|r| (0.into(), r.gas_left, r.return_data.to_vec())); Err(err) => (Err(err), None),
(result, informant.drain()) };
(result.0, result.1, informant.drain())
}) })
} }
@ -116,20 +117,20 @@ pub fn run_transaction<T: Informant>(
let result = run(&spec, env_info.gas_limit, pre_state, |mut client| { let result = run(&spec, env_info.gas_limit, pre_state, |mut client| {
let result = client.transact(env_info, transaction, trace::NoopTracer, informant); let result = client.transact(env_info, transaction, trace::NoopTracer, informant);
match result { 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!( (Err(EvmTestError::PostCondition(format!(
"State root mismatch (got: {}, expected: {})", "State root mismatch (got: 0x{:x}, expected: 0x{:x})",
state_root, state_root,
post_root, post_root,
))), None) ))), Some(gas_left), None)
}, },
TransactResult::Ok { state_root, gas_left, output, vm_trace, .. } => { 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, .. } => { TransactResult::Err { error, .. } => {
(Err(EvmTestError::PostCondition(format!( (Err(EvmTestError::PostCondition(format!(
"Unexpected execution error: {:?}", error "Unexpected execution error: {:?}", error
))), None) ))), None, None)
}, },
} }
}); });
@ -144,7 +145,7 @@ pub fn run<'a, F, X>(
pre_state: &'a pod_state::PodState, pre_state: &'a pod_state::PodState,
run: F, run: F,
) -> RunResult<X> where ) -> RunResult<X> where
F: FnOnce(EvmTestClient) -> (Result<(H256, U256, Vec<u8>), EvmTestError>, Option<X>), F: FnOnce(EvmTestClient) -> (Result<(H256, Vec<u8>), EvmTestError>, Option<U256>, Option<X>),
{ {
let test_client = EvmTestClient::from_pod_state(spec, pre_state.clone()) let test_client = EvmTestClient::from_pod_state(spec, pre_state.clone())
.map_err(|error| Failure { .map_err(|error| Failure {
@ -159,15 +160,15 @@ pub fn run<'a, F, X>(
let time = start.elapsed(); let time = start.elapsed();
match result { match result {
(Ok((state_root, gas_left, output)), traces) => Ok(Success { (Ok((state_root, output)), gas_left, traces) => Ok(Success {
state_root, state_root,
gas_used: initial_gas - gas_left, gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
output, output,
time, time,
traces, traces,
}), }),
(Err(error), traces) => Err(Failure { (Err(error), gas_left, traces) => Err(Failure {
gas_used: initial_gas, gas_used: gas_left.map(|gas_left| initial_gas - gas_left).unwrap_or(initial_gas),
error, error,
time, time,
traces, traces,