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

WASM libraries bump #7970

Merged
merged 10 commits into from
Mar 12, 2018
25 changes: 16 additions & 9 deletions Cargo.lock

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

10 changes: 6 additions & 4 deletions ethcore/vm/src/schedule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ pub struct Schedule {

/// Wasm cost table
pub struct WasmCosts {
/// Arena allocator cost, per byte
pub alloc: u32,
/// Default opcode cost
pub regular: u32,
/// Div operations multiplier.
pub div: u32,
/// Div operations multiplier.
Expand All @@ -135,24 +135,26 @@ pub struct WasmCosts {
pub initial_mem: u32,
/// Grow memory cost, per page (64kb)
pub grow_mem: u32,
/// Max stack height (native WebAssembly stack limiter)
pub max_stack_height: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_mul: u32,
/// Cost of wasm opcode is calculated as TABLE_ENTRY_COST * `opcodes_mul` / `opcodes_div`
pub opcodes_div: u32,

}

impl Default for WasmCosts {
fn default() -> Self {
WasmCosts {
alloc: 2,
regular: 1,
div: 16,
mul: 4,
mem: 2,
static_u256: 64,
static_address: 40,
initial_mem: 4096,
grow_mem: 8192,
max_stack_height: 64*1024,
opcodes_mul: 3,
opcodes_div: 8,
}
Expand Down
4 changes: 2 additions & 2 deletions ethcore/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ authors = ["Parity Technologies <[email protected]>"]
byteorder = "1.0"
ethereum-types = "0.2"
log = "0.3"
parity-wasm = "0.23"
parity-wasm = "0.27"
libc = "0.2"
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
vm = { path = "../vm" }
ethcore-logger = { path = "../../logger" }
wasmi = { git = "https://github.com/pepyakin/wasmi" }
wasmi = { git = "https://github.com/paritytech/wasmi" }
14 changes: 10 additions & 4 deletions ethcore/wasm/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
use std::cell::RefCell;
use wasmi::{
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
MemoryRef, MemoryInstance,
MemoryRef, MemoryInstance, memory_units,
};

/// Internal ids all functions runtime supports. This is just a glue for wasmi interpreter
Expand Down Expand Up @@ -219,7 +219,10 @@ impl ImportResolver {
let mut mem_ref = self.memory.borrow_mut();
if mem_ref.is_none() {
*mem_ref = Some(
MemoryInstance::alloc(0, Some(0)).expect("Memory allocation (0, 0) should not fail; qed")
MemoryInstance::alloc(
memory_units::Pages(0),
Some(memory_units::Pages(0)),
).expect("Memory allocation (0, 0) should not fail; qed")
);
}
}
Expand All @@ -229,7 +232,7 @@ impl ImportResolver {

/// Returns memory size module initially requested
pub fn memory_size(&self) -> Result<u32, Error> {
Ok(self.memory_ref().size())
Ok(self.memory_ref().current_size().0 as u32)
}
}

Expand Down Expand Up @@ -281,7 +284,10 @@ impl wasmi::ModuleImportResolver for ImportResolver {
{
Err(Error::Instantiation("Module requested too much memory".to_owned()))
} else {
let mem = MemoryInstance::alloc(descriptor.initial(), descriptor.maximum())?;
let mem = MemoryInstance::alloc(
memory_units::Pages(descriptor.initial() as usize),
descriptor.maximum().map(|x| memory_units::Pages(x as usize)),
)?;
*self.memory.borrow_mut() = Some(mem.clone());
Ok(mem)
}
Expand Down
76 changes: 41 additions & 35 deletions ethcore/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,37 @@ mod panic_payload;
mod parser;

use vm::{GasLeft, ReturnData, ActionParams};
use wasmi::Error as InterpreterError;
use wasmi::{Error as InterpreterError, Trap};

use runtime::{Runtime, RuntimeContext};

use ethereum_types::U256;

/// Wrapped interpreter error
#[derive(Debug)]
pub struct Error(InterpreterError);
pub enum Error {
Interpreter(InterpreterError),
Trap(Trap),
}

impl From<InterpreterError> for Error {
fn from(e: InterpreterError) -> Self {
Error(e)
Error::Interpreter(e)
}
}

impl From<Trap> for Error {
fn from(e: Trap) -> Self {
Error::Trap(e)
}
}

impl From<Error> for vm::Error {
fn from(e: Error) -> Self {
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e.0))
match e {
Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)),
Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)),
}
}
}

Expand All @@ -70,14 +82,14 @@ impl vm::Vm for WasmInterpreter {
fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
let (module, data) = parser::payload(&params, ext.schedule().wasm())?;

let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error)?;
let loaded_module = wasmi::Module::from_parity_wasm_module(module).map_err(Error::Interpreter)?;

let instantiation_resolover = env::ImportResolver::with_limit(16);
let instantiation_resolver = env::ImportResolver::with_limit(16);

