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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1465 additions and 804 deletions

View File

@ -62,7 +62,7 @@ impl Finalize for Error {
} }
/// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256 /// Cost calculation type. For low-gas usage we calculate costs using usize instead of U256
pub trait CostType: Sized + From<usize> + Copy pub trait CostType: Sized + From<usize> + Copy + Send
+ ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> +ops::Sub<Output=Self> + ops::Mul<Output=Self> + ops::Div<Output=Self> + ops::Add<Output=Self> +ops::Sub<Output=Self>
+ ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self> + ops::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self>
+ cmp::Ord + fmt::Debug { + cmp::Ord + fmt::Debug {

View File

@ -17,7 +17,7 @@
//! Evm factory. //! Evm factory.
//! //!
use std::sync::Arc; use std::sync::Arc;
use vm::{Vm, Schedule}; use vm::{Exec, Schedule};
use ethereum_types::U256; use ethereum_types::U256;
use super::vm::ActionParams; use super::vm::ActionParams;
use super::interpreter::SharedCache; use super::interpreter::SharedCache;
@ -33,7 +33,7 @@ pub struct Factory {
impl Factory { impl Factory {
/// Create fresh instance of VM /// Create fresh instance of VM
/// Might choose implementation depending on supplied gas. /// Might choose implementation depending on supplied gas.
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Vm> { pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Exec> {
match self.evm { match self.evm {
VMType::Interpreter => if Self::can_fit_in_usize(&params.gas) { VMType::Interpreter => if Self::can_fit_in_usize(&params.gas) {
Box::new(super::interpreter::Interpreter::<usize>::new(params, self.evm_cache.clone(), schedule, depth)) Box::new(super::interpreter::Interpreter::<usize>::new(params, self.evm_cache.clone(), schedule, depth))

View File

@ -32,7 +32,8 @@ use ethereum_types::{U256, U512, H256, Address};
use vm::{ use vm::{
self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult, self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult,
ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule,
TrapKind, TrapError
}; };
use evm::CostType; use evm::CostType;
@ -103,6 +104,7 @@ enum InstructionResult<Gas> {
apply: bool, apply: bool,
}, },
StopExecution, StopExecution,
Trap(TrapKind),
} }
enum Never {} enum Never {}
@ -161,6 +163,7 @@ pub enum InterpreterResult {
Done(vm::Result<GasLeft>), Done(vm::Result<GasLeft>),
/// The VM can continue to run. /// The VM can continue to run.
Continue, Continue,
Trap(TrapKind),
} }
impl From<vm::Error> for InterpreterResult { impl From<vm::Error> for InterpreterResult {
@ -182,22 +185,89 @@ pub struct Interpreter<Cost: CostType> {
valid_jump_destinations: Option<Arc<BitSet>>, valid_jump_destinations: Option<Arc<BitSet>>,
gasometer: Option<Gasometer<Cost>>, gasometer: Option<Gasometer<Cost>>,
stack: VecStack<U256>, stack: VecStack<U256>,
resume_output_range: Option<(U256, U256)>,
resume_result: Option<InstructionResult<Cost>>,
last_stack_ret_len: usize,
_type: PhantomData<Cost>, _type: PhantomData<Cost>,
} }
impl<Cost: CostType> vm::Vm for Interpreter<Cost> { impl<Cost: 'static + CostType> vm::Exec for Interpreter<Cost> {
fn exec(&mut self, ext: &mut vm::Ext) -> vm::Result<GasLeft> { fn exec(mut self: Box<Self>, ext: &mut vm::Ext) -> vm::ExecTrapResult<GasLeft> {
loop { loop {
let result = self.step(ext); let result = self.step(ext);
match result { match result {
InterpreterResult::Continue => {}, InterpreterResult::Continue => {},
InterpreterResult::Done(value) => return value, InterpreterResult::Done(value) => return Ok(value),
InterpreterResult::Trap(trap) => match trap {
TrapKind::Call(params) => {
return Err(TrapError::Call(params, self));
},
TrapKind::Create(params, address) => {
return Err(TrapError::Create(params, address, self));
},
},
InterpreterResult::Stopped => panic!("Attempted to execute an already stopped VM.") InterpreterResult::Stopped => panic!("Attempted to execute an already stopped VM.")
} }
} }
} }
} }
impl<Cost: 'static + CostType> vm::ResumeCall for Interpreter<Cost> {
fn resume_call(mut self: Box<Self>, result: MessageCallResult) -> Box<vm::Exec> {
{
let this = &mut *self;
let (out_off, out_size) = this.resume_output_range.take().expect("Box<ResumeCall> is obtained from a call opcode; resume_output_range is always set after those opcodes are executed; qed");
match result {
MessageCallResult::Success(gas_left, data) => {
let output = this.mem.writeable_slice(out_off, out_size);
let len = cmp::min(output.len(), data.len());
(&mut output[..len]).copy_from_slice(&data[..len]);
this.return_data = data;
this.stack.push(U256::one());
this.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")));
},
MessageCallResult::Reverted(gas_left, data) => {
let output = this.mem.writeable_slice(out_off, out_size);
let len = cmp::min(output.len(), data.len());
(&mut output[..len]).copy_from_slice(&data[..len]);
this.return_data = data;
this.stack.push(U256::zero());
this.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")));
},
MessageCallResult::Failed => {
this.stack.push(U256::zero());
this.resume_result = Some(InstructionResult::Ok);
},
}
}
self
}
}
impl<Cost: 'static + CostType> vm::ResumeCreate for Interpreter<Cost> {
fn resume_create(mut self: Box<Self>, result: ContractCreateResult) -> Box<vm::Exec> {
match result {
ContractCreateResult::Created(address, gas_left) => {
self.stack.push(address_to_u256(address));
self.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")));
},
ContractCreateResult::Reverted(gas_left, return_data) => {
self.stack.push(U256::zero());
self.return_data = return_data;
self.resume_result = Some(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")));
},
ContractCreateResult::Failed => {
self.stack.push(U256::zero());
self.resume_result = Some(InstructionResult::Ok);
},
}
self
}
}
impl<Cost: CostType> Interpreter<Cost> { impl<Cost: CostType> Interpreter<Cost> {
/// Create a new `Interpreter` instance with shared cache. /// Create a new `Interpreter` instance with shared cache.
pub fn new(mut params: ActionParams, cache: Arc<SharedCache>, schedule: &Schedule, depth: usize) -> Interpreter<Cost> { pub fn new(mut params: ActionParams, cache: Arc<SharedCache>, schedule: &Schedule, depth: usize) -> Interpreter<Cost> {
@ -215,6 +285,9 @@ impl<Cost: CostType> Interpreter<Cost> {
do_trace: true, do_trace: true,
mem: Vec::new(), mem: Vec::new(),
return_data: ReturnData::empty(), return_data: ReturnData::empty(),
last_stack_ret_len: 0,
resume_output_range: None,
resume_result: None,
_type: PhantomData, _type: PhantomData,
} }
} }
@ -244,51 +317,58 @@ impl<Cost: CostType> Interpreter<Cost> {
/// Inner helper function for step. /// Inner helper function for step.
#[inline(always)] #[inline(always)]
fn step_inner(&mut self, ext: &mut vm::Ext) -> Result<Never, InterpreterResult> { fn step_inner(&mut self, ext: &mut vm::Ext) -> Result<Never, InterpreterResult> {
let opcode = self.reader.code[self.reader.position]; let result = match self.resume_result.take() {
let instruction = Instruction::from_u8(opcode); Some(result) => result,
self.reader.position += 1; None => {
let opcode = self.reader.code[self.reader.position];
let instruction = Instruction::from_u8(opcode);
self.reader.position += 1;
// TODO: make compile-time removable if too much of a performance hit. // TODO: make compile-time removable if too much of a performance hit.
self.do_trace = self.do_trace && ext.trace_next_instruction( self.do_trace = self.do_trace && ext.trace_next_instruction(
self.reader.position - 1, opcode, self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), self.reader.position - 1, opcode, self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(),
); );
let instruction = match instruction { let instruction = match instruction {
Some(i) => i, Some(i) => i,
None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction { None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction {
instruction: opcode instruction: opcode
}))), }))),
};
let info = instruction.info();
self.last_stack_ret_len = info.ret;
self.verify_instruction(ext, instruction, info)?;
// Calculate gas cost
let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?;
if self.do_trace {
ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256(), Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack));
}
self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost)?;
self.mem.expand(requirements.memory_required_size);
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas;
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost;
evm_debug!({ self.informant.before_instruction(self.reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &self.stack) });
// Execute instruction
let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas;
let result = self.exec_instruction(
current_gas, ext, instruction, requirements.provide_gas
)?;
evm_debug!({ self.informant.after_instruction(instruction) });
result
},
}; };
let info = instruction.info(); if let InstructionResult::Trap(trap) = result {
self.verify_instruction(ext, instruction, info)?; return Err(InterpreterResult::Trap(trap));
// Calculate gas cost
let requirements = self.gasometer.as_mut().expect(GASOMETER_PROOF).requirements(ext, instruction, info, &self.stack, self.mem.size())?;
if self.do_trace {
ext.trace_prepare_execute(self.reader.position - 1, opcode, requirements.gas_cost.as_u256());
} }
self.gasometer.as_mut().expect(GASOMETER_PROOF).verify_gas(&requirements.gas_cost)?;
self.mem.expand(requirements.memory_required_size);
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_mem_gas = requirements.memory_total_gas;
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas - requirements.gas_cost;
evm_debug!({ self.informant.before_instruction(self.reader.position, instruction, info, &self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas, &self.stack) });
let (mem_written, store_written) = match self.do_trace {
true => (Self::mem_written(instruction, &self.stack), Self::store_written(instruction, &self.stack)),
false => (None, None),
};
// Execute instruction
let current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas;
let result = self.exec_instruction(
current_gas, ext, instruction, requirements.provide_gas
)?;
evm_debug!({ self.informant.after_instruction(instruction) });
if let InstructionResult::UnusedGas(ref gas) = result { if let InstructionResult::UnusedGas(ref gas) = result {
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + *gas; self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas = self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas + *gas;
} }
@ -296,9 +376,8 @@ impl<Cost: CostType> Interpreter<Cost> {
if self.do_trace { if self.do_trace {
ext.trace_executed( ext.trace_executed(
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(), self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(),
self.stack.peek_top(info.ret), self.stack.peek_top(self.last_stack_ret_len),
mem_written.map(|(o, s)| (o, &(self.mem[o..o+s]))), &self.mem,
store_written,
); );
} }
@ -451,21 +530,24 @@ impl<Cost: CostType> Interpreter<Cost> {
let contract_code = self.mem.read_slice(init_off, init_size); let contract_code = self.mem.read_slice(init_off, init_size);
let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme); let create_result = ext.create(&create_gas.as_u256(), &endowment, contract_code, address_scheme, true);
return match create_result { return match create_result {
ContractCreateResult::Created(address, gas_left) => { Ok(ContractCreateResult::Created(address, gas_left)) => {
self.stack.push(address_to_u256(address)); self.stack.push(address_to_u256(address));
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
}, },
ContractCreateResult::Reverted(gas_left, return_data) => { Ok(ContractCreateResult::Reverted(gas_left, return_data)) => {
self.stack.push(U256::zero()); self.stack.push(U256::zero());
self.return_data = return_data; self.return_data = return_data;
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater."))) Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
}, },
ContractCreateResult::Failed => { Ok(ContractCreateResult::Failed) => {
self.stack.push(U256::zero()); self.stack.push(U256::zero());
Ok(InstructionResult::Ok) Ok(InstructionResult::Ok)
}, },
Err(trap) => {
Ok(InstructionResult::Trap(trap))
},
}; };
}, },
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => { instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
@ -524,13 +606,14 @@ impl<Cost: CostType> Interpreter<Cost> {
let call_result = { let call_result = {
let input = self.mem.read_slice(in_off, in_size); let input = self.mem.read_slice(in_off, in_size);
ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, call_type) ext.call(&call_gas.as_u256(), sender_address, receive_address, value, input, &code_address, call_type, true)
}; };
let output = self.mem.writeable_slice(out_off, out_size); self.resume_output_range = Some((out_off, out_size));
return match call_result { return match call_result {
MessageCallResult::Success(gas_left, data) => { Ok(MessageCallResult::Success(gas_left, data)) => {
let output = self.mem.writeable_slice(out_off, out_size);
let len = cmp::min(output.len(), data.len()); let len = cmp::min(output.len(), data.len());
(&mut output[..len]).copy_from_slice(&data[..len]); (&mut output[..len]).copy_from_slice(&data[..len]);
@ -538,7 +621,8 @@ impl<Cost: CostType> Interpreter<Cost> {
self.return_data = data; self.return_data = data;
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")))
}, },
MessageCallResult::Reverted(gas_left, data) => { Ok(MessageCallResult::Reverted(gas_left, data)) => {
let output = self.mem.writeable_slice(out_off, out_size);
let len = cmp::min(output.len(), data.len()); let len = cmp::min(output.len(), data.len());
(&mut output[..len]).copy_from_slice(&data[..len]); (&mut output[..len]).copy_from_slice(&data[..len]);
@ -546,10 +630,13 @@ impl<Cost: CostType> Interpreter<Cost> {
self.return_data = data; self.return_data = data;
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one"))) Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater than current one")))
}, },
MessageCallResult::Failed => { Ok(MessageCallResult::Failed) => {
self.stack.push(U256::zero()); self.stack.push(U256::zero());
Ok(InstructionResult::Ok) Ok(InstructionResult::Ok)
}, },
Err(trap) => {
Ok(InstructionResult::Trap(trap))
},
}; };
}, },
instructions::RETURN => { instructions::RETURN => {
@ -1095,10 +1182,10 @@ mod tests {
use rustc_hex::FromHex; use rustc_hex::FromHex;
use vmtype::VMType; use vmtype::VMType;
use factory::Factory; use factory::Factory;
use vm::{self, Vm, ActionParams, ActionValue}; use vm::{self, Exec, ActionParams, ActionValue};
use vm::tests::{FakeExt, test_finalize}; use vm::tests::{FakeExt, test_finalize};
fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box<Vm> { fn interpreter(params: ActionParams, ext: &vm::Ext) -> Box<Exec> {
Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth()) Factory::new(VMType::Interpreter, 1).create(params, ext.schedule(), ext.depth())
} }
@ -1118,7 +1205,7 @@ mod tests {
let gas_left = { let gas_left = {
let mut vm = interpreter(params, &ext); let mut vm = interpreter(params, &ext);
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(ext.calls.len(), 1); assert_eq!(ext.calls.len(), 1);
@ -1140,7 +1227,7 @@ mod tests {
let err = { let err = {
let mut vm = interpreter(params, &ext); let mut vm = interpreter(params, &ext);
test_finalize(vm.exec(&mut ext)).err().unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).err().unwrap()
}; };
assert_eq!(err, ::vm::Error::OutOfBounds); assert_eq!(err, ::vm::Error::OutOfBounds);

View File

@ -39,7 +39,7 @@ fn test_add(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_988)); assert_eq!(gas_left, U256::from(79_988));
@ -59,7 +59,7 @@ fn test_sha3(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_961)); assert_eq!(gas_left, U256::from(79_961));
@ -79,7 +79,7 @@ fn test_address(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -101,7 +101,7 @@ fn test_origin(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -123,7 +123,7 @@ fn test_sender(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -158,7 +158,7 @@ fn test_extcodecopy(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_935)); assert_eq!(gas_left, U256::from(79_935));
@ -178,7 +178,7 @@ fn test_log_empty(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(99_619)); assert_eq!(gas_left, U256::from(99_619));
@ -210,7 +210,7 @@ fn test_log_sender(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(98_974)); assert_eq!(gas_left, U256::from(98_974));
@ -235,7 +235,7 @@ fn test_blockhash(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_974)); assert_eq!(gas_left, U256::from(79_974));
@ -257,7 +257,7 @@ fn test_calldataload(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_991)); assert_eq!(gas_left, U256::from(79_991));
@ -278,7 +278,7 @@ fn test_author(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -298,7 +298,7 @@ fn test_timestamp(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -318,7 +318,7 @@ fn test_number(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -338,7 +338,7 @@ fn test_difficulty(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -358,7 +358,7 @@ fn test_gas_limit(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(79_995)); assert_eq!(gas_left, U256::from(79_995));
@ -376,7 +376,7 @@ fn test_mul(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383"); assert_store(&ext, 0, "000000000000000000000000000000000000000000000000734349397b853383");
@ -394,7 +394,7 @@ fn test_sub(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000012364ad0302");
@ -412,7 +412,7 @@ fn test_div(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac");
@ -430,7 +430,7 @@ fn test_div_zero(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -448,7 +448,7 @@ fn test_mod(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b");
@ -467,7 +467,7 @@ fn test_smod(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000076b4b");
@ -486,7 +486,7 @@ fn test_sdiv(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac"); assert_store(&ext, 0, "000000000000000000000000000000000000000000000000000000000002e0ac");
@ -505,7 +505,7 @@ fn test_exp(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59"); assert_store(&ext, 0, "90fd23767b60204c3d6fc8aec9e70a42a3f127140879c133a20129a597ed0c59");
@ -525,7 +525,7 @@ fn test_comparison(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -546,7 +546,7 @@ fn test_signed_comparison(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -567,7 +567,7 @@ fn test_bitops(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0");
@ -590,7 +590,7 @@ fn test_addmod_mulmod(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000001");
@ -611,7 +611,7 @@ fn test_byte(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000000");
@ -630,7 +630,7 @@ fn test_signextend(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff"); assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000fff");
@ -650,7 +650,7 @@ fn test_badinstruction_int() {
let err = { let err = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap_err() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err()
}; };
match err { match err {
@ -670,7 +670,7 @@ fn test_pop(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0"); assert_store(&ext, 0, "00000000000000000000000000000000000000000000000000000000000000f0");
@ -690,7 +690,7 @@ fn test_extops(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE assert_store(&ext, 0, "0000000000000000000000000000000000000000000000000000000000000004"); // PC / CALLDATASIZE
@ -713,7 +713,7 @@ fn test_jumps(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(ext.sstore_clears, U256::from(ext.schedule().sstore_refund_gas)); assert_eq!(ext.sstore_clears, U256::from(ext.schedule().sstore_refund_gas));
@ -741,7 +741,7 @@ fn test_calls(factory: super::Factory) {
let gas_left = { let gas_left = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_set_contains(&ext.calls, &FakeCall { assert_set_contains(&ext.calls, &FakeCall {
@ -782,7 +782,7 @@ fn test_create_in_staticcall(factory: super::Factory) {
let err = { let err = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap_err() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap_err()
}; };
assert_eq!(err, vm::Error::MutableCallInStaticContext); assert_eq!(err, vm::Error::MutableCallInStaticContext);
@ -1050,7 +1050,7 @@ fn push_two_pop_one_constantinople_test(factory: &super::Factory, opcode: u8, pu
let _ = { let _ = {
let mut vm = factory.create(params, ext.schedule(), ext.depth()); let mut vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext)).unwrap() test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_store(&ext, 0, result); assert_store(&ext, 0, result);

File diff suppressed because it is too large Load Diff

View File

@ -25,9 +25,8 @@ use executive::*;
use vm::{ use vm::{
self, ActionParams, ActionValue, EnvInfo, CallType, Schedule, self, ActionParams, ActionValue, EnvInfo, CallType, Schedule,
Ext, ContractCreateResult, MessageCallResult, CreateContractAddress, Ext, ContractCreateResult, MessageCallResult, CreateContractAddress,
ReturnData ReturnData, TrapKind
}; };
use evm::FinalizationResult;
use transaction::UNSIGNED_SENDER; use transaction::UNSIGNED_SENDER;
use trace::{Tracer, VMTracer}; use trace::{Tracer, VMTracer};
@ -67,7 +66,8 @@ pub struct Externalities<'a, T: 'a, V: 'a, B: 'a> {
state: &'a mut State<B>, state: &'a mut State<B>,
env_info: &'a EnvInfo, env_info: &'a EnvInfo,
depth: usize, depth: usize,
origin_info: OriginInfo, stack_depth: usize,
origin_info: &'a OriginInfo,
substate: &'a mut Substate, substate: &'a mut Substate,
machine: &'a Machine, machine: &'a Machine,
schedule: &'a Schedule, schedule: &'a Schedule,
@ -87,7 +87,8 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B>
machine: &'a Machine, machine: &'a Machine,
schedule: &'a Schedule, schedule: &'a Schedule,
depth: usize, depth: usize,
origin_info: OriginInfo, stack_depth: usize,
origin_info: &'a OriginInfo,
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy, output: OutputPolicy,
tracer: &'a mut T, tracer: &'a mut T,
@ -98,6 +99,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Externalities<'a, T, V, B>
state: state, state: state,
env_info: env_info, env_info: env_info,
depth: depth, depth: depth,
stack_depth: stack_depth,
origin_info: origin_info, origin_info: origin_info,
substate: substate, substate: substate,
machine: machine, machine: machine,
@ -176,7 +178,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
}; };
let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule); let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule);
let r = ex.call(params, self.substate, self.tracer, self.vm_tracer); let r = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer);
let output = match &r { let output = match &r {
Ok(ref r) => H256::from(&r.return_data[..32]), Ok(ref r) => H256::from(&r.return_data[..32]),
_ => H256::new(), _ => H256::new(),
@ -206,14 +208,15 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
gas: &U256, gas: &U256,
value: &U256, value: &U256,
code: &[u8], code: &[u8],
address_scheme: CreateContractAddress address_scheme: CreateContractAddress,
) -> ContractCreateResult { trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
// create new contract address // create new contract address
let (address, code_hash) = match self.state.nonce(&self.origin_info.address) { let (address, code_hash) = match self.state.nonce(&self.origin_info.address) {
Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code), Ok(nonce) => contract_address(address_scheme, &self.origin_info.address, &nonce, &code),
Err(e) => { Err(e) => {
debug!(target: "ext", "Database corruption encountered: {:?}", e); debug!(target: "ext", "Database corruption encountered: {:?}", e);
return ContractCreateResult::Failed return Ok(ContractCreateResult::Failed)
} }
}; };
@ -237,23 +240,19 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
if !self.schedule.keep_unsigned_nonce || params.sender != UNSIGNED_SENDER { if !self.schedule.keep_unsigned_nonce || params.sender != UNSIGNED_SENDER {
if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { if let Err(e) = self.state.inc_nonce(&self.origin_info.address) {
debug!(target: "ext", "Database corruption encountered: {:?}", e); debug!(target: "ext", "Database corruption encountered: {:?}", e);
return ContractCreateResult::Failed return Ok(ContractCreateResult::Failed)
} }
} }
} }
let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag);
if trap {
return Err(TrapKind::Create(params, address));
}
// TODO: handle internal error separately // TODO: handle internal error separately
match ex.create(params, self.substate, self.tracer, self.vm_tracer) { let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag);
Ok(FinalizationResult{ gas_left, apply_state: true, .. }) => { let out = ex.create_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer);
self.substate.contracts_created.push(address.clone()); Ok(into_contract_create_result(out, &address, self.substate))
ContractCreateResult::Created(address, gas_left)
},
Ok(FinalizationResult{ gas_left, apply_state: false, return_data }) => {
ContractCreateResult::Reverted(gas_left, return_data)
},
_ => ContractCreateResult::Failed,
}
} }
fn call( fn call(
@ -264,8 +263,9 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
call_type: CallType call_type: CallType,
) -> MessageCallResult { trap: bool,
) -> ::std::result::Result<MessageCallResult, TrapKind> {
trace!(target: "externalities", "call"); trace!(target: "externalities", "call");
let code_res = self.state.code(code_address) let code_res = self.state.code(code_address)
@ -273,7 +273,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
let (code, code_hash) = match code_res { let (code, code_hash) = match code_res {
Ok((code, hash)) => (code, hash), Ok((code, hash)) => (code, hash),
Err(_) => return MessageCallResult::Failed, Err(_) => return Ok(MessageCallResult::Failed),
}; };
let mut params = ActionParams { let mut params = ActionParams {
@ -295,13 +295,13 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
params.value = ActionValue::Transfer(value); params.value = ActionValue::Transfer(value);
} }
let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag); if trap {
return Err(TrapKind::Call(params));
match ex.call(params, self.substate, self.tracer, self.vm_tracer) {
Ok(FinalizationResult{ gas_left, return_data, apply_state: true }) => MessageCallResult::Success(gas_left, return_data),
Ok(FinalizationResult{ gas_left, return_data, apply_state: false }) => MessageCallResult::Reverted(gas_left, return_data),
_ => MessageCallResult::Failed
} }
let mut ex = Executive::from_parent(self.state, self.env_info, self.machine, self.schedule, self.depth, self.static_flag);
let out = ex.call_with_crossbeam(params, self.substate, self.stack_depth + 1, self.tracer, self.vm_tracer);
Ok(into_message_call_result(out))
} }
fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> { fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> {
@ -406,12 +406,12 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B>
self.vm_tracer.trace_next_instruction(pc, instruction, current_gas) self.vm_tracer.trace_next_instruction(pc, instruction, current_gas)
} }
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)>) {
self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost) self.vm_tracer.trace_prepare_execute(pc, instruction, gas_cost, mem_written, store_written)
} }
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: &[u8]) {
self.vm_tracer.trace_executed(gas_used, stack_push, mem_diff, store_diff) self.vm_tracer.trace_executed(gas_used, stack_push, mem)
} }
} }
@ -480,8 +480,9 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
let ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
assert_eq!(ext.env_info().number, 100); assert_eq!(ext.env_info().number, 100);
} }
@ -492,8 +493,9 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::<U256>().unwrap()); let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::<U256>().unwrap());
@ -516,8 +518,9 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::<U256>().unwrap()); let hash = ext.blockhash(&"0000000000000000000000000000000000000000000000000000000000120000".parse::<U256>().unwrap());
@ -531,8 +534,9 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
// this should panic because we have no balance on any account // this should panic because we have no balance on any account
ext.call( ext.call(
@ -542,8 +546,9 @@ mod tests {
Some("0000000000000000000000000000000000000000000000000000000000150000".parse::<U256>().unwrap()), Some("0000000000000000000000000000000000000000000000000000000000150000".parse::<U256>().unwrap()),
&[], &[],
&Address::new(), &Address::new(),
CallType::Call CallType::Call,
); false,
).ok().unwrap();
} }
#[test] #[test]
@ -555,9 +560,10 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
{ {
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
ext.log(log_topics, &log_data).unwrap(); ext.log(log_topics, &log_data).unwrap();
} }
@ -572,9 +578,10 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
{ {
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
ext.suicide(refund_account).unwrap(); ext.suicide(refund_account).unwrap();
} }
@ -589,11 +596,12 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
let address = { let address = {
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce) { match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderAndNonce, false) {
ContractCreateResult::Created(address, _) => address, Ok(ContractCreateResult::Created(address, _)) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."), _ => panic!("Test create failed; expected Created, got Failed/Reverted."),
} }
}; };
@ -609,12 +617,13 @@ mod tests {
let state = &mut setup.state; let state = &mut setup.state;
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let origin_info = get_test_origin();
let address = { let address = {
let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, get_test_origin(), &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false); let mut ext = Externalities::new(state, &setup.env_info, &setup.machine, &setup.schedule, 0, 0, &origin_info, &mut setup.sub_state, OutputPolicy::InitContract, &mut tracer, &mut vm_tracer, false);
match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::default())) { match ext.create(&U256::max_value(), &U256::zero(), &[], CreateContractAddress::FromSenderSaltAndCodeHash(H256::default()), false) {
ContractCreateResult::Created(address, _) => address, Ok(ContractCreateResult::Created(address, _)) => address,
_ => panic!("Test create failed; expected Created, got Failed/Reverted."), _ => panic!("Test create failed; expected Created, got Failed/Reverted."),
} }
}; };

View File

@ -18,7 +18,7 @@ use trie::TrieFactory;
use ethtrie::RlpCodec; use ethtrie::RlpCodec;
use account_db::Factory as AccountFactory; use account_db::Factory as AccountFactory;
use evm::{Factory as EvmFactory, VMType}; use evm::{Factory as EvmFactory, VMType};
use vm::{Vm, ActionParams, Schedule}; use vm::{Exec, ActionParams, Schedule};
use wasm::WasmInterpreter; use wasm::WasmInterpreter;
use keccak_hasher::KeccakHasher; use keccak_hasher::KeccakHasher;
@ -31,7 +31,7 @@ pub struct VmFactory {
} }
impl VmFactory { impl VmFactory {
pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Vm> { pub fn create(&self, params: ActionParams, schedule: &Schedule, depth: usize) -> Box<Exec> {
if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) { if schedule.wasm.is_some() && params.code.as_ref().map_or(false, |code| code.len() > 4 && &code[0..4] == WASM_MAGIC_NUMBER) {
Box::new(WasmInterpreter::new(params)) Box::new(WasmInterpreter::new(params))
} else { } else {

View File

@ -88,7 +88,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B>
machine: &'a Machine, machine: &'a Machine,
schedule: &'a Schedule, schedule: &'a Schedule,
depth: usize, depth: usize,
origin_info: OriginInfo, origin_info: &'a OriginInfo,
substate: &'a mut Substate, substate: &'a mut Substate,
output: OutputPolicy, output: OutputPolicy,
address: Address, address: Address,
@ -98,7 +98,7 @@ impl<'a, T: 'a, V: 'a, B: 'a> TestExt<'a, T, V, B>
let static_call = false; let static_call = false;
Ok(TestExt { Ok(TestExt {
nonce: state.nonce(&address)?, nonce: state.nonce(&address)?,
ext: Externalities::new(state, info, machine, schedule, depth, origin_info, substate, output, tracer, vm_tracer, static_call), ext: Externalities::new(state, info, machine, schedule, depth, 0, origin_info, substate, output, tracer, vm_tracer, static_call),
callcreates: vec![], callcreates: vec![],
sender: address, sender: address,
}) })
@ -140,7 +140,14 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
self.ext.blockhash(number) self.ext.blockhash(number)
} }
fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult { fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
_trap: bool
) -> Result<ContractCreateResult, vm::TrapKind> {
self.callcreates.push(CallCreate { self.callcreates.push(CallCreate {
data: code.to_vec(), data: code.to_vec(),
destination: None, destination: None,
@ -148,25 +155,27 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for TestExt<'a, T, V, B>
value: *value value: *value
}); });
let contract_address = contract_address(address, &self.sender, &self.nonce, &code).0; let contract_address = contract_address(address, &self.sender, &self.nonce, &code).0;
ContractCreateResult::Created(contract_address, *gas) Ok(ContractCreateResult::Created(contract_address, *gas))
} }
fn call(&mut self, fn call(
&mut self,
gas: &U256, gas: &U256,
_sender_address: &Address, _sender_address: &Address,
receive_address: &Address, receive_address: &Address,
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
_code_address: &Address, _code_address: &Address,
_call_type: CallType _call_type: CallType,
) -> MessageCallResult { _trap: bool
) -> Result<MessageCallResult, vm::TrapKind> {
self.callcreates.push(CallCreate { self.callcreates.push(CallCreate {
data: data.to_vec(), data: data.to_vec(),
destination: Some(receive_address.clone()), destination: Some(receive_address.clone()),
gas_limit: *gas, gas_limit: *gas,
value: value.unwrap() value: value.unwrap()
}); });
MessageCallResult::Success(*gas, ReturnData::empty()) Ok(MessageCallResult::Success(*gas, ReturnData::empty()))
} }
fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> { fn extcode(&self, address: &Address) -> vm::Result<Option<Arc<Bytes>>> {
@ -270,6 +279,7 @@ fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8]
let mut tracer = NoopTracer; let mut tracer = NoopTracer;
let mut vm_tracer = NoopVMTracer; let mut vm_tracer = NoopVMTracer;
let vm_factory = state.vm_factory(); let vm_factory = state.vm_factory();
let origin_info = OriginInfo::from(&params);
// execute // execute
let (res, callcreates) = { let (res, callcreates) = {
@ -280,7 +290,7 @@ fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8]
&machine, &machine,
&schedule, &schedule,
0, 0,
OriginInfo::from(&params), &origin_info,
&mut substate, &mut substate,
OutputPolicy::Return, OutputPolicy::Return,
params.address.clone(), params.address.clone(),
@ -288,7 +298,7 @@ fn do_json_test_for<H: FnMut(&str, HookType)>(vm_type: &VMType, json_data: &[u8]
&mut vm_tracer, &mut vm_tracer,
)); ));
let mut evm = vm_factory.create(params, &schedule, 0); let mut evm = vm_factory.create(params, &schedule, 0);
let res = evm.exec(&mut ex); let res = evm.exec(&mut ex).ok().expect("TestExt never trap; resume error never happens; qed");
// a return in finalize will not alter callcreates // a return in finalize will not alter callcreates
let callcreates = ex.callcreates.clone(); let callcreates = ex.callcreates.clone();
(res.finalize(ex), callcreates) (res.finalize(ex), callcreates)

View File

@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Trace database. //! Trace database.
use std::collections::{HashMap, VecDeque}; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use blockchain::{BlockChainDB}; use blockchain::{BlockChainDB};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
@ -227,13 +227,12 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
} }
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> { fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
let trace_position_deq = VecDeque::from(trace_position);
self.extras.block_hash(block_number) self.extras.block_hash(block_number)
.and_then(|block_hash| self.transactions_traces(&block_hash) .and_then(|block_hash| self.transactions_traces(&block_hash)
.and_then(|traces| traces.into_iter().nth(tx_position)) .and_then(|traces| traces.into_iter().nth(tx_position))
.map(Into::<Vec<FlatTrace>>::into) .map(Into::<Vec<FlatTrace>>::into)
// this may and should be optimized // this may and should be optimized
.and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position_deq)) .and_then(|traces| traces.into_iter().find(|trace| trace.trace_address == trace_position))
.map(|trace| { .map(|trace| {
let tx_hash = self.extras.transaction_hash(block_number, tx_position) let tx_hash = self.extras.transaction_hash(block_number, tx_position)
.expect("Expected to find transaction hash. Database is probably corrupted"); .expect("Expected to find transaction hash. Database is probably corrupted");

View File

@ -17,159 +17,175 @@
//! Simple executive tracer. //! Simple executive tracer.
use ethereum_types::{U256, Address}; use ethereum_types::{U256, Address};
use vm::ActionParams; use vm::{Error as VmError, ActionParams};
use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType}; use trace::trace::{Call, Create, Action, Res, CreateResult, CallResult, VMTrace, VMOperation, VMExecutedOperation, MemoryDiff, StorageDiff, Suicide, Reward, RewardType};
use trace::{Tracer, VMTracer, FlatTrace, TraceError}; use trace::{Tracer, VMTracer, FlatTrace};
/// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls. /// Simple executive tracer. Traces all calls and creates. Ignores delegatecalls.
#[derive(Default)] #[derive(Default)]
pub struct ExecutiveTracer { pub struct ExecutiveTracer {
traces: Vec<FlatTrace>, traces: Vec<FlatTrace>,
} index_stack: Vec<usize>,
vecindex_stack: Vec<usize>,
fn top_level_subtraces(traces: &[FlatTrace]) -> usize { sublen_stack: Vec<usize>,
traces.iter().filter(|t| t.trace_address.is_empty()).count() skip_one: bool,
}
fn prefix_subtrace_addresses(mut traces: Vec<FlatTrace>) -> Vec<FlatTrace> {
// input traces are expected to be ordered like
// []
// [0]
// [0, 0]
// [0, 1]
// []
// [0]
//
// so they can be transformed to
//
// [0]
// [0, 0]
// [0, 0, 0]
// [0, 0, 1]
// [1]
// [1, 0]
let mut current_subtrace_index = 0;
let mut first = true;
for trace in &mut traces {
match (first, trace.trace_address.is_empty()) {
(true, _) => first = false,
(_, true) => current_subtrace_index += 1,
_ => {}
}
trace.trace_address.push_front(current_subtrace_index);
}
traces
}
#[test]
fn should_prefix_address_properly() {
use super::trace::{Action, Res, Suicide};
let f = |v: Vec<usize>| FlatTrace {
action: Action::Suicide(Suicide {
address: Default::default(),
balance: Default::default(),
refund_address: Default::default(),
}),
result: Res::None,
subtraces: 0,
trace_address: v.into_iter().collect(),
};
let t = vec![vec![], vec![0], vec![0, 0], vec![0], vec![], vec![], vec![0], vec![]].into_iter().map(&f).collect();
let t = prefix_subtrace_addresses(t);
assert_eq!(t, vec![vec![0], vec![0, 0], vec![0, 0, 0], vec![0, 0], vec![1], vec![2], vec![2, 0], vec![3]].into_iter().map(&f).collect::<Vec<_>>());
} }
impl Tracer for ExecutiveTracer { impl Tracer for ExecutiveTracer {
type Output = FlatTrace; type Output = FlatTrace;
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call> { fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool) {
Some(Call::from(params.clone())) assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_call it cannot be true; qed");
}
fn prepare_trace_create(&self, params: &ActionParams) -> Option<Create> { if depth != 0 && is_builtin && params.value.value() == U256::zero() {
Some(Create::from(params.clone())) self.skip_one = true;
} return;
}
if let Some(parentlen) = self.sublen_stack.last_mut() {
*parentlen += 1;
}
fn trace_call(&mut self, call: Option<Call>, gas_used: U256, output: &[u8], subs: Vec<FlatTrace>) {
let trace = FlatTrace { let trace = FlatTrace {
trace_address: Default::default(), trace_address: self.index_stack.clone(),
subtraces: top_level_subtraces(&subs), subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), action: Action::Call(Call::from(params.clone())),
result: Res::Call(CallResult { result: Res::Call(CallResult {
gas_used: gas_used, gas_used: U256::zero(),
output: output.into() output: Vec::new()
}), }),
}; };
debug!(target: "trace", "Traced call {:?}", trace); self.vecindex_stack.push(self.traces.len());
self.traces.push(trace); self.traces.push(trace);
self.traces.extend(prefix_subtrace_addresses(subs)); self.index_stack.push(0);
self.sublen_stack.push(0);
} }
fn trace_create(&mut self, create: Option<Create>, gas_used: U256, code: &[u8], address: Address, subs: Vec<FlatTrace>) { fn prepare_trace_create(&mut self, params: &ActionParams) {
assert!(!self.skip_one, "skip_one is used only for builtin contracts that do not have subsequent calls; in prepare_trace_create it cannot be true; qed");
if let Some(parentlen) = self.sublen_stack.last_mut() {
*parentlen += 1;
}
let trace = FlatTrace { let trace = FlatTrace {
subtraces: top_level_subtraces(&subs), trace_address: self.index_stack.clone(),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
action: Action::Create(Create::from(params.clone())),
result: Res::Create(CreateResult { result: Res::Create(CreateResult {
gas_used: gas_used, gas_used: U256::zero(),
code: code.into(), code: Vec::new(),
address: address address: Address::default(),
}), }),
trace_address: Default::default(),
}; };
debug!(target: "trace", "Traced create {:?}", trace); self.vecindex_stack.push(self.traces.len());
self.traces.push(trace); self.traces.push(trace);
self.traces.extend(prefix_subtrace_addresses(subs)); self.index_stack.push(0);
self.sublen_stack.push(0);
} }
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<FlatTrace>, error: TraceError) { fn done_trace_call(&mut self, gas_used: U256, output: &[u8]) {
let trace = FlatTrace { if self.skip_one {
trace_address: Default::default(), self.skip_one = false;
subtraces: top_level_subtraces(&subs), return;
action: Action::Call(call.expect("self.prepare_trace_call().is_some(): so we must be tracing: qed")), }
result: Res::FailedCall(error),
}; let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_call before this function; vecindex_stack is never empty; qed");
debug!(target: "trace", "Traced failed call {:?}", trace); let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_call before this function; sublen_stack is never empty; qed");
self.traces.push(trace); self.index_stack.pop();
self.traces.extend(prefix_subtrace_addresses(subs));
self.traces[vecindex].result = Res::Call(CallResult {
gas_used,
output: output.into(),
});
self.traces[vecindex].subtraces = sublen;
if let Some(index) = self.index_stack.last_mut() {
*index += 1;
}
} }
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<FlatTrace>, error: TraceError) { fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address) {
let trace = FlatTrace { assert!(!self.skip_one, "skip_one is only set with prepare_trace_call for builtin contracts with no subsequent calls; skip_one cannot be true after the same level prepare_trace_create; qed");
subtraces: top_level_subtraces(&subs),
action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")), let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create before this function; vecindex_stack is never empty; qed");
result: Res::FailedCreate(error), let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create before this function; sublen_stack is never empty; qed");
trace_address: Default::default(), self.index_stack.pop();
self.traces[vecindex].result = Res::Create(CreateResult {
gas_used, address,
code: code.into(),
});
self.traces[vecindex].subtraces = sublen;
if let Some(index) = self.index_stack.last_mut() {
*index += 1;
}
}
fn done_trace_failed(&mut self, error: &VmError) {
if self.skip_one {
self.skip_one = false;
return;
}
let vecindex = self.vecindex_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed");
let sublen = self.sublen_stack.pop().expect("Executive invoked prepare_trace_create/call before this function; vecindex_stack is never empty; qed");
self.index_stack.pop();
let is_create = match self.traces[vecindex].action {
Action::Create(_) => true,
_ => false,
}; };
debug!(target: "trace", "Traced failed create {:?}", trace);
self.traces.push(trace); if is_create {
self.traces.extend(prefix_subtrace_addresses(subs)); self.traces[vecindex].result = Res::FailedCreate(error.into());
} else {
self.traces[vecindex].result = Res::FailedCall(error.into());
}
self.traces[vecindex].subtraces = sublen;
if let Some(index) = self.index_stack.last_mut() {
*index += 1;
}
} }
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) { fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address) {
if let Some(parentlen) = self.sublen_stack.last_mut() {
*parentlen += 1;
}
let trace = FlatTrace { let trace = FlatTrace {
subtraces: 0, subtraces: 0,
action: Action::Suicide(Suicide { address, refund_address, balance } ), action: Action::Suicide(Suicide { address, refund_address, balance } ),
result: Res::None, result: Res::None,
trace_address: Default::default(), trace_address: self.index_stack.clone(),
}; };
debug!(target: "trace", "Traced suicide {:?}", trace); debug!(target: "trace", "Traced suicide {:?}", trace);
self.traces.push(trace); self.traces.push(trace);
if let Some(index) = self.index_stack.last_mut() {
*index += 1;
}
} }
fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) { fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType) {
if let Some(parentlen) = self.sublen_stack.last_mut() {
*parentlen += 1;
}
let trace = FlatTrace { let trace = FlatTrace {
subtraces: 0, subtraces: 0,
action: Action::Reward(Reward { author, value, reward_type } ), action: Action::Reward(Reward { author, value, reward_type } ),
result: Res::None, result: Res::None,
trace_address: Default::default(), trace_address: self.index_stack.clone(),
}; };
debug!(target: "trace", "Traced reward {:?}", trace); debug!(target: "trace", "Traced reward {:?}", trace);
self.traces.push(trace); self.traces.push(trace);
}
fn subtracer(&self) -> Self { if let Some(index) = self.index_stack.last_mut() {
ExecutiveTracer::default() *index += 1;
}
} }
fn drain(self) -> Vec<FlatTrace> { fn drain(self) -> Vec<FlatTrace> {
@ -180,6 +196,9 @@ impl Tracer for ExecutiveTracer {
/// Simple VM tracer. Traces all operations. /// Simple VM tracer. Traces all operations.
pub struct ExecutiveVMTracer { pub struct ExecutiveVMTracer {
data: VMTrace, data: VMTrace,
depth: usize,
last_mem_written: Option<(usize, usize)>,
last_store_written: Option<(U256, U256)>,
} }
impl ExecutiveVMTracer { impl ExecutiveVMTracer {
@ -191,7 +210,18 @@ impl ExecutiveVMTracer {
code: vec![], code: vec![],
operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step operations: vec![Default::default()], // prefill with a single entry so that prepare_subtrace can get the parent_step
subs: vec![], subs: vec![],
} },
depth: 0,
last_mem_written: None,
last_store_written: None,
}
}
fn with_trace_in_depth<F: Fn(&mut VMTrace)>(trace: &mut VMTrace, depth: usize, f: F) {
if depth == 0 {
f(trace);
} else {
Self::with_trace_in_depth(trace.subs.last_mut().expect("self.depth is incremented with prepare_subtrace; a subtrace is always pushed; self.depth cannot be greater than subtrace stack; qed"), depth - 1, f);
} }
} }
} }
@ -201,37 +231,77 @@ impl VMTracer for ExecutiveVMTracer {
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { true } fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { 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)>) {
self.data.operations.push(VMOperation { Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
pc: pc, trace.operations.push(VMOperation {
instruction: instruction, pc: pc,
gas_cost: gas_cost, instruction: instruction,
executed: None, gas_cost: gas_cost,
executed: None,
});
});
self.last_mem_written = mem_written;
self.last_store_written = store_written;
}
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
let mem_diff = self.last_mem_written.take().map(|(o, s)| (o, &(mem[o..o+s])));
let store_diff = self.last_store_written.take();
Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
let ex = VMExecutedOperation {
gas_used: gas_used,
stack_push: stack_push.iter().cloned().collect(),
mem_diff: mem_diff.map(|(s, r)| MemoryDiff { offset: s, data: r.iter().cloned().collect() }),
store_diff: store_diff.map(|(l, v)| StorageDiff { location: l, value: v }),
};
trace.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute; trace.operations cannot be empty; qed").executed = Some(ex);
}); });
} }
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem_diff: Option<(usize, &[u8])>, store_diff: Option<(U256, U256)>) { fn prepare_subtrace(&mut self, code: &[u8]) {
let ex = VMExecutedOperation { Self::with_trace_in_depth(&mut self.data, self.depth, move |trace| {
gas_used: gas_used, let parent_step = trace.operations.len() - 1; // won't overflow since we must already have pushed an operation in trace_prepare_execute.
stack_push: stack_push.iter().cloned().collect(), trace.subs.push(VMTrace {
mem_diff: mem_diff.map(|(s, r)| MemoryDiff{ offset: s, data: r.iter().cloned().collect() }), parent_step,
store_diff: store_diff.map(|(l, v)| StorageDiff{ location: l, value: v }), code: code.to_vec(),
}; operations: vec![],
self.data.operations.last_mut().expect("trace_executed is always called after a trace_prepare_execute").executed = Some(ex); subs: vec![],
});
});
self.depth += 1;
} }
fn prepare_subtrace(&self, code: &[u8]) -> Self { fn done_subtrace(&mut self) {
ExecutiveVMTracer { data: VMTrace { self.depth -= 1;
parent_step: self.data.operations.len() - 1, // won't overflow since we must already have pushed an operation in trace_prepare_execute.
code: code.to_vec(),
operations: vec![],
subs: vec![],
}}
}
fn done_subtrace(&mut self, sub: Self) {
self.data.subs.push(sub.data);
} }
fn drain(mut self) -> Option<VMTrace> { self.data.subs.pop() } fn drain(mut self) -> Option<VMTrace> { self.data.subs.pop() }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_prefix_address_properly() {
let mut tracer = ExecutiveTracer::default();
tracer.prepare_trace_call(&ActionParams::default(), 0, false);
tracer.prepare_trace_call(&ActionParams::default(), 1, false);
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
tracer.done_trace_call(U256::zero(), &[]);
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
tracer.done_trace_call(U256::zero(), &[]);
tracer.prepare_trace_call(&ActionParams::default(), 2, false);
tracer.done_trace_call(U256::zero(), &[]);
tracer.done_trace_call(U256::zero(), &[]);
tracer.done_trace_call(U256::zero(), &[]);
let drained = tracer.drain();
assert!(drained[0].trace_address.len() == 0);
assert_eq!(&drained[1].trace_address, &[0]);
assert_eq!(&drained[2].trace_address, &[0, 0]);
assert_eq!(&drained[3].trace_address, &[0, 1]);
assert_eq!(&drained[4].trace_address, &[0, 2]);
}
}

