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:
Wei Tang
2018-10-02 22:33:19 +08:00
committed by GitHub
parent 61ec361182
commit 1e9aebbc86
24 changed files with 1465 additions and 804 deletions

View File

@@ -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![]));
}

View File

@@ -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 }
}

View File

@@ -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 }
}