Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Decouple virtual machines (#6184)
Browse files Browse the repository at this point in the history
* work in progress for splitting vms

* evm working

* Evm -> Vm

* wasm converted

* ethcore working

* test fixes
  • Loading branch information
NikVolf authored Aug 1, 2017
1 parent c402562 commit b700603
Show file tree
Hide file tree
Showing 65 changed files with 534 additions and 400 deletions.
34 changes: 32 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ethcore/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ semver = "0.6"
stats = { path = "../util/stats" }
time = "0.1"
transient-hashmap = "0.4"
vm = { path = "vm" }
wasm = { path = "wasm" }

[dev-dependencies]
native-contracts = { path = "native_contracts", features = ["test_contracts"] }
Expand Down
1 change: 1 addition & 0 deletions ethcore/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ethjson = { path = "../../json" }
lazy_static = "0.2"
log = "0.3"
rlp = { path = "../../util/rlp" }
vm = { path = "../vm" }
parity-wasm = "0.12"
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }

Expand Down
2 changes: 1 addition & 1 deletion ethcore/evm/src/benches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extern crate test;
use self::test::{Bencher, black_box};

use util::*;
use evm::action_params::ActionParams;
use vm::ActionParams;
use evm::{self, Factory, VMType};
use evm::tests::FakeExt;

Expand Down
147 changes: 2 additions & 145 deletions ethcore/evm/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,142 +17,8 @@
//! Evm interface.
use std::{ops, cmp, fmt};
use util::{U128, U256, U512, trie};
use action_params::ActionParams;
use {Ext};

use super::wasm;

/// Evm errors.
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
/// `OutOfGas` is returned when transaction execution runs out of gas.
/// The state should be reverted to the state from before the
/// transaction execution. But it does not mean that transaction
/// was invalid. Balance still should be transfered and nonce
/// should be increased.
OutOfGas,
/// `BadJumpDestination` is returned when execution tried to move
/// to position that wasn't marked with JUMPDEST instruction
BadJumpDestination {
/// Position the code tried to jump to.
destination: usize
},
/// `BadInstructions` is returned when given instruction is not supported
BadInstruction {
/// Unrecognized opcode
instruction: u8,
},
/// `StackUnderflow` when there is not enough stack elements to execute instruction
StackUnderflow {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements was requested by instruction
wanted: usize,
/// How many elements were on stack
on_stack: usize
},
/// When execution would exceed defined Stack Limit
OutOfStack {
/// Invoked instruction
instruction: &'static str,
/// How many stack elements instruction wanted to push
wanted: usize,
/// What was the stack limit
limit: usize
},
/// Built-in contract failed on given input
BuiltIn(&'static str),
/// When execution tries to modify the state in static context
MutableCallInStaticContext,
/// Likely to cause consensus issues.
Internal(String),
/// Wasm runtime error
Wasm(String),
}

impl From<Box<trie::TrieError>> for Error {
fn from(err: Box<trie::TrieError>) -> Self {
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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match *self {
OutOfGas => write!(f, "Out of gas"),
BadJumpDestination { destination } => write!(f, "Bad jump destination {:x}", destination),
BadInstruction { instruction } => write!(f, "Bad instruction {:x}", instruction),
StackUnderflow { instruction, wanted, on_stack } => write!(f, "Stack underflow {} {}/{}", instruction, wanted, on_stack),
OutOfStack { instruction, wanted, limit } => write!(f, "Out of stack {} {}/{}", instruction, wanted, limit),
BuiltIn(name) => write!(f, "Built-in failed: {}", name),
Internal(ref msg) => write!(f, "Internal error: {}", msg),
MutableCallInStaticContext => write!(f, "Mutable call in static context"),
Wasm(ref msg) => write!(f, "Internal error: {}", msg),
}
}
}

/// A specialized version of Result over EVM errors.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Return data buffer. Holds memory from a previous call and a slice into that memory.
#[derive(Debug)]
pub struct ReturnData {
mem: Vec<u8>,
offset: usize,
size: usize,
}

impl ::std::ops::Deref for ReturnData {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.mem[self.offset..self.offset + self.size]
}
}

impl ReturnData {
/// Create empty `ReturnData`.
pub fn empty() -> Self {
ReturnData {
mem: Vec::new(),
offset: 0,
size: 0,
}
}
/// Create `ReturnData` from give buffer and slice.
pub fn new(mem: Vec<u8>, offset: usize, size: usize) -> Self {
ReturnData {
mem: mem,
offset: offset,
size: size,
}
}
}

/// Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
#[derive(Debug)]
pub enum GasLeft {
/// Known gas left
Known(U256),
/// Return or Revert instruction must be processed.
NeedsReturn {
/// Amount of gas left.
gas_left: U256,
/// Return data buffer.
data: ReturnData,
/// Apply or revert state changes on revert.
apply_state: bool
},
}
use util::{U128, U256, U512};
use vm::{Ext, Result, ReturnData, GasLeft, Error};