View File

@ -38,8 +38,7 @@ pub use self::types::filter::{Filter, AddressesFilter};
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use kvdb::DBTransaction; use kvdb::DBTransaction;
use self::trace::{Call, Create}; use vm::{Error as VmError, ActionParams};
use vm::ActionParams;
use header::BlockNumber; use header::BlockNumber;
/// This trait is used by executive to build traces. /// This trait is used by executive to build traces.
@ -47,48 +46,20 @@ pub trait Tracer: Send {
/// Data returned when draining the Tracer. /// Data returned when draining the Tracer.
type Output; type Output;
/// Prepares call trace for given params. Noop tracer should return None. /// Prepares call trace for given params. Would panic if prepare/done_trace are not balanced.
/// fn prepare_trace_call(&mut self, params: &ActionParams, depth: usize, is_builtin: bool);
/// This is called before a call has been executed.
fn prepare_trace_call(&self, params: &ActionParams) -> Option<Call>;
/// Prepares create trace for given params. Noop tracer should return None. /// Prepares create trace for given params. Would panic if prepare/done_trace are not balanced.
/// fn prepare_trace_create(&mut self, params: &ActionParams);
/// This is called before a create has been executed.
fn prepare_trace_create(&self, params: &ActionParams) -> Option<Create>;
/// Stores trace call info. /// Finishes a successful call trace. Would panic if prepare/done_trace are not balanced.
/// fn done_trace_call(&mut self, gas_used: U256, output: &[u8]);
/// This is called after a call has completed successfully.
fn trace_call(
&mut self,
call: Option<Call>,
gas_used: U256,
output: &[u8],
subs: Vec<Self::Output>,
);
/// Stores trace create info. /// Finishes a successful create trace. Would panic if prepare/done_trace are not balanced.
/// fn done_trace_create(&mut self, gas_used: U256, code: &[u8], address: Address);
/// This is called after a create has completed successfully.
fn trace_create(
&mut self,
create: Option<Create>,
gas_used: U256,
code: &[u8],
address: Address,
subs: Vec<Self::Output>
);
/// Stores failed call trace. /// Finishes a failed trace. Would panic if prepare/done_trace are not balanced.
/// fn done_trace_failed(&mut self, error: &VmError);
/// This is called after a call has completed erroneously.
fn trace_failed_call(&mut self, call: Option<Call>, subs: Vec<Self::Output>, error: TraceError);
/// Stores failed create trace.
///
/// This is called after a create has completed erroneously.
fn trace_failed_create(&mut self, create: Option<Create>, subs: Vec<Self::Output>, error: TraceError);
/// Stores suicide info. /// Stores suicide info.
fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address); fn trace_suicide(&mut self, address: Address, balance: U256, refund_address: Address);
@ -96,9 +67,6 @@ pub trait Tracer: Send {
/// Stores reward info. /// Stores reward info.
fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType); fn trace_reward(&mut self, author: Address, value: U256, reward_type: RewardType);
/// Spawn subtracer which will be used to trace deeper levels of execution.
fn subtracer(&self) -> Self where Self: Sized;
/// Consumes self and returns all traces. /// Consumes self and returns all traces.
fn drain(self) -> Vec<Self::Output>; fn drain(self) -> Vec<Self::Output>;
} }
@ -115,16 +83,16 @@ pub trait VMTracer: Send {
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false }
/// Trace the preparation to execute a single valid instruction. /// Trace the preparation to execute a single valid instruction.
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)>) {}
/// Trace the finalised execution of a single valid instruction. /// 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: &[u8]) {}
/// 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(&mut self, _code: &[u8]) {}
/// Finalize subtracer. /// Finalize subtracer.
fn done_subtrace(&mut self, sub: Self) where Self: Sized; fn done_subtrace(&mut self) {}
/// Consumes self and returns the VM trace. /// Consumes self and returns the VM trace.
fn drain(self) -> Option<Self::Output>; fn drain(self) -> Option<Self::Output>;

