Fix output of parity-evm in case of bad instruction (#5955)

* Fix output of evmbin.

* Use foundation chain settings by default.
This commit is contained in:
Tomasz Drwięga 2017-07-10 13:23:40 +02:00 committed by Gav Wood
parent ed5efebec1
commit f4453f77b8
10 changed files with 49 additions and 37 deletions

View File

@ -129,8 +129,11 @@ pub trait Ext {
/// Increments sstore refunds count by 1. /// Increments sstore refunds count by 1.
fn inc_sstore_clears(&mut self); fn inc_sstore_clears(&mut self);
/// Decide if any more operations should be traced. Passthrough for the VM trace.
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
/// Prepare to trace an operation. Passthrough for the VM trace. /// Prepare to trace an operation. Passthrough for the VM trace.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false } fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
/// Trace the finalised execution of a single instruction. /// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}

View File

@ -111,6 +111,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
self.mem.clear(); self.mem.clear();
let mut informant = informant::EvmInformant::new(ext.depth()); let mut informant = informant::EvmInformant::new(ext.depth());
let mut do_trace = true;
let code = &params.code.as_ref().expect("exec always called with code; qed"); let code = &params.code.as_ref().expect("exec always called with code; qed");
let mut valid_jump_destinations = None; let mut valid_jump_destinations = None;
@ -124,13 +125,17 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
let instruction = code[reader.position]; let instruction = code[reader.position];
reader.position += 1; reader.position += 1;
// TODO: make compile-time removable if too much of a performance hit.
do_trace = do_trace && ext.trace_next_instruction(reader.position - 1, instruction);
let info = &infos[instruction as usize]; let info = &infos[instruction as usize];
self.verify_instruction(ext, instruction, info, &stack)?; self.verify_instruction(ext, instruction, info, &stack)?;
// Calculate gas cost // Calculate gas cost
let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?; let requirements = gasometer.requirements(ext, instruction, info, &stack, self.mem.size())?;
// TODO: make compile-time removable if too much of a performance hit. if do_trace {
let trace_executed = ext.trace_prepare_execute(reader.position - 1, instruction, &requirements.gas_cost.as_u256()); ext.trace_prepare_execute(reader.position - 1, instruction, requirements.gas_cost.as_u256());
}
gasometer.verify_gas(&requirements.gas_cost)?; gasometer.verify_gas(&requirements.gas_cost)?;
self.mem.expand(requirements.memory_required_size); self.mem.expand(requirements.memory_required_size);
@ -139,7 +144,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) }); evm_debug!({ informant.before_instruction(reader.position, instruction, info, &gasometer.current_gas, &stack) });
let (mem_written, store_written) = match trace_executed { let (mem_written, store_written) = match do_trace {
true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)), true => (Self::mem_written(instruction, &stack), Self::store_written(instruction, &stack)),
false => (None, None), false => (None, None),
}; };
@ -155,7 +160,7 @@ impl<Cost: CostType> evm::Evm for Interpreter<Cost> {
gasometer.current_gas = gasometer.current_gas + *gas; gasometer.current_gas = gasometer.current_gas + *gas;
} }
if trace_executed { if do_trace {
ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written); ext.trace_executed(gasometer.current_gas.as_u256(), stack.peek_top(info.ret), mem_written.map(|(o, s)| (o, &(self.mem[o..(o + s)]))), store_written);
} }

View File

@ -376,7 +376,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::Return(output, trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
}; };
vm_tracer.done_subtrace(subvmtracer, res.is_ok()); vm_tracer.done_subtrace(subvmtracer);
trace!(target: "executive", "res={:?}", res); trace!(target: "executive", "res={:?}", res);
@ -457,7 +457,7 @@ impl<'a, B: 'a + StateBackend, E: Engine + ?Sized> Executive<'a, B, E> {
self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer) self.exec_vm(params, &mut unconfirmed_substate, OutputPolicy::InitContract(trace_output.as_mut()), &mut subtracer, &mut subvmtracer)
}; };
vm_tracer.done_subtrace(subvmtracer, res.is_ok()); vm_tracer.done_subtrace(subvmtracer);
match res { match res {
Ok(ref res) => tracer.trace_create( Ok(ref res) => tracer.trace_create(

View File

@ -379,7 +379,11 @@ impl<'a, T: 'a, V: 'a, B: 'a, E: 'a> Ext for Externalities<'a, T, V, B, E>
self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one(); self.substate.sstore_clears_count = self.substate.sstore_clears_count + U256::one();
} }
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { fn trace_next_instruction(&mut self, pc: usize, instruction: u8) -> bool {
self.vm_tracer.trace_next_instruction(pc, instruction)
}
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) {
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost) self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost)
} }

View File

