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:
		
							parent
							
								
									3e4a525520
								
							
						
					
					
						commit
						8a5c9a8c70
					
				@ -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<T: Writer = io::Stdout> {
 | 
			
		||||
pub struct Informant<Trace = io::Stderr, Out = io::Stdout> {
 | 
			
		||||
	code: Vec<u8>,
 | 
			
		||||
	instruction: u8,
 | 
			
		||||
	depth: usize,
 | 
			
		||||
	stack: Vec<U256>,
 | 
			
		||||
	storage: HashMap<H256, H256>,
 | 
			
		||||
	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<T: Writer> Informant<T> {
 | 
			
		||||
	pub fn new(sink: T) -> Self {
 | 
			
		||||
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
 | 
			
		||||
	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<T: Writer> Informant<T> {
 | 
			
		||||
impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
 | 
			
		||||
	fn stack(&self) -> String {
 | 
			
		||||
		let items = self.stack.iter().map(|i| format!("\"0x{:x}\"", i)).collect::<Vec<_>>();
 | 
			
		||||
		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) {
 | 
			
		||||
		writeln!(
 | 
			
		||||
			&mut self.sink,
 | 
			
		||||
			&mut self.out_sink,
 | 
			
		||||
			"{{\"test\":\"{name}\",\"action\":\"{action}\"}}",
 | 
			
		||||
			name = name,
 | 
			
		||||
			action = action,
 | 
			
		||||
@ -91,30 +107,38 @@ impl<T: Writer> vm::Informant for Informant<T> {
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
			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<T: Writer> trace::VMTracer for Informant<T> {
 | 
			
		||||
impl<Trace: Writer, Out: Writer> trace::VMTracer for Informant<Trace, Out> {
 | 
			
		||||
	type Output = ();
 | 
			
		||||
 | 
			
		||||
	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();
 | 
			
		||||
 | 
			
		||||
		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<T: Writer> trace::VMTracer for Informant<T> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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<TestWriter>, Arc<Mutex<Vec<u8>>>) {
 | 
			
		||||
		let writer = TestWriter::default();
 | 
			
		||||
		let res = writer.0.clone();
 | 
			
		||||
		(Informant::new(writer), res)
 | 
			
		||||
	pub fn informant() -> (Informant<TestWriter, TestWriter>, Arc<Mutex<Vec<u8>>>) {
 | 
			
		||||
		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]
 | 
			
		||||
 | 
			
		||||
@ -81,10 +81,11 @@ pub fn run_action<T: Informant>(
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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<T: Informant>(
 | 
			
		||||
	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<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())
 | 
			
		||||
		.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,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user