/// Finalization result. Gas Left: either it is a known value, or it needs to be computed by processing
/// a return instruction.
Expand Down Expand Up @@ -281,15 +147,6 @@ impl CostType for usize {
}
}

/// Evm interface
pub trait Evm {
/// This function should be used to execute transaction.
///
/// It returns either an error, a known amount of gas left, or parameters to be used
/// to compute the final gas left.
fn exec(&mut self, params: ActionParams, ext: &mut Ext) -> Result<GasLeft>;
}

#[cfg(test)]
mod tests {
use util::U256;
Expand Down
6 changes: 3 additions & 3 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 evm::Evm;
use vm::Vm;
use util::U256;
use super::interpreter::SharedCache;
use super::vmtype::VMType;
Expand All @@ -33,7 +33,7 @@ impl Factory {
/// Create fresh instance of VM
/// Might choose implementation depending on supplied gas.
#[cfg(feature = "jit")]
pub fn create(&self, gas: U256) -> Box<Evm> {
pub fn create(&self, gas: U256) -> Box<Vm> {
match self.evm {
VMType::Jit => {
Box::new(super::jit::JitEvm::default())
Expand All @@ -49,7 +49,7 @@ impl Factory {
/// Create fresh instance of VM
/// Might choose implementation depending on supplied gas.
#[cfg(not(feature = "jit"))]
pub fn create(&self, gas: U256) -> Box<Evm> {
pub fn create(&self, gas: U256) -> Box<Vm> {
match self.evm {
VMType::Interpreter => if Self::can_fit_in_usize(gas) {
Box::new(super::interpreter::Interpreter::<usize>::new(self.evm_cache.clone()))
Expand Down
22 changes: 11 additions & 11 deletions ethcore/evm/src/interpreter/gasometer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
use util::*;
use super::u256_to_address;

use {evm, ext};
use {evm, vm};
use instructions::{self, Instruction, InstructionInfo};
use interpreter::stack::Stack;
use schedule::Schedule;
use vm::Schedule;

macro_rules! overflowing {
($x: expr) => {{
let (v, overflow) = $x;
if overflow { return Err(evm::Error::OutOfGas); }
if overflow { return Err(vm::Error::OutOfGas); }
v
}}
}
Expand Down Expand Up @@ -59,16 +59,16 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
}
}

pub fn verify_gas(&self, gas_cost: &Gas) -> evm::Result<()> {
pub fn verify_gas(&self, gas_cost: &Gas) -> vm::Result<()> {
match &self.current_gas < gas_cost {
true => Err(evm::Error::OutOfGas),
true => Err(vm::Error::OutOfGas),
false => Ok(())
}
}

/// How much gas is provided to a CALL/CREATE, given that we need to deduct `needed` for this operation
/// and that we `requested` some.
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> evm::Result<Gas> {
pub fn gas_provided(&self, schedule: &Schedule, needed: Gas, requested: Option<U256>) -> vm::Result<Gas> {
// Try converting requested gas to `Gas` (`U256/u64`)
// but in EIP150 even if we request more we should never fail from OOG
let requested = requested.map(Gas::from_u256);
Expand Down Expand Up @@ -107,12 +107,12 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
/// it will be the amount of gas that the current context provides to the child context.
pub fn requirements(
&mut self,
ext: &ext::Ext,
ext: &vm::Ext,
instruction: Instruction,
info: &InstructionInfo,
stack: &Stack<U256>,
current_mem_size: usize,
) -> evm::Result<InstructionRequirements<Gas>> {
) -> vm::Result<InstructionRequirements<Gas>> {
let schedule = ext.schedule();
let tier = instructions::get_tier_idx(info.tier);
let default_gas = Gas::from(schedule.tier_step_gas[tier]);
Expand Down Expand Up @@ -291,7 +291,7 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
})
}

fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> evm::Result<(Gas, Gas, usize)> {
fn mem_gas_cost(&self, schedule: &Schedule, current_mem_size: usize, mem_size: &Gas) -> vm::Result<(Gas, Gas, usize)> {
let gas_for_mem = |mem_size: Gas| {
let s = mem_size >> 5;
// s * memory_gas + s * s / quad_coeff_div
Expand Down Expand Up @@ -319,12 +319,12 @@ impl<Gas: evm::CostType> Gasometer<Gas> {


#[inline]
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> evm::Result<Gas> {
fn mem_needed_const<Gas: evm::CostType>(mem: &U256, add: usize) -> vm::Result<Gas> {
Gas::from_u256(overflowing!(mem.overflowing_add(U256::from(add))))
}

#[inline]
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> evm::Result<Gas> {
fn mem_needed<Gas: evm::CostType>(offset: &U256, size: &U256) -> vm::Result<Gas> {
if size.is_zero() {
return Ok(Gas::from(0));
}
Expand Down
2 changes: 1 addition & 1 deletion ethcore/evm/src/interpreter/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

use util::U256;
use {ReturnData};
use vm::ReturnData;

const MAX_RETURN_WASTE_BYTES: usize = 16384;

Expand Down
Loading

0 comments on commit b700603

Please sign in to comment.