View File

@ -17,9 +17,9 @@
//! Nonoperative tracer. //! Nonoperative tracer.
use ethereum_types::{U256, Address}; use ethereum_types::{U256, Address};
use vm::ActionParams; use vm::{Error as VmError, ActionParams};
use trace::{Tracer, VMTracer, FlatTrace, TraceError}; use trace::{Tracer, VMTracer, FlatTrace};
use trace::trace::{Call, Create, VMTrace, RewardType}; use trace::trace::{VMTrace, RewardType};
/// Nonoperative tracer. Does not trace anything. /// Nonoperative tracer. Does not trace anything.
pub struct NoopTracer; pub struct NoopTracer;
@ -27,43 +27,14 @@ pub struct NoopTracer;
impl Tracer for NoopTracer { impl Tracer for NoopTracer {
type Output = FlatTrace; type Output = FlatTrace;
fn prepare_trace_call(&self, _: &ActionParams) -> Option<Call> { fn prepare_trace_call(&mut self, _: &ActionParams, _: usize, _: bool) { }
None fn prepare_trace_create(&mut self, _: &ActionParams) { }
} fn done_trace_call(&mut self, _: U256, _: &[u8]) { }
fn done_trace_create(&mut self, _: U256, _: &[u8], _: Address) { }
fn prepare_trace_create(&self, _: &ActionParams) -> Option<Create> { fn done_trace_failed(&mut self, _: &VmError) { }
None fn trace_suicide(&mut self, _: Address, _: U256, _: Address) { }
} fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) { }
fn drain(self) -> Vec<FlatTrace> { vec![] }
fn trace_call(&mut self, call: Option<Call>, _: U256, _: &[u8], _: Vec<FlatTrace>) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
}
fn trace_create(&mut self, create: Option<Create>, _: U256, _: &[u8], _: Address, _: Vec<FlatTrace>) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
}
fn trace_failed_call(&mut self, call: Option<Call>, _: Vec<FlatTrace>, _: TraceError) {
assert!(call.is_none(), "self.prepare_trace_call().is_none(): so we can't be tracing: qed");
}
fn trace_failed_create(&mut self, create: Option<Create>, _: Vec<FlatTrace>, _: TraceError) {
assert!(create.is_none(), "self.prepare_trace_create().is_none(): so we can't be tracing: qed");
}
fn trace_suicide(&mut self, _address: Address, _balance: U256, _refund_address: Address) {
}
fn trace_reward(&mut self, _: Address, _: U256, _: RewardType) {
}
fn subtracer(&self) -> Self {
NoopTracer
}
fn drain(self) -> Vec<FlatTrace> {
vec![]
}
} }
/// Nonoperative VM tracer. Does not trace anything. /// Nonoperative VM tracer. Does not trace anything.
@ -72,15 +43,5 @@ pub struct NoopVMTracer;
impl VMTracer for NoopVMTracer { impl VMTracer for NoopVMTracer {
type Output = VMTrace; type Output = VMTrace;
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false }
fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256) {}
fn trace_executed(&mut self, _gas_used: U256, _stack_push: &[U256], _mem_diff: Option<(usize, &[u8])>, _store_diff: Option<(U256, U256)>) {}
fn prepare_subtrace(&self, _code: &[u8]) -> Self { NoopVMTracer }
fn done_subtrace(&mut self, _sub: Self) {}
fn drain(self) -> Option<VMTrace> { None } fn drain(self) -> Option<VMTrace> { None }
} }

