Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
31c1130
Add new Vm trappable interface
sorpaas Aug 15, 2018
7e8c0b5
Exec/Resume interface
sorpaas Aug 15, 2018
dda51e8
Basic implementation of CallCreateExecutive
sorpaas Aug 15, 2018
f98195e
Implement resume_call and resume_create for executive
sorpaas Aug 15, 2018
10b7f30
Move convertion to call/create result to separate function
sorpaas Aug 15, 2018
458a31c
Implement consume that converts resumable to non-resumable
sorpaas Aug 15, 2018
851f5b4
Use consume for Executive::call/create
sorpaas Aug 15, 2018
4625943
Resumable EVM
sorpaas Aug 16, 2018
354aaa7
Implement tracing mode without needing subtracers
sorpaas Aug 20, 2018
e27ff3e
Implement vmtracer so it doesn't require extra structs for subtracing
sorpaas Aug 20, 2018
9be2d3d
Use the new tracing mode in executive
sorpaas Aug 20, 2018
e8683d3
Fix most of the linting errors for cargo build
sorpaas Aug 20, 2018
c8c8377
Add the concept of stack_depth
sorpaas Aug 20, 2018
cdff7c9
Add back crossbeam
sorpaas Aug 20, 2018
2042397
Fix some test compile
sorpaas Aug 20, 2018
4fe09ad
Fix prefix address test
sorpaas Aug 20, 2018
635c11f
Fix evm crate tests
sorpaas Aug 20, 2018
8d391bb
Fix wasm crate test compile
sorpaas Aug 20, 2018
9b0dc13
Fix wasm runner compile
sorpaas Aug 20, 2018
98b92c1
Fix jsontests compile
sorpaas Aug 20, 2018
fcbeff1
Fix evmbin compile
sorpaas Aug 20, 2018
f6e4d77
Fix an issue with create nonce and better vm tracing interface
sorpaas Aug 21, 2018
5088854
Fix linting
sorpaas Aug 21, 2018
4b7c6de
Fix evmbin compile
sorpaas Aug 21, 2018
0bceec2
Fix unconfirmed_substate and static_flag
sorpaas Aug 21, 2018
eab180f
Fix an issue in create address logic
sorpaas Aug 21, 2018
8aef879
Fix top-level tracing
sorpaas Aug 22, 2018
d766e37
Handle builtin tracing
sorpaas Aug 22, 2018
b159734
Fix suicide and reward tracing index stack
sorpaas Aug 22, 2018
9581ad4
Fix an issue where trap conflicts with tracing
sorpaas Aug 22, 2018
41ad7ee
Fix an issue in parent step vm tracing
sorpaas Aug 22, 2018
d033fe8
Fix revert tracing
sorpaas Aug 22, 2018
434f1d3
Fix evmbin tests
sorpaas Aug 22, 2018
37aa837
Remove params clone
sorpaas Aug 23, 2018
bb8fae5
Fix TODO proofs
sorpaas Aug 23, 2018
0ebefdb
Fix jsontests compile
sorpaas Aug 23, 2018
03e6119
Merge branch 'master' into sp-resume-executive2
sorpaas Aug 31, 2018
9f20224
Fix evmbin merge issue
sorpaas Aug 31, 2018
42cdd7c
Fix wasm merge issue
sorpaas Aug 31, 2018
28676e2
Fix wasm test
sorpaas Aug 31, 2018
2c5f285
Merge branch 'master' of https://github.com/paritytech/parity into sp…
sorpaas Sep 13, 2018
ec30751
Fix ethcore merge warnings
sorpaas Sep 13, 2018
4a6b559
Fix evmbin compile
sorpaas Sep 13, 2018
cf3f12f
Merge branch 'master' of https://github.com/paritytech/parity into sp…
sorpaas Sep 17, 2018
f485ce2
Merge branch 'master' into sp-resume-executive2
sorpaas Sep 17, 2018
9544f1d
Merge branch 'master' into sp-resume-executive2
debris Sep 24, 2018
60cf639
Merge branch 'master' of https://github.com/paritytech/parity into sp…
sorpaas Sep 25, 2018
e9916f6
Merge branch 'master' of https://github.com/paritytech/parity into sp…
sorpaas Oct 2, 2018
9c603c6
Better expect messages and add some trace::skip_one asserts
sorpaas Oct 2, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ethcore/evm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ impl Finalize for Error {
}

