Resumable EVM and heap-allocated callstack (#9360)
* Add new Vm trappable interface * Exec/Resume interface * Basic implementation of CallCreateExecutive * Implement resume_call and resume_create for executive * Move convertion to call/create result to separate function * Implement consume that converts resumable to non-resumable * Use consume for Executive::call/create * Resumable EVM * Implement tracing mode without needing subtracers * Implement vmtracer so it doesn't require extra structs for subtracing * Use the new tracing mode in executive * Fix most of the linting errors for cargo build * Add the concept of stack_depth * Add back crossbeam * Fix some test compile * Fix prefix address test * Fix evm crate tests * Fix wasm crate test compile * Fix wasm runner compile * Fix jsontests compile * Fix evmbin compile * Fix an issue with create nonce and better vm tracing interface * Fix linting * Fix evmbin compile * Fix unconfirmed_substate and static_flag * Fix an issue in create address logic * Fix top-level tracing * Handle builtin tracing * Fix suicide and reward tracing index stack * Fix an issue where trap conflicts with tracing * Fix an issue in parent step vm tracing * Fix revert tracing * Fix evmbin tests * Remove params clone * Fix TODO proofs * Fix jsontests compile * Fix evmbin merge issue * Fix wasm merge issue * Fix wasm test * Fix ethcore merge warnings * Fix evmbin compile * Better expect messages and add some trace::skip_one asserts
This commit is contained in:
@@ -35,14 +35,28 @@ pub struct Informant {
|
||||
instruction: u8,
|
||||
gas_cost: U256,
|
||||
gas_used: U256,
|
||||
mem_written: Option<(usize, usize)>,
|
||||
store_written: Option<(U256, U256)>,
|
||||
stack: Vec<U256>,
|
||||
memory: Vec<u8>,
|
||||
storage: HashMap<H256, H256>,
|
||||
traces: Vec<String>,
|
||||
subtraces: Vec<String>,
|
||||
subinfos: Vec<Informant>,
|
||||
subdepth: usize,
|
||||
unmatched: bool,
|
||||
}
|
||||
|
||||
impl Informant {
|
||||
fn with_informant_in_depth<F: Fn(&mut Informant)>(informant: &mut Informant, depth: usize, f: F) {
|
||||
if depth == 0 {
|
||||
f(informant);
|
||||
} else {
|
||||
Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl vm::Informant for Informant {
|
||||
fn before_test(&mut self, name: &str, action: &str) {
|
||||
println!("{}", json!({"action": action, "test": name}));
|
||||
@@ -88,72 +102,92 @@ impl trace::VMTracer for Informant {
|
||||
type Output = Vec<String>;
|
||||
|
||||
fn trace_next_instruction(&mut self, pc: usize, instruction: u8, _current_gas: U256) -> bool {
|
||||
self.pc = pc;
|
||||
self.instruction = instruction;
|
||||
self.unmatched = true;
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
|
||||
informant.pc = pc;
|
||||
informant.instruction = instruction;
|
||||
informant.unmatched = 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)>) {
|
||||
let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info());
|
||||
|
||||
let trace = json!({
|
||||
"pc": self.pc,
|
||||
"op": self.instruction,
|
||||
"opName": info.map(|i| i.name).unwrap_or(""),
|
||||
"gas": format!("{:#x}", gas_used.saturating_add(self.gas_cost)),
|
||||
"gasCost": format!("{:#x}", self.gas_cost),
|
||||
"memory": format!("0x{}", self.memory.to_hex()),
|
||||
"stack": self.stack,
|
||||
"storage": self.storage,
|
||||
"depth": self.depth,
|
||||
fn trace_prepare_execute(&mut self, pc: usize, instruction: u8, gas_cost: U256, mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
|
||||
informant.pc = pc;
|
||||
informant.instruction = instruction;
|
||||
informant.gas_cost = gas_cost;
|
||||
informant.mem_written = mem_written;
|
||||
informant.store_written = store_written;
|
||||
});
|
||||
}
|
||||
|
||||
self.traces.push(trace.to_string());
|
||||
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
|
||||
let mem_diff = informant.mem_written.clone().map(|(o, s)| (o, &(mem[o..o+s])));
|
||||
let store_diff = informant.store_written.clone();
|
||||
let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info());
|
||||
|
||||
self.unmatched = false;
|
||||
self.gas_used = gas_used;
|
||||
let trace = json!({
|
||||
"pc": informant.pc,
|
||||
"op": informant.instruction,
|
||||
"opName": info.map(|i| i.name).unwrap_or(""),
|
||||
"gas": format!("{:#x}", gas_used.saturating_add(informant.gas_cost)),
|
||||
"gasCost": format!("{:#x}", informant.gas_cost),
|
||||
"memory": format!("0x{}", informant.memory.to_hex()),
|
||||
"stack": informant.stack,
|
||||
"storage": informant.storage,
|
||||
"depth": informant.depth,
|
||||
});
|
||||
informant.traces.push(trace.to_string());
|
||||
|
||||
let len = self.stack.len();
|
||||
let info_args = info.map(|i| i.args).unwrap_or(0);
|
||||
self.stack.truncate(if len > info_args { len - info_args } else { 0 });
|
||||
self.stack.extend_from_slice(stack_push);
|
||||
informant.unmatched = false;
|
||||
informant.gas_used = gas_used;
|
||||
|
||||
// TODO [ToDr] Align memory?
|
||||
if let Some((pos, data)) = mem_diff {
|
||||
if self.memory.len() < (pos + data.len()) {
|
||||
self.memory.resize(pos + data.len(), 0);
|
||||
let len = informant.stack.len();
|
||||
let info_args = info.map(|i| i.args).unwrap_or(0);
|
||||
informant.stack.truncate(if len > info_args { len - info_args } else { 0 });
|
||||
informant.stack.extend_from_slice(stack_push);
|
||||
|
||||
// TODO [ToDr] Align memory?
|
||||
if let Some((pos, data)) = mem_diff {
|
||||
if informant.memory.len() < (pos + data.len()) {
|
||||
informant.memory.resize(pos + data.len(), 0);
|
||||
}
|
||||
informant.memory[pos..pos + data.len()].copy_from_slice(data);
|
||||
}
|
||||
self.memory[pos..pos + data.len()].copy_from_slice(data);
|
||||
}
|
||||
|
||||
if let Some((pos, val)) = store_diff {
|
||||
self.storage.insert(pos.into(), val.into());
|
||||
}
|
||||
if let Some((pos, val)) = store_diff {
|
||||
informant.storage.insert(pos.into(), val.into());
|
||||
}
|
||||
|
||||
if !self.subtraces.is_empty() {
|
||||
self.traces.extend(mem::replace(&mut self.subtraces, vec![]));
|
||||
}
|
||||
if !informant.subtraces.is_empty() {
|
||||
informant.traces.extend(mem::replace(&mut informant.subtraces, vec![]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized {
|
||||
let mut vm = Informant::default();
|
||||
vm.depth = self.depth + 1;
|
||||
vm.code = code.to_vec();
|
||||
vm.gas_used = self.gas_used;
|
||||
vm
|
||||
fn prepare_subtrace(&mut self, code: &[u8]) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
|
||||
let mut vm = Informant::default();
|
||||
vm.depth = informant.depth + 1;
|
||||
vm.code = code.to_vec();
|
||||
vm.gas_used = informant.gas_used;
|
||||
informant.subinfos.push(vm);
|
||||
});
|
||||
self.subdepth += 1;
|
||||
}
|
||||
|
||||
fn done_subtrace(&mut self, sub: Self) {
|
||||
if let Some(subtraces) = sub.drain() {
|
||||
self.subtraces.extend(subtraces);
|
||||
}
|
||||
fn done_subtrace(&mut self) {
|
||||
self.subdepth -= 1;
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
|
||||
if let Some(subtraces) = informant.subinfos.pop().expect("prepare/done_subtrace are not balanced").drain() {
|
||||
informant.subtraces.extend(subtraces);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn drain(mut self) -> Option<Self::Output> {
|
||||
@@ -161,7 +195,7 @@ impl trace::VMTracer for Informant {
|
||||
// print last line with final state:
|
||||
self.gas_cost = 0.into();
|
||||
let gas_used = self.gas_used;
|
||||
self.trace_executed(gas_used, &[], None, None);
|
||||
self.trace_executed(gas_used, &[], &[]);
|
||||
} else if !self.subtraces.is_empty() {
|
||||
self.traces.extend(mem::replace(&mut self.subtraces, vec![]));
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ impl vm::Informant for Informant {
|
||||
impl trace::VMTracer for Informant {
|
||||
type Output = ();
|
||||
|
||||
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() }
|
||||
fn done_subtrace(&mut self, _sub: Self) {}
|
||||
fn prepare_subtrace(&mut self, _code: &[u8]) { Default::default() }
|
||||
fn done_subtrace(&mut self) {}
|
||||
fn drain(self) -> Option<()> { None }
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ pub struct Informant<Trace = io::Stderr, Out = io::Stdout> {
|
||||
depth: usize,
|
||||
stack: Vec<U256>,
|
||||
storage: HashMap<H256, H256>,
|
||||
subinfos: Vec<Informant<Trace, Out>>,
|
||||
subdepth: usize,
|
||||
trace_sink: Trace,
|
||||
out_sink: Out,
|
||||
}
|
||||
@@ -76,9 +78,19 @@ impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
|
||||
depth: Default::default(),
|
||||
stack: Default::default(),
|
||||
storage: Default::default(),
|
||||
subinfos: Default::default(),
|
||||
subdepth: 0,
|
||||
trace_sink, out_sink
|
||||
}
|
||||
}
|
||||
|
||||
fn with_informant_in_depth<F: Fn(&mut Informant<Trace, Out>)>(informant: &mut Informant<Trace, Out>, depth: usize, f: F) {
|
||||
if depth == 0 {
|
||||
f(informant);
|
||||
} else {
|
||||
Self::with_informant_in_depth(informant.subinfos.last_mut().expect("prepare/done_trace are not balanced"), depth - 1, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Trace: Writer, Out: Writer> vm::Informant for Informant<Trace, Out> {
|
||||
@@ -128,47 +140,64 @@ 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 {
|
||||
let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info());
|
||||
self.instruction = instruction;
|
||||
let trace_data = json!({
|
||||
"pc": pc,
|
||||
"op": instruction,
|
||||
"opName": info.map(|i| i.name).unwrap_or(""),
|
||||
"gas": format!("{:#x}", current_gas),
|
||||
"stack": self.stack,
|
||||
"storage": self.storage,
|
||||
"depth": self.depth,
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
|
||||
let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info());
|
||||
informant.instruction = instruction;
|
||||
let trace_data = json!({
|
||||
"pc": pc,
|
||||
"op": instruction,
|
||||
"opName": info.map(|i| i.name).unwrap_or(""),
|
||||
"gas": format!("{:#x}", current_gas),
|
||||
"stack": informant.stack,
|
||||
"storage": informant.storage,
|
||||
"depth": informant.depth,
|
||||
});
|
||||
|
||||
writeln!(&mut informant.trace_sink, "{}", trace_data).expect("The sink must be writeable.");
|
||||
});
|
||||
|
||||
writeln!(&mut self.trace_sink, "{}", trace_data).expect("The sink must be writeable.");
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {
|
||||
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, store_written: Option<(U256, U256)>) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
|
||||
if let Some((pos, val)) = store_written {
|
||||
informant.storage.insert(pos.into(), val.into());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) {
|
||||
let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info());
|
||||
fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem: &[u8]) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
|
||||
let info = ::evm::Instruction::from_u8(informant.instruction).map(|i| i.info());
|
||||
|
||||
let len = self.stack.len();
|
||||
let info_args = info.map(|i| i.args).unwrap_or(0);
|
||||
self.stack.truncate(if len > info_args { len - info_args } else { 0 });
|
||||
self.stack.extend_from_slice(stack_push);
|
||||
|
||||
if let Some((pos, val)) = store_diff {
|
||||
self.storage.insert(pos.into(), val.into());
|
||||
}
|
||||
let len = informant.stack.len();
|
||||
let info_args = info.map(|i| i.args).unwrap_or(0);
|
||||
informant.stack.truncate(if len > info_args { len - info_args } else { 0 });
|
||||
informant.stack.extend_from_slice(stack_push);
|
||||
});
|
||||
}
|
||||
|
||||
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized {
|
||||
let mut vm = Informant::new(self.trace_sink.clone(), self.out_sink.clone());
|
||||
vm.depth = self.depth + 1;
|
||||
vm.code = code.to_vec();
|
||||
vm
|
||||
fn prepare_subtrace(&mut self, code: &[u8]) {
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
|
||||
let mut vm = Informant::new(informant.trace_sink.clone(), informant.out_sink.clone());
|
||||
vm.depth = informant.depth + 1;
|
||||
vm.code = code.to_vec();
|
||||
informant.subinfos.push(vm);
|
||||
});
|
||||
self.subdepth += 1;
|
||||
}
|
||||
|
||||
fn done_subtrace(&mut self, _sub: Self) {}
|
||||
fn done_subtrace(&mut self) {
|
||||
self.subdepth -= 1;
|
||||
let subdepth = self.subdepth;
|
||||
Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
|
||||
informant.subinfos.pop();
|
||||
});
|
||||
}
|
||||
|
||||
fn drain(self) -> Option<Self::Output> { None }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user