View File

@ -16,7 +16,6 @@
//! Flat trace module //! Flat trace module
use std::collections::VecDeque;
use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError}; use rlp::{Rlp, RlpStream, Decodable, Encodable, DecoderError};
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use ethereum_types::Bloom; use ethereum_types::Bloom;
@ -36,7 +35,7 @@ pub struct FlatTrace {
/// Exact location of trace. /// Exact location of trace.
/// ///
/// [index in root, index in first CALL, index in second CALL, ...] /// [index in root, index in first CALL, index in second CALL, ...]
pub trace_address: VecDeque<usize>, pub trace_address: Vec<usize>,
} }
impl FlatTrace { impl FlatTrace {

View File

@ -16,9 +16,23 @@
//! VM errors module //! VM errors module
use ::{ResumeCall, ResumeCreate};
use ethereum_types::Address;
use action_params::ActionParams;
use std::fmt; use std::fmt;
use ethtrie; use ethtrie;
#[derive(Debug)]
pub enum TrapKind {
Call(ActionParams),
Create(ActionParams, Address),
}
pub enum TrapError<Call, Create> {
Call(ActionParams, Call),
Create(ActionParams, Address, Create),
}
/// VM errors. /// VM errors.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Error { pub enum Error {
@ -76,18 +90,13 @@ impl From<Box<ethtrie::TrieError>> for Error {
Error::Internal(format!("Internal error: {}", err)) Error::Internal(format!("Internal error: {}", err))
} }
} }
impl From<ethtrie::TrieError> for Error { impl From<ethtrie::TrieError> for Error {
fn from(err: ethtrie::TrieError) -> Self { fn from(err: ethtrie::TrieError) -> Self {
Error::Internal(format!("Internal error: {}", err)) Error::Internal(format!("Internal error: {}", err))
} }
} }
// impl From<wasm::RuntimeError> for Error {
// fn from(err: wasm::RuntimeError) -> Self {
// Error::Wasm(format!("Runtime error: {:?}", err))
// }
// }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*; use self::Error::*;
@ -108,3 +117,7 @@ impl fmt::Display for Error {
} }
pub type Result<T> = ::std::result::Result<T, Error>; pub type Result<T> = ::std::result::Result<T, Error>;
pub type TrapResult<T, Call, Create> = ::std::result::Result<Result<T>, TrapError<Call, Create>>;
pub type ExecTrapResult<T> = TrapResult<T, Box<ResumeCall>, Box<ResumeCreate>>;
pub type ExecTrapError = TrapError<Box<ResumeCall>, Box<ResumeCreate>>;