@ -192,14 +192,15 @@ impl ExecutiveVMTracer {
} }
impl VMTracer for ExecutiveVMTracer { impl VMTracer for ExecutiveVMTracer {
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { true }
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) {
self.data.operations.push(VMOperation { self.data.operations.push(VMOperation {
pc: pc, pc: pc,
instruction: instruction, instruction: instruction,
gas_cost: gas_cost.clone(), gas_cost: gas_cost,
executed: None, executed: None,
}); });
true
} }
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
@ -221,7 +222,7 @@ impl VMTracer for ExecutiveVMTracer {
}} }}
} }
fn done_subtrace(&mut self, sub: Self, _is_successful: bool) { fn done_subtrace(&mut self, sub: Self) {
self.data.subs.push(sub.data); self.data.subs.push(sub.data);
} }

View File

@ -87,18 +87,23 @@ pub trait Tracer: Send {
/// Used by executive to build VM traces. /// Used by executive to build VM traces.
pub trait VMTracer: Send { pub trait VMTracer: Send {
/// Trace the preparation to execute a single instruction.
/// @returns true if `trace_executed` should be called.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
/// Trace the finalised execution of a single instruction. /// Trace the progression of interpreter to next instruction.
/// If tracer returns `false` it won't be called again.
/// @returns true if `trace_prepare_execute` and `trace_executed` should be called.
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
/// Trace the preparation to execute a single valid instruction.
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
/// Trace the finalised execution of a single valid instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
/// Spawn subtracer which will be used to trace deeper levels of execution. /// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized; fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized;
/// Finalize subtracer. /// Finalize subtracer.
fn done_subtrace(&mut self, sub: Self, is_successful: bool) where Self: Sized; fn done_subtrace(&mut self, sub: Self) where Self: Sized;
/// Consumes self and returns the VM trace. /// Consumes self and returns the VM trace.
fn drain(self) -> Option<VMTrace>; fn drain(self) -> Option<VMTrace>;

View File

@ -71,18 +71,15 @@ impl Tracer for NoopTracer {
pub struct NoopVMTracer; pub struct NoopVMTracer;
impl VMTracer for NoopVMTracer { impl VMTracer for NoopVMTracer {
/// Trace the preparation to execute a single instruction. fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8) -> bool { false }
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: &U256) -> bool { false }
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
/// Trace the finalised execution of a single instruction.
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {} fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn prepare_subtrace(&self, _code: &[u8]) -> Self { NoopVMTracer } fn prepare_subtrace(&self, _code: &[u8]) -> Self { NoopVMTracer }
/// Spawn subtracer which will be used to trace deeper levels of execution. fn done_subtrace(&mut self, _sub: Self) {}
fn done_subtrace(&mut self, _sub: Self, _is_successful: bool) {}
/// Consumes self and returns all VM traces.
fn drain(self) -> Option<VMTrace> { None } fn drain(self) -> Option<VMTrace> { None }
} }

View File

@ -81,13 +81,18 @@ impl vm::Informant for Informant {
} }
impl trace::VMTracer for Informant { impl trace::VMTracer for Informant {
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: &U256) -> bool { fn trace_next_instruction(&mut self, pc: usize, instruction: u8) -> bool {
self.pc = pc; self.pc = pc;
self.instruction = instruction; self.instruction = instruction;
self.gas_cost = *gas_cost;
true true
} }
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256) {
self.pc = pc;
self.instruction = instruction;
self.gas_cost = gas_cost;
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
let info = evm::INSTRUCTIONS[self.instruction as usize]; let info = evm::INSTRUCTIONS[self.instruction as usize];
@ -127,17 +132,9 @@ impl trace::VMTracer for Informant {
vm vm
} }
fn done_subtrace(&mut self, mut sub: Self, is_successful: bool) where Self: Sized { fn done_subtrace(&mut self, mut sub: Self) {
if sub.depth == 1 { if sub.depth == 1 {
// print last line with final state: // print last line with final state:
if is_successful {
sub.pc += 1;
sub.instruction = 0;
} else {
let push_bytes = evm::push_bytes(sub.instruction);
sub.pc += if push_bytes > 0 { push_bytes + 1 } else { 0 };
sub.instruction = if sub.pc < sub.code.len() { sub.code[sub.pc] } else { 0 };
}
sub.gas_cost = 0.into(); sub.gas_cost = 0.into();
let gas_used = sub.gas_used; let gas_used = sub.gas_used;
trace::VMTracer::trace_executed(&mut sub, gas_used, &[], None, None); trace::VMTracer::trace_executed(&mut sub, gas_used, &[], None, None);

View File

@ -44,6 +44,6 @@ impl vm::Informant for Informant {
impl trace::VMTracer for Informant { impl trace::VMTracer for Informant {
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() } fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() }
fn done_subtrace(&mut self, _sub: Self, _is_successful: bool) where Self: Sized {} fn done_subtrace(&mut self, _sub: Self) {}
fn drain(self) -> Option<trace::VMTrace> { None } fn drain(self) -> Option<trace::VMTrace> { None }
} }

View File

@ -137,7 +137,7 @@ impl Args {
spec::Spec::load(file)? spec::Spec::load(file)?
}, },
None => { None => {
spec::Spec::new_instant() ethcore::ethereum::new_foundation()
}, },
}) })
} }