/// 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::Shr<usize, Output=Self> + ops::Shl<usize, Output=Self>
+ cmp::Ord + fmt::Debug {
Expand Down
4 changes: 2 additions & 2 deletions ethcore/evm/src/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
//! Evm factory.
//!
use std::sync::Arc;
use vm::{Vm, Schedule};
use vm::{Exec, Schedule};
use ethereum_types::U256;
use super::vm::ActionParams;
use super::interpreter::SharedCache;
Expand All @@ -33,7 +33,7 @@ pub struct Factory {
impl Factory {
/// Create fresh instance of VM
/// 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 {
VMType::Interpreter => if Self::can_fit_in_usize(&params.gas) {
Box::new(super::interpreter::Interpreter::<usize>::new(params, self.evm_cache.clone(), schedule, depth))
Expand Down
201 changes: 144 additions & 57 deletions ethcore/evm/src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ use ethereum_types::{U256, U512, H256, Address};

use vm::{
self, ActionParams, ParamsType, ActionValue, CallType, MessageCallResult,
ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule
ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule,
TrapKind, TrapError
};

use evm::CostType;
Expand Down Expand Up @@ -103,6 +104,7 @@ enum InstructionResult<Gas> {
apply: bool,
},
StopExecution,
Trap(TrapKind),
}

enum Never {}
Expand Down Expand Up @@ -161,6 +163,7 @@ pub enum InterpreterResult {
Done(vm::Result<GasLeft>),
/// The VM can continue to run.
Continue,
Trap(TrapKind),
}

impl From<vm::Error> for InterpreterResult {
Expand All @@ -182,22 +185,89 @@ pub struct Interpreter<Cost: CostType> {
valid_jump_destinations: Option<Arc<BitSet>>,
gasometer: Option<Gasometer<Cost>>,
stack: VecStack<U256>,
resume_output_range: Option<(U256, U256)>,
resume_result: Option<InstructionResult<Cost>>,
last_stack_ret_len: usize,
_type: PhantomData<Cost>,
}

impl<Cost: CostType> vm::Vm for Interpreter<Cost> {
fn exec(&mut self, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
impl<Cost: 'static + CostType> vm::Exec for Interpreter<Cost> {
fn exec(mut self: Box<Self>, ext: &mut vm::Ext) -> vm::ExecTrapResult<GasLeft> {
loop {
let result = self.step(ext);
match result {
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.")
}
}
}
}

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> {
/// Create a new `Interpreter` instance with shared cache.
pub fn new(mut params: ActionParams, cache: Arc<SharedCache>, schedule: &Schedule, depth: usize) -> Interpreter<Cost> {
Expand All @@ -215,6 +285,9 @@ impl<Cost: CostType> Interpreter<Cost> {
do_trace: true,
mem: Vec::new(),
return_data: ReturnData::empty(),
last_stack_ret_len: 0,
resume_output_range: None,
resume_result: None,
_type: PhantomData,
}
}
Expand Down Expand Up @@ -244,50 +317,57 @@ impl<Cost: CostType> Interpreter<Cost> {
/// Inner helper function for step.
#[inline(always)]
fn step_inner(&mut self, ext: &mut vm::Ext) -> Result<Never, InterpreterResult> {
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.
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(),
);

let instruction = match instruction {
Some(i) => i,
None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction {
instruction: opcode
}))),
};
let result = match self.resume_result.take() {
Some(result) => result,
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.
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(),
);

let info = instruction.info();
self.verify_instruction(ext, instruction, info)?;
let instruction = match instruction {
Some(i) => i,
None => return Err(InterpreterResult::Done(Err(vm::Error::BadInstruction {
instruction: opcode
}))),
};

// 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());
}
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;
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) });
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
)?;

// 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
},
};

evm_debug!({ self.informant.after_instruction(instruction) });
if let InstructionResult::Trap(trap) = result {
return Err(InterpreterResult::Trap(trap));
}

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;
Expand All @@ -296,9 +376,8 @@ impl<Cost: CostType> Interpreter<Cost> {
if self.do_trace {
ext.trace_executed(
self.gasometer.as_mut().expect(GASOMETER_PROOF).current_gas.as_u256(),
self.stack.peek_top(info.ret),
mem_written.map(|(o, s)| (o, &(self.mem[o..o+s]))),
store_written,
self.stack.peek_top(self.last_stack_ret_len),
&self.mem,
);
}

Expand Down Expand Up @@ -451,21 +530,24 @@ impl<Cost: CostType> Interpreter<Cost> {

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 {
ContractCreateResult::Created(address, gas_left) => {
Ok(ContractCreateResult::Created(address, gas_left)) => {
self.stack.push(address_to_u256(address));
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.return_data = return_data;
Ok(InstructionResult::UnusedGas(Cost::from_u256(gas_left).expect("Gas left cannot be greater.")))
},
ContractCreateResult::Failed => {
Ok(ContractCreateResult::Failed) => {
self.stack.push(U256::zero());
Ok(InstructionResult::Ok)
},
Err(trap) => {
Ok(InstructionResult::Trap(trap))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is Err turned into Ok here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If ext.create returns Err, it can only be a trap. (That also means that if trap parameter is passed false, Err case is impossible.) So in here if we want to trap the interpreter, we can directly match this.

We really have three cases for the exec_instruction return type:

  • vm::Result::Err which indicates some errors happened.
  • vm::Result::Ok(Instruction::Result::Trap(..)) which indicates that a trap in the EVM happened.
  • vm::Result::Ok(..) which indicates some other instruction results.

Putting Trap to error and wrap vm::Result::Err can also work, but it means that we need to replicate a lot more From/Into impls to support ? syntax in this function, and I think it may be a little bit overkill.

},
};
},
instructions::CALL | instructions::CALLCODE | instructions::DELEGATECALL | instructions::STATICCALL => {
Expand Down Expand Up @@ -524,32 +606,37 @@ impl<Cost: CostType> Interpreter<Cost> {

let call_result = {
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 {
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());
(&mut output[..len]).copy_from_slice(&data[..len]);

self.stack.push(U256::one());
self.return_data = data;
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());
(&mut output[..len]).copy_from_slice(&data[..len]);

self.stack.push(U256::zero());
self.return_data = data;
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());
Ok(InstructionResult::Ok)
},
Err(trap) => {
Ok(InstructionResult::Trap(trap))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is Err turned into Ok here? I just don't understand the flow :)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the same case as #9360 (comment)

},
};
},
instructions::RETURN => {
Expand Down Expand Up @@ -1095,10 +1182,10 @@ mod tests {
use rustc_hex::FromHex;
use vmtype::VMType;
use factory::Factory;
use vm::{self, Vm, ActionParams, ActionValue};
use vm::{self, Exec, ActionParams, ActionValue};
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())
}

Expand All @@ -1118,7 +1205,7 @@ mod tests {

let gas_left = {
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);
Expand All @@ -1140,7 +1227,7 @@ mod tests {

let err = {
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);
Expand Down
Loading