View File

@ -23,8 +23,9 @@ use call_type::CallType;
use env_info::EnvInfo; use env_info::EnvInfo;
use schedule::Schedule; use schedule::Schedule;
use return_data::ReturnData; use return_data::ReturnData;
use error::Result; use error::{Result, TrapKind};
#[derive(Debug)]
/// Result of externalities create function. /// Result of externalities create function.
pub enum ContractCreateResult { pub enum ContractCreateResult {
/// Returned when creation was successfull. /// Returned when creation was successfull.
@ -37,6 +38,7 @@ pub enum ContractCreateResult {
Reverted(U256, ReturnData), Reverted(U256, ReturnData),
} }
#[derive(Debug)]
/// Result of externalities call function. /// Result of externalities call function.
pub enum MessageCallResult { pub enum MessageCallResult {
/// Returned when message call was successfull. /// Returned when message call was successfull.
@ -90,22 +92,31 @@ pub trait Ext {
/// Creates new contract. /// Creates new contract.
/// ///
/// Returns gas_left and contract address if contract creation was succesfull. /// Returns gas_left and contract address if contract creation was succesfull.
fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult; fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind>;
/// Message call. /// Message call.
/// ///
/// Returns Err, if we run out of gas. /// Returns Err, if we run out of gas.
/// Otherwise returns call_result which contains gas left /// Otherwise returns call_result which contains gas left
/// and true if subcall was successfull. /// and true if subcall was successfull.
fn call(&mut self, fn call(
&mut self,
gas: &U256, gas: &U256,
sender_address: &Address, sender_address: &Address,
receive_address: &Address, receive_address: &Address,
value: Option<U256>, value: Option<U256>,
data: &[u8], data: &[u8],
code_address: &Address, code_address: &Address,
call_type: CallType call_type: CallType,
) -> MessageCallResult; trap: bool
) -> ::std::result::Result<MessageCallResult, TrapKind>;
/// Returns code at given address /// Returns code at given address
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>>; fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>>;
@ -149,10 +160,10 @@ pub trait Ext {
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> bool { false } fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _current_gas: U256) -> 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) {} fn trace_prepare_execute(&mut self, _pc: usize, _instruction: u8, _gas_cost: U256, _mem_written: Option<(usize, usize)>, _store_written: Option<(U256, 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: &[u8]) {}
/// Check if running in static context. /// Check if running in static context.
fn is_static(&self) -> bool; fn is_static(&self) -> bool;

View File

@ -41,12 +41,24 @@ pub use env_info::{EnvInfo, LastHashes};
pub use schedule::{Schedule, CleanDustMode, WasmCosts}; pub use schedule::{Schedule, CleanDustMode, WasmCosts};
pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress}; pub use ext::{Ext, MessageCallResult, ContractCreateResult, CreateContractAddress};
pub use return_data::{ReturnData, GasLeft}; pub use return_data::{ReturnData, GasLeft};
pub use error::{Error, Result}; pub use error::{Error, Result, TrapResult, TrapError, TrapKind, ExecTrapResult, ExecTrapError};
/// Virtual Machine interface /// Virtual Machine interface
pub trait Vm { pub trait Exec: Send {
/// This function should be used to execute transaction. /// This function should be used to execute transaction.
/// It returns either an error, a known amount of gas left, or parameters to be used /// It returns either an error, a known amount of gas left, or parameters to be used
/// to compute the final gas left. /// to compute the final gas left.
fn exec(&mut self, ext: &mut Ext) -> Result<GasLeft>; fn exec(self: Box<Self>, ext: &mut Ext) -> ExecTrapResult<GasLeft>;
}
/// Resume call interface
pub trait ResumeCall: Send {
/// Resume an execution for call, returns back the Vm interface.
fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<Exec>;
}
/// Resume create interface
pub trait ResumeCreate: Send {
/// Resume an execution from create, returns back the Vm interface.
fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<Exec>;
} }

View File

@ -25,6 +25,7 @@ use {
CreateContractAddress, Result, GasLeft, CreateContractAddress, Result, GasLeft,
}; };
use hash::keccak; use hash::keccak;
use error::TrapKind;
pub struct FakeLogEntry { pub struct FakeLogEntry {
pub topics: Vec<H256>, pub topics: Vec<H256>,
@ -138,7 +139,14 @@ impl Ext for FakeExt {
self.blockhashes.get(number).unwrap_or(&H256::new()).clone() self.blockhashes.get(number).unwrap_or(&H256::new()).clone()
} }
fn create(&mut self, gas: &U256, value: &U256, code: &[u8], address: CreateContractAddress) -> ContractCreateResult { fn create(
&mut self,
gas: &U256,
value: &U256,
code: &[u8],
address: CreateContractAddress,
_trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind> {
self.calls.insert(FakeCall { self.calls.insert(FakeCall {
call_type: FakeCallType::Create, call_type: FakeCallType::Create,
create_scheme: Some(address), create_scheme: Some(address),
@ -149,19 +157,21 @@ impl Ext for FakeExt {
data: code.to_vec(), data: code.to_vec(),
code_address: None code_address: None
}); });
ContractCreateResult::Failed // TODO: support traps in testing.
Ok(ContractCreateResult::Failed)
} }
fn call(&mut self, fn call(
gas: &U256, &mut self,
sender_address: &Address, gas: &U256,
receive_address: &Address, sender_address: &Address,
value: Option<U256>, receive_address: &Address,
data: &[u8], value: Option<U256>,
code_address: &Address, data: &[u8],
_call_type: CallType code_address: &Address,
) -> MessageCallResult { _call_type: CallType,
_trap: bool,
) -> ::std::result::Result<MessageCallResult, TrapKind> {
self.calls.insert(FakeCall { self.calls.insert(FakeCall {
call_type: FakeCallType::Call, call_type: FakeCallType::Call,
create_scheme: None, create_scheme: None,
@ -172,7 +182,8 @@ impl Ext for FakeExt {
data: data.to_vec(), data: data.to_vec(),
code_address: Some(code_address.clone()) code_address: Some(code_address.clone())
}); });
MessageCallResult::Success(*gas, ReturnData::empty()) // TODO: support traps in testing.
Ok(MessageCallResult::Success(*gas, ReturnData::empty()))
} }
fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>> { fn extcode(&self, address: &Address) -> Result<Option<Arc<Bytes>>> {

View File

@ -16,7 +16,7 @@
use fixture::{Fixture, Assert, CallLocator, Source}; use fixture::{Fixture, Assert, CallLocator, Source};
use wasm::WasmInterpreter; use wasm::WasmInterpreter;
use vm::{self, Vm, GasLeft, ActionParams, ActionValue, ParamsType}; use vm::{self, Exec, GasLeft, ActionParams, ActionValue, ParamsType};
use vm::tests::FakeExt; use vm::tests::FakeExt;
use std::io::{self, Read}; use std::io::{self, Read};
use std::{fs, path, fmt}; use std::{fs, path, fmt};
@ -31,8 +31,8 @@ fn load_code<P: AsRef<path::Path>>(p: P) -> io::Result<Vec<u8>> {
Ok(result) Ok(result)
} }
fn wasm_interpreter(params: ActionParams) -> WasmInterpreter { fn wasm_interpreter(params: ActionParams) -> Box<WasmInterpreter> {
WasmInterpreter::new(params) Box::new(WasmInterpreter::new(params))
} }
#[derive(Debug)] #[derive(Debug)]
@ -131,7 +131,7 @@ pub fn construct(
params.params_type = ParamsType::Separate; params.params_type = ParamsType::Separate;
Ok( Ok(
match wasm_interpreter(params).exec(ext)? { match wasm_interpreter(params).exec(ext).ok().expect("Wasm interpreter always calls with trap=false; trap never happens; qed")? {
GasLeft::Known(_) => Vec::new(), GasLeft::Known(_) => Vec::new(),
GasLeft::NeedsReturn { data, .. } => data.to_vec(), GasLeft::NeedsReturn { data, .. } => data.to_vec(),
} }
@ -192,9 +192,9 @@ pub fn run_fixture(fixture: &Fixture) -> Vec<Fail> {
} }
} }
let mut interpreter = wasm_interpreter(params); let interpreter = wasm_interpreter(params);
let interpreter_return = match interpreter.exec(&mut ext) { let interpreter_return = match interpreter.exec(&mut ext).ok().expect("Wasm interpreter always calls with trap=false; trap never happens; qed") {
Ok(ret) => ret, Ok(ret) => ret,
Err(e) => { return Fail::runtime(e); } Err(e) => { return Fail::runtime(e); }
}; };

View File

@ -91,9 +91,8 @@ enum ExecutionOutcome {
NotSpecial, NotSpecial,
} }
impl vm::Vm for WasmInterpreter { impl WasmInterpreter {
pub fn run(self: Box<WasmInterpreter>, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
fn exec(&mut self, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?; let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?;
let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?; let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?;
@ -190,3 +189,9 @@ impl vm::Vm for WasmInterpreter {
} }
} }
} }
impl vm::Exec for WasmInterpreter {
fn exec(self: Box<WasmInterpreter>, ext: &mut vm::Ext) -> vm::ExecTrapResult<GasLeft> {
Ok(self.run(ext))
}
}

View File

@ -450,7 +450,8 @@ impl<'a> Runtime<'a> {
&payload, &payload,
&address, &address,
call_type, call_type,
); false
).ok().expect("Trap is false; trap error will not happen; qed");
match call_result { match call_result {
vm::MessageCallResult::Success(gas_left, data) => { vm::MessageCallResult::Success(gas_left, data) => {
@ -528,7 +529,7 @@ impl<'a> Runtime<'a> {
* U256::from(self.ext.schedule().wasm().opcodes_mul) * U256::from(self.ext.schedule().wasm().opcodes_mul)
/ U256::from(self.ext.schedule().wasm().opcodes_div); / U256::from(self.ext.schedule().wasm().opcodes_div);
match self.ext.create(&gas_left, &endowment, &code, scheme) { match self.ext.create(&gas_left, &endowment, &code, scheme, false).ok().expect("Trap is false; trap error will not happen; qed") {
vm::ContractCreateResult::Created(address, gas_left) => { vm::ContractCreateResult::Created(address, gas_left) => {
self.memory.set(result_ptr, &*address)?; self.memory.set(result_ptr, &*address)?;
self.gas_counter = self.gas_limit - self.gas_counter = self.gas_limit -

View File

@ -20,7 +20,7 @@ use byteorder::{LittleEndian, ByteOrder};
use ethereum_types::{H256, U256, Address}; use ethereum_types::{H256, U256, Address};
use super::WasmInterpreter; use super::WasmInterpreter;
use vm::{self, Vm, GasLeft, ActionParams, ActionValue, CreateContractAddress}; use vm::{self, Exec, GasLeft, ActionParams, ActionValue, CreateContractAddress};
use vm::tests::{FakeCall, FakeExt, FakeCallType}; use vm::tests::{FakeCall, FakeExt, FakeCallType};
macro_rules! load_sample { macro_rules! load_sample {
@ -48,7 +48,7 @@ macro_rules! reqrep_test {
fake_ext.blockhashes = $block_hashes; fake_ext.blockhashes = $block_hashes;
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
interpreter.exec(&mut fake_ext) interpreter.exec(&mut fake_ext).ok().unwrap()
.map(|result| match result { .map(|result| match result {
GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); }, GasLeft::Known(_) => { panic!("Test is expected to return payload to check"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -65,8 +65,8 @@ fn test_finalize(res: Result<GasLeft, vm::Error>) -> Result<U256, vm::Error> {
} }
} }
fn wasm_interpreter(params: ActionParams) -> WasmInterpreter { fn wasm_interpreter(params: ActionParams) -> Box<WasmInterpreter> {
WasmInterpreter::new(params) Box::new(WasmInterpreter::new(params))
} }
/// Empty contract does almost nothing except producing 1 (one) local node debug log message /// Empty contract does almost nothing except producing 1 (one) local node debug log message
@ -83,7 +83,7 @@ fn empty() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
test_finalize(interpreter.exec(&mut ext)).unwrap() test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap()
}; };
assert_eq!(gas_left, U256::from(96_926)); assert_eq!(gas_left, U256::from(96_926));
@ -112,7 +112,7 @@ fn logger() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
test_finalize(interpreter.exec(&mut ext)).unwrap() test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap()
}; };
let address_val: H256 = address.into(); let address_val: H256 = address.into();
@ -161,7 +161,7 @@ fn identity() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("Identity contract should return payload"); }, GasLeft::Known(_) => { panic!("Identity contract should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -196,7 +196,7 @@ fn dispersion() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("Dispersion routine should return payload"); }, GasLeft::Known(_) => { panic!("Dispersion routine should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -224,7 +224,7 @@ fn suicide_not() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("Suicidal contract should return payload when had not actualy killed himself"); }, GasLeft::Known(_) => { panic!("Suicidal contract should return payload when had not actualy killed himself"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -257,7 +257,7 @@ fn suicide() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(gas) => gas, GasLeft::Known(gas) => gas,
GasLeft::NeedsReturn { .. } => { GasLeft::NeedsReturn { .. } => {
@ -285,7 +285,7 @@ fn create() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { GasLeft::Known(_) => {
panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero."); panic!("Create contract always return 40 bytes of the creation address, or in the case where it fails, return 40 bytes of zero.");
@ -347,7 +347,7 @@ fn call_msg() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(gas_left) => gas_left, GasLeft::Known(gas_left) => gas_left,
GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); },
@ -395,7 +395,7 @@ fn call_msg_gasleft() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(gas_left) => gas_left, GasLeft::Known(gas_left) => gas_left,
GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); }, GasLeft::NeedsReturn { .. } => { panic!("Call test should not return payload"); },
@ -438,7 +438,7 @@ fn call_code() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("Call test should return payload"); }, GasLeft::Known(_) => { panic!("Call test should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -486,7 +486,7 @@ fn call_static() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("Static call test should return payload"); }, GasLeft::Known(_) => { panic!("Static call test should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -527,7 +527,7 @@ fn realloc() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("Realloc should return payload"); }, GasLeft::Known(_) => { panic!("Realloc should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -549,7 +549,7 @@ fn alloc() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("alloc test should return payload"); }, GasLeft::Known(_) => { panic!("alloc test should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -576,7 +576,7 @@ fn storage_read() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("storage_read should return payload"); }, GasLeft::Known(_) => { panic!("storage_read should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -602,7 +602,7 @@ fn keccak() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("keccak should return payload"); }, GasLeft::Known(_) => { panic!("keccak should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -749,7 +749,7 @@ fn storage_metering() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
test_finalize(interpreter.exec(&mut ext)).unwrap() test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap()
}; };
// 0 -> not 0 // 0 -> not 0
@ -768,7 +768,7 @@ fn storage_metering() {
let gas_left = { let gas_left = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
test_finalize(interpreter.exec(&mut ext)).unwrap() test_finalize(interpreter.exec(&mut ext).ok().unwrap()).unwrap()
}; };
// not 0 -> not 0 // not 0 -> not 0
@ -875,8 +875,8 @@ fn gasleft() {
let mut ext = FakeExt::new().with_wasm(); let mut ext = FakeExt::new().with_wasm();
ext.schedule.wasm.as_mut().unwrap().have_gasleft = true; ext.schedule.wasm.as_mut().unwrap().have_gasleft = true;
let mut interpreter = wasm_interpreter(params); let interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => {}, GasLeft::Known(_) => {},
GasLeft::NeedsReturn { gas_left, data, .. } => { GasLeft::NeedsReturn { gas_left, data, .. } => {
@ -897,8 +897,8 @@ fn gasleft_fail() {
params.gas = U256::from(100_000); params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("gasleft.wasm"))); params.code = Some(Arc::new(load_sample!("gasleft.wasm")));
let mut ext = FakeExt::new().with_wasm(); let mut ext = FakeExt::new().with_wasm();
let mut interpreter = wasm_interpreter(params); let interpreter = wasm_interpreter(params);
match interpreter.exec(&mut ext) { match interpreter.exec(&mut ext).ok().unwrap() {
Err(_) => {}, Err(_) => {},
Ok(_) => panic!("interpreter.exec should return Err if ext.schedule.wasm.have_gasleft = false") Ok(_) => panic!("interpreter.exec should return Err if ext.schedule.wasm.have_gasleft = false")
} }
@ -919,7 +919,7 @@ fn embedded_keccak() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("keccak should return payload"); }, GasLeft::Known(_) => { panic!("keccak should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -947,7 +947,7 @@ fn events() {
let (gas_left, result) = { let (gas_left, result) = {
let mut interpreter = wasm_interpreter(params); let mut interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext).expect("Interpreter to execute without any errors"); let result = interpreter.exec(&mut ext).ok().unwrap().expect("Interpreter to execute without any errors");
match result { match result {
GasLeft::Known(_) => { panic!("events should return payload"); }, GasLeft::Known(_) => { panic!("events should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()), GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
@ -986,8 +986,8 @@ fn recursive() {
let mut ext = FakeExt::new().with_wasm(); let mut ext = FakeExt::new().with_wasm();
let mut interpreter = wasm_interpreter(params); let interpreter = wasm_interpreter(params);
let result = interpreter.exec(&mut ext); let result = interpreter.exec(&mut ext).ok().unwrap();
// We expect that stack overflow will occur and it should be generated by // We expect that stack overflow will occur and it should be generated by
// deterministic stack metering. Exceeding deterministic stack height limit // deterministic stack metering. Exceeding deterministic stack height limit

View File

@ -35,14 +35,28 @@ pub struct Informant {
instruction: u8, instruction: u8,
gas_cost: U256, gas_cost: U256,
gas_used: U256, gas_used: U256,
mem_written: Option<(usize, usize)>,
store_written: Option<(U256, U256)>,
stack: Vec<U256>, stack: Vec<U256>,
memory: Vec<u8>, memory: Vec<u8>,
storage: HashMap<H256, H256>, storage: HashMap<H256, H256>,
traces: Vec<String>, traces: Vec<String>,
subtraces: Vec<String>, subtraces: Vec<String>,
subinfos: Vec<Informant>,
subdepth: usize,
unmatched: bool, 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 { impl vm::Informant for Informant {
fn before_test(&mut self, name: &str, action: &str) { fn before_test(&mut self, name: &str, action: &str) {
println!("{}", json!({"action": action, "test": name})); println!("{}", json!({"action": action, "test": name}));
@ -88,72 +102,92 @@ impl trace::VMTracer for Informant {
type Output = Vec<String>; type Output = Vec<String>;
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 {
self.pc = pc; let subdepth = self.subdepth;
self.instruction = instruction; Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
self.unmatched = true; informant.pc = pc;
informant.instruction = instruction;
informant.unmatched = true;
});
true 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)>) {
self.pc = pc; let subdepth = self.subdepth;
self.instruction = instruction; Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
self.gas_cost = gas_cost; informant.pc = pc;
} informant.instruction = instruction;
informant.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)>) { informant.mem_written = mem_written;
let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info()); informant.store_written = store_written;
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,
}); });
}
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; let trace = json!({
self.gas_used = gas_used; "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(); informant.unmatched = false;
let info_args = info.map(|i| i.args).unwrap_or(0); informant.gas_used = gas_used;
self.stack.truncate(if len > info_args { len - info_args } else { 0 });
self.stack.extend_from_slice(stack_push);
// TODO [ToDr] Align memory? let len = informant.stack.len();
if let Some((pos, data)) = mem_diff { let info_args = info.map(|i| i.args).unwrap_or(0);
if self.memory.len() < (pos + data.len()) { informant.stack.truncate(if len > info_args { len - info_args } else { 0 });
self.memory.resize(pos + data.len(), 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 { if let Some((pos, val)) = store_diff {
self.storage.insert(pos.into(), val.into()); informant.storage.insert(pos.into(), val.into());
} }
if !self.subtraces.is_empty() { if !informant.subtraces.is_empty() {
self.traces.extend(mem::replace(&mut self.subtraces, vec![])); informant.traces.extend(mem::replace(&mut informant.subtraces, vec![]));
} }
});
} }
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized { fn prepare_subtrace(&mut self, code: &[u8]) {
let mut vm = Informant::default(); let subdepth = self.subdepth;
vm.depth = self.depth + 1; Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant| {
vm.code = code.to_vec(); let mut vm = Informant::default();
vm.gas_used = self.gas_used; vm.depth = informant.depth + 1;
vm 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) { fn done_subtrace(&mut self) {
if let Some(subtraces) = sub.drain() { self.subdepth -= 1;
self.subtraces.extend(subtraces); 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> { fn drain(mut self) -> Option<Self::Output> {
@ -161,7 +195,7 @@ impl trace::VMTracer for Informant {
// print last line with final state: // print last line with final state:
self.gas_cost = 0.into(); self.gas_cost = 0.into();
let gas_used = self.gas_used; let gas_used = self.gas_used;
self.trace_executed(gas_used, &[], None, None); self.trace_executed(gas_used, &[], &[]);
} else if !self.subtraces.is_empty() { } else if !self.subtraces.is_empty() {
self.traces.extend(mem::replace(&mut self.subtraces, vec![])); 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 { impl trace::VMTracer for Informant {
type Output = (); type Output = ();
fn prepare_subtrace(&self, _code: &[u8]) -> Self where Self: Sized { Default::default() } fn prepare_subtrace(&mut self, _code: &[u8]) { Default::default() }
fn done_subtrace(&mut self, _sub: Self) {} fn done_subtrace(&mut self) {}
fn drain(self) -> Option<()> { None } fn drain(self) -> Option<()> { None }
} }

View File

@ -58,6 +58,8 @@ pub struct Informant<Trace = io::Stderr, Out = io::Stdout> {
depth: usize, depth: usize,
stack: Vec<U256>, stack: Vec<U256>,
storage: HashMap<H256, H256>, storage: HashMap<H256, H256>,
subinfos: Vec<Informant<Trace, Out>>,
subdepth: usize,
trace_sink: Trace, trace_sink: Trace,
out_sink: Out, out_sink: Out,
} }
@ -76,9 +78,19 @@ impl<Trace: Writer, Out: Writer> Informant<Trace, Out> {
depth: Default::default(), depth: Default::default(),
stack: Default::default(), stack: Default::default(),
storage: Default::default(), storage: Default::default(),
subinfos: Default::default(),
subdepth: 0,
trace_sink, out_sink 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> { 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 = (); 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 {
let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info()); let subdepth = self.subdepth;
self.instruction = instruction; Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
let trace_data = json!({ let info = ::evm::Instruction::from_u8(instruction).map(|i| i.info());
"pc": pc, informant.instruction = instruction;
"op": instruction, let trace_data = json!({
"opName": info.map(|i| i.name).unwrap_or(""), "pc": pc,
"gas": format!("{:#x}", current_gas), "op": instruction,
"stack": self.stack, "opName": info.map(|i| i.name).unwrap_or(""),
"storage": self.storage, "gas": format!("{:#x}", current_gas),
"depth": self.depth, "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 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)>) { fn trace_executed(&mut self, _gas_used: U256, stack_push: &[U256], _mem: &[u8]) {
let info = ::evm::Instruction::from_u8(self.instruction).map(|i| i.info()); 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 len = informant.stack.len();
let info_args = info.map(|i| i.args).unwrap_or(0); let info_args = info.map(|i| i.args).unwrap_or(0);
self.stack.truncate(if len > info_args { len - info_args } else { 0 }); informant.stack.truncate(if len > info_args { len - info_args } else { 0 });
self.stack.extend_from_slice(stack_push); informant.stack.extend_from_slice(stack_push);
});
if let Some((pos, val)) = store_diff {
self.storage.insert(pos.into(), val.into());
}
} }
fn prepare_subtrace(&self, code: &[u8]) -> Self where Self: Sized { fn prepare_subtrace(&mut self, code: &[u8]) {
let mut vm = Informant::new(self.trace_sink.clone(), self.out_sink.clone()); let subdepth = self.subdepth;
vm.depth = self.depth + 1; Self::with_informant_in_depth(self, subdepth, |informant: &mut Informant<Trace, Out>| {
vm.code = code.to_vec(); let mut vm = Informant::new(informant.trace_sink.clone(), informant.out_sink.clone());
vm 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 } fn drain(self) -> Option<Self::Output> { None }
} }