let module_instance = wasmi::ModuleInstance::new(
&loaded_module,
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolover)
).map_err(Error)?;
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver)
).map_err(Error::Interpreter)?;

let adjusted_gas = params.gas * U256::from(ext.schedule().wasm().opcodes_div) /
U256::from(ext.schedule().wasm().opcodes_mul);
Expand All @@ -87,13 +99,13 @@ impl vm::Vm for WasmInterpreter {
return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned()));
}

let initial_memory = instantiation_resolover.memory_size().map_err(Error)?;
let initial_memory = instantiation_resolver.memory_size().map_err(Error::Interpreter)?;
trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory);

let (gas_left, result) = {
let mut runtime = Runtime::with_params(
ext,
instantiation_resolover.memory_ref(),
instantiation_resolver.memory_ref(),
// cannot overflow, checked above
adjusted_gas.low_u64(),
data.to_vec(),
Expand All @@ -114,33 +126,27 @@ impl vm::Vm for WasmInterpreter {
assert!(runtime.schedule().wasm().initial_mem < 1 << 16);
runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?;

let module_instance = module_instance.run_start(&mut runtime).map_err(Error)?;

match module_instance.invoke_export("call", &[], &mut runtime) {
Ok(_) => { },
Err(InterpreterError::Host(boxed)) => {
match boxed.downcast_ref::<runtime::Error>() {
None => {
return Err(vm::Error::Wasm("Invalid user error used in interpreter".to_owned()));
}
Some(runtime_err) => {
match *runtime_err {
runtime::Error::Suicide => {
// Suicide uses trap to break execution
}
ref any_err => {
trace!(target: "wasm", "Error executing contract: {:?}", boxed);
return Err(vm::Error::from(Error::from(InterpreterError::Host(Box::new(any_err.clone())))));
}
}
}
}
},
Err(err) => {
trace!(target: "wasm", "Error executing contract: {:?}", err);
return Err(vm::Error::from(Error::from(err)))
let module_instance = module_instance.run_start(&mut runtime).map_err(Error::Trap)?;

let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);

let mut suicide = false;
if let Err(InterpreterError::Trap(ref trap)) = invoke_result {
if let wasmi::TrapKind::Host(ref boxed) = *trap.kind() {
let ref runtime_err = boxed.downcast_ref::<runtime::Error>()
.expect("Host errors other than runtime::Error never produced; qed");

if let runtime::Error::Suicide = **runtime_err { suicide = true; }
}
}

if !suicide {
if let Err(e) = invoke_result {
trace!(target: "wasm", "Error executing contract: {:?}", e);
return Err(vm::Error::from(Error::from(e)));
}
}

(
runtime.gas_left().expect("Cannot fail since it was not updated since last charge"),
runtime.into_result(),
Expand Down
27 changes: 18 additions & 9 deletions ethcore/wasm/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ use parity_wasm::elements::{self, Deserialize};
use parity_wasm::peek_size;

fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set {
rules::Set::new({
let mut vals = ::std::collections::HashMap::with_capacity(4);
vals.insert(rules::InstructionType::Load, wasm_costs.mem as u32);
vals.insert(rules::InstructionType::Store, wasm_costs.mem as u32);
vals.insert(rules::InstructionType::Div, wasm_costs.div as u32);
vals.insert(rules::InstructionType::Mul, wasm_costs.mul as u32);
vals
}).with_grow_cost(wasm_costs.grow_mem)
rules::Set::new(
wasm_costs.regular,
{
let mut vals = ::std::collections::HashMap::with_capacity(8);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why increase the capacity?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Float disabling rules include 4 entries

vals.insert(rules::InstructionType::Load, rules::Metering::Fixed(wasm_costs.mem as u32));
vals.insert(rules::InstructionType::Store, rules::Metering::Fixed(wasm_costs.mem as u32));
vals.insert(rules::InstructionType::Div, rules::Metering::Fixed(wasm_costs.div as u32));
vals.insert(rules::InstructionType::Mul, rules::Metering::Fixed(wasm_costs.mul as u32));
vals
})
.with_grow_cost(wasm_costs.grow_mem)
.with_forbidden_floats()
}

/// Splits payload to code and data according to params.params_type, also
Expand Down Expand Up @@ -71,7 +75,12 @@ pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts)
let contract_module = wasm_utils::inject_gas_counter(
deserialized_module,
&gas_rules(wasm_costs),
);
).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: bytecode invalid")))?;

let contract_module = wasm_utils::stack_height::inject_limiter(
contract_module,
wasm_costs.max_stack_height,
).map_err(|_| vm::Error::Wasm(format!("Wasm contract error: stack limiter failure")))?;

let data = match params.params_type {
vm::ParamsType::Embedded => {
Expand Down
Loading