Reformat the source code

This commit is contained in:
Artem Vorotnikov
2020-08-05 07:08:03 +03:00
parent 253ff3f37b
commit 610d9baba4
742 changed files with 175791 additions and 141379 deletions

View File

@@ -19,189 +19,111 @@
use std::cell::RefCell;
use vm::WasmCosts;
use wasmi::{
self, Signature, Error, FuncRef, FuncInstance, MemoryDescriptor,
MemoryRef, MemoryInstance, memory_units,
self, memory_units, Error, FuncInstance, FuncRef, MemoryDescriptor, MemoryInstance, MemoryRef,
Signature,
};
/// Internal ids all functions runtime supports. This is just a glue for wasmi interpreter
/// that lacks high-level api and later will be factored out
pub mod ids {
pub const STORAGE_WRITE_FUNC: usize = 0;
pub const STORAGE_READ_FUNC: usize = 10;
pub const RET_FUNC: usize = 20;
pub const GAS_FUNC: usize = 30;
pub const FETCH_INPUT_FUNC: usize = 40;
pub const INPUT_LENGTH_FUNC: usize = 50;
pub const CCALL_FUNC: usize = 60;
pub const SCALL_FUNC: usize = 70;
pub const DCALL_FUNC: usize = 80;
pub const VALUE_FUNC: usize = 90;
pub const CREATE_FUNC: usize = 100;
pub const SUICIDE_FUNC: usize = 110;
pub const BLOCKHASH_FUNC: usize = 120;
pub const BLOCKNUMBER_FUNC: usize = 130;
pub const COINBASE_FUNC: usize = 140;
pub const DIFFICULTY_FUNC: usize = 150;
pub const GASLIMIT_FUNC: usize = 160;
pub const TIMESTAMP_FUNC: usize = 170;
pub const ADDRESS_FUNC: usize = 180;
pub const SENDER_FUNC: usize = 190;
pub const ORIGIN_FUNC: usize = 200;
pub const ELOG_FUNC: usize = 210;
pub const CREATE2_FUNC: usize = 220;
pub const GASLEFT_FUNC: usize = 230;
pub const STORAGE_WRITE_FUNC: usize = 0;
pub const STORAGE_READ_FUNC: usize = 10;
pub const RET_FUNC: usize = 20;
pub const GAS_FUNC: usize = 30;
pub const FETCH_INPUT_FUNC: usize = 40;
pub const INPUT_LENGTH_FUNC: usize = 50;
pub const CCALL_FUNC: usize = 60;
pub const SCALL_FUNC: usize = 70;
pub const DCALL_FUNC: usize = 80;
pub const VALUE_FUNC: usize = 90;
pub const CREATE_FUNC: usize = 100;
pub const SUICIDE_FUNC: usize = 110;
pub const BLOCKHASH_FUNC: usize = 120;
pub const BLOCKNUMBER_FUNC: usize = 130;
pub const COINBASE_FUNC: usize = 140;
pub const DIFFICULTY_FUNC: usize = 150;
pub const GASLIMIT_FUNC: usize = 160;
pub const TIMESTAMP_FUNC: usize = 170;
pub const ADDRESS_FUNC: usize = 180;
pub const SENDER_FUNC: usize = 190;
pub const ORIGIN_FUNC: usize = 200;
pub const ELOG_FUNC: usize = 210;
pub const CREATE2_FUNC: usize = 220;
pub const GASLEFT_FUNC: usize = 230;
pub const PANIC_FUNC: usize = 1000;
pub const DEBUG_FUNC: usize = 1010;
pub const PANIC_FUNC: usize = 1000;
pub const DEBUG_FUNC: usize = 1010;
}
/// Signatures of all functions runtime supports. The actual dispatch happens at
/// impl runtime::Runtime methods.
pub mod signatures {
use wasmi::{self, ValueType};
use wasmi::ValueType::*;
use wasmi::{self, ValueType, ValueType::*};
pub struct StaticSignature(pub &'static [ValueType], pub Option<ValueType>);
pub struct StaticSignature(pub &'static [ValueType], pub Option<ValueType>);
pub const STORAGE_READ: StaticSignature = StaticSignature(
&[I32, I32],
None,
);
pub const STORAGE_READ: StaticSignature = StaticSignature(&[I32, I32], None);
pub const STORAGE_WRITE: StaticSignature = StaticSignature(
&[I32, I32],
None,
);
pub const STORAGE_WRITE: StaticSignature = StaticSignature(&[I32, I32], None);
pub const RET: StaticSignature = StaticSignature(
&[I32, I32],
None,
);
pub const RET: StaticSignature = StaticSignature(&[I32, I32], None);
pub const GAS: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const GAS: StaticSignature = StaticSignature(&[I32], None);
pub const FETCH_INPUT: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const FETCH_INPUT: StaticSignature = StaticSignature(&[I32], None);
pub const INPUT_LENGTH: StaticSignature = StaticSignature(
&[],
Some(I32),
);
pub const INPUT_LENGTH: StaticSignature = StaticSignature(&[], Some(I32));
pub const CCALL: StaticSignature = StaticSignature(
&[I64, I32, I32, I32, I32, I32, I32],
Some(I32),
);
pub const CCALL: StaticSignature =
StaticSignature(&[I64, I32, I32, I32, I32, I32, I32], Some(I32));
pub const DCALL: StaticSignature = StaticSignature(
&[I64, I32, I32, I32, I32, I32],
Some(I32),
);
pub const DCALL: StaticSignature = StaticSignature(&[I64, I32, I32, I32, I32, I32], Some(I32));
pub const SCALL: StaticSignature = StaticSignature(
&[I64, I32, I32, I32, I32, I32],
Some(I32),
);
pub const SCALL: StaticSignature = StaticSignature(&[I64, I32, I32, I32, I32, I32], Some(I32));
pub const PANIC: StaticSignature = StaticSignature(
&[I32, I32],
None,
);
pub const PANIC: StaticSignature = StaticSignature(&[I32, I32], None);
pub const DEBUG: StaticSignature = StaticSignature(
&[I32, I32],
None,
);
pub const DEBUG: StaticSignature = StaticSignature(&[I32, I32], None);
pub const VALUE: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const VALUE: StaticSignature = StaticSignature(&[I32], None);
pub const CREATE: StaticSignature = StaticSignature(
&[I32, I32, I32, I32],
Some(I32),
);
pub const CREATE: StaticSignature = StaticSignature(&[I32, I32, I32, I32], Some(I32));
pub const CREATE2: StaticSignature = StaticSignature(
&[I32, I32, I32, I32, I32],
Some(I32),
);
pub const CREATE2: StaticSignature = StaticSignature(&[I32, I32, I32, I32, I32], Some(I32));
pub const SUICIDE: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const SUICIDE: StaticSignature = StaticSignature(&[I32], None);
pub const BLOCKHASH: StaticSignature = StaticSignature(
&[I64, I32],
None,
);
pub const BLOCKHASH: StaticSignature = StaticSignature(&[I64, I32], None);
pub const BLOCKNUMBER: StaticSignature = StaticSignature(
&[],
Some(I64),
);
pub const BLOCKNUMBER: StaticSignature = StaticSignature(&[], Some(I64));
pub const COINBASE: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const COINBASE: StaticSignature = StaticSignature(&[I32], None);
pub const DIFFICULTY: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const DIFFICULTY: StaticSignature = StaticSignature(&[I32], None);
pub const GASLEFT: StaticSignature = StaticSignature(
&[],
Some(I64),
);
pub const GASLEFT: StaticSignature = StaticSignature(&[], Some(I64));
pub const GASLIMIT: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const GASLIMIT: StaticSignature = StaticSignature(&[I32], None);
pub const TIMESTAMP: StaticSignature = StaticSignature(
&[],
Some(I64),
);
pub const TIMESTAMP: StaticSignature = StaticSignature(&[], Some(I64));
pub const ADDRESS: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const ADDRESS: StaticSignature = StaticSignature(&[I32], None);
pub const SENDER: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const SENDER: StaticSignature = StaticSignature(&[I32], None);
pub const ORIGIN: StaticSignature = StaticSignature(
&[I32],
None,
);
pub const ORIGIN: StaticSignature = StaticSignature(&[I32], None);
pub const ELOG: StaticSignature = StaticSignature(
&[I32, I32, I32, I32],
None,
);
pub const ELOG: StaticSignature = StaticSignature(&[I32, I32, I32, I32], None);
impl Into<wasmi::Signature> for StaticSignature {
fn into(self) -> wasmi::Signature {
wasmi::Signature::new(self.0, self.1)
}
}
impl Into<wasmi::Signature> for StaticSignature {
fn into(self) -> wasmi::Signature {
wasmi::Signature::new(self.0, self.1)
}
}
}
fn host(signature: signatures::StaticSignature, idx: usize) -> FuncRef {
FuncInstance::alloc_host(signature.into(), idx)
FuncInstance::alloc_host(signature.into(), idx)
}
/// Import resolver for wasmi
@@ -209,110 +131,117 @@ fn host(signature: signatures::StaticSignature, idx: usize) -> FuncRef {
/// entries.
/// Also manages initial memory request from the runtime.
pub struct ImportResolver {
max_memory: u32,
memory: RefCell<Option<MemoryRef>>,
max_memory: u32,
memory: RefCell<Option<MemoryRef>>,
have_create2: bool,
have_gasleft: bool,
have_create2: bool,
have_gasleft: bool,
}
impl ImportResolver {
/// New import resolver with specifed maximum amount of inital memory (in wasm pages = 64kb)
pub fn with_limit(max_memory: u32, schedule: &WasmCosts) -> ImportResolver {
ImportResolver {
max_memory: max_memory,
memory: RefCell::new(None),
/// New import resolver with specifed maximum amount of inital memory (in wasm pages = 64kb)
pub fn with_limit(max_memory: u32, schedule: &WasmCosts) -> ImportResolver {
ImportResolver {
max_memory: max_memory,
memory: RefCell::new(None),
have_create2: schedule.have_create2,
have_gasleft: schedule.have_gasleft,
}
}
have_create2: schedule.have_create2,
have_gasleft: schedule.have_gasleft,
}
}
/// Returns memory that was instantiated during the contract module
/// start. If contract does not use memory at all, the dummy memory of length (0, 0)
/// will be created instead. So this method always returns memory instance
/// unless errored.
pub fn memory_ref(&self) -> MemoryRef {
{
let mut mem_ref = self.memory.borrow_mut();
if mem_ref.is_none() {
*mem_ref = Some(
MemoryInstance::alloc(
memory_units::Pages(0),
Some(memory_units::Pages(0)),
).expect("Memory allocation (0, 0) should not fail; qed")
);
}
}
/// Returns memory that was instantiated during the contract module
/// start. If contract does not use memory at all, the dummy memory of length (0, 0)
/// will be created instead. So this method always returns memory instance
/// unless errored.
pub fn memory_ref(&self) -> MemoryRef {
{
let mut mem_ref = self.memory.borrow_mut();
if mem_ref.is_none() {
*mem_ref = Some(
MemoryInstance::alloc(memory_units::Pages(0), Some(memory_units::Pages(0)))
.expect("Memory allocation (0, 0) should not fail; qed"),
);
}
}
self.memory.borrow().clone().expect("it is either existed or was created as (0, 0) above; qed")
}
self.memory
.borrow()
.clone()
.expect("it is either existed or was created as (0, 0) above; qed")
}
/// Returns memory size module initially requested
pub fn memory_size(&self) -> Result<u32, Error> {
Ok(self.memory_ref().current_size().0 as u32)
}
/// Returns memory size module initially requested
pub fn memory_size(&self) -> Result<u32, Error> {
Ok(self.memory_ref().current_size().0 as u32)
}
}
impl wasmi::ModuleImportResolver for ImportResolver {
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
let func_ref = match field_name {
"storage_read" => host(signatures::STORAGE_READ, ids::STORAGE_READ_FUNC),
"storage_write" => host(signatures::STORAGE_WRITE, ids::STORAGE_WRITE_FUNC),
"ret" => host(signatures::RET, ids::RET_FUNC),
"gas" => host(signatures::GAS, ids::GAS_FUNC),
"input_length" => host(signatures::INPUT_LENGTH, ids::INPUT_LENGTH_FUNC),
"fetch_input" => host(signatures::FETCH_INPUT, ids::FETCH_INPUT_FUNC),
"panic" => host(signatures::PANIC, ids::PANIC_FUNC),
"debug" => host(signatures::DEBUG, ids::DEBUG_FUNC),
"ccall" => host(signatures::CCALL, ids::CCALL_FUNC),
"dcall" => host(signatures::DCALL, ids::DCALL_FUNC),
"scall" => host(signatures::SCALL, ids::SCALL_FUNC),
"value" => host(signatures::VALUE, ids::VALUE_FUNC),
"create" => host(signatures::CREATE, ids::CREATE_FUNC),
"suicide" => host(signatures::SUICIDE, ids::SUICIDE_FUNC),
"blockhash" => host(signatures::BLOCKHASH, ids::BLOCKHASH_FUNC),
"blocknumber" => host(signatures::BLOCKNUMBER, ids::BLOCKNUMBER_FUNC),
"coinbase" => host(signatures::COINBASE, ids::COINBASE_FUNC),
"difficulty" => host(signatures::DIFFICULTY, ids::DIFFICULTY_FUNC),
"gaslimit" => host(signatures::GASLIMIT, ids::GASLIMIT_FUNC),
"timestamp" => host(signatures::TIMESTAMP, ids::TIMESTAMP_FUNC),
"address" => host(signatures::ADDRESS, ids::ADDRESS_FUNC),
"sender" => host(signatures::SENDER, ids::SENDER_FUNC),
"origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC),
"elog" => host(signatures::ELOG, ids::ELOG_FUNC),
"create2" if self.have_create2 => host(signatures::CREATE2, ids::CREATE2_FUNC),
"gasleft" if self.have_gasleft => host(signatures::GASLEFT, ids::GASLEFT_FUNC),
_ => {
return Err(wasmi::Error::Instantiation(
format!("Export {} not found", field_name),
))
}
};
fn resolve_func(&self, field_name: &str, _signature: &Signature) -> Result<FuncRef, Error> {
let func_ref = match field_name {
"storage_read" => host(signatures::STORAGE_READ, ids::STORAGE_READ_FUNC),
"storage_write" => host(signatures::STORAGE_WRITE, ids::STORAGE_WRITE_FUNC),
"ret" => host(signatures::RET, ids::RET_FUNC),
"gas" => host(signatures::GAS, ids::GAS_FUNC),
"input_length" => host(signatures::INPUT_LENGTH, ids::INPUT_LENGTH_FUNC),
"fetch_input" => host(signatures::FETCH_INPUT, ids::FETCH_INPUT_FUNC),
"panic" => host(signatures::PANIC, ids::PANIC_FUNC),
"debug" => host(signatures::DEBUG, ids::DEBUG_FUNC),
"ccall" => host(signatures::CCALL, ids::CCALL_FUNC),
"dcall" => host(signatures::DCALL, ids::DCALL_FUNC),
"scall" => host(signatures::SCALL, ids::SCALL_FUNC),
"value" => host(signatures::VALUE, ids::VALUE_FUNC),
"create" => host(signatures::CREATE, ids::CREATE_FUNC),
"suicide" => host(signatures::SUICIDE, ids::SUICIDE_FUNC),
"blockhash" => host(signatures::BLOCKHASH, ids::BLOCKHASH_FUNC),
"blocknumber" => host(signatures::BLOCKNUMBER, ids::BLOCKNUMBER_FUNC),
"coinbase" => host(signatures::COINBASE, ids::COINBASE_FUNC),
"difficulty" => host(signatures::DIFFICULTY, ids::DIFFICULTY_FUNC),
"gaslimit" => host(signatures::GASLIMIT, ids::GASLIMIT_FUNC),
"timestamp" => host(signatures::TIMESTAMP, ids::TIMESTAMP_FUNC),
"address" => host(signatures::ADDRESS, ids::ADDRESS_FUNC),
"sender" => host(signatures::SENDER, ids::SENDER_FUNC),
"origin" => host(signatures::ORIGIN, ids::ORIGIN_FUNC),
"elog" => host(signatures::ELOG, ids::ELOG_FUNC),
"create2" if self.have_create2 => host(signatures::CREATE2, ids::CREATE2_FUNC),
"gasleft" if self.have_gasleft => host(signatures::GASLEFT, ids::GASLEFT_FUNC),
_ => {
return Err(wasmi::Error::Instantiation(format!(
"Export {} not found",
field_name
)))
}
};
Ok(func_ref)
}
Ok(func_ref)
}
fn resolve_memory(
&self,
field_name: &str,
descriptor: &MemoryDescriptor,
) -> Result<MemoryRef, Error> {
if field_name == "memory" {
let effective_max = descriptor.maximum().unwrap_or(self.max_memory + 1);
if descriptor.initial() > self.max_memory || effective_max > self.max_memory
{
Err(Error::Instantiation("Module requested too much memory".to_owned()))
} else {
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)
}
} else {
Err(Error::Instantiation("Memory imported under unknown name".to_owned()))
}
}
fn resolve_memory(
&self,
field_name: &str,
descriptor: &MemoryDescriptor,
) -> Result<MemoryRef, Error> {
if field_name == "memory" {
let effective_max = descriptor.maximum().unwrap_or(self.max_memory + 1);
if descriptor.initial() > self.max_memory || effective_max > self.max_memory {
Err(Error::Instantiation(
"Module requested too much memory".to_owned(),
))
} else {
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)
}
} else {
Err(Error::Instantiation(
"Memory imported under unknown name".to_owned(),
))
}
}
}

View File

@@ -18,11 +18,12 @@
extern crate byteorder;
extern crate ethereum_types;
#[macro_use] extern crate log;
#[macro_use]
extern crate log;
extern crate libc;
extern crate parity_wasm;
extern crate vm;
extern crate pwasm_utils as wasm_utils;
extern crate vm;
extern crate wasmi;
#[cfg(test)]
@@ -36,8 +37,7 @@ mod runtime;
#[cfg(test)]
mod tests;
use vm::{GasLeft, ReturnData, ActionParams};
use vm::{ActionParams, GasLeft, ReturnData};
use wasmi::{Error as InterpreterError, Trap};
use runtime::{Runtime, RuntimeContext};
@@ -47,155 +47,164 @@ use ethereum_types::U256;
/// Wrapped interpreter error
#[derive(Debug)]
pub enum Error {
Interpreter(InterpreterError),
Trap(Trap),
Interpreter(InterpreterError),
Trap(Trap),
}
impl From<InterpreterError> for Error {
fn from(e: InterpreterError) -> Self {
Error::Interpreter(e)
}
fn from(e: InterpreterError) -> Self {
Error::Interpreter(e)
}
}
impl From<Trap> for Error {
fn from(e: Trap) -> Self {
Error::Trap(e)
}
fn from(e: Trap) -> Self {
Error::Trap(e)
}
}
impl From<Error> for vm::Error {
fn from(e: Error) -> Self {
match e {
Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)),
Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)),
}
}
fn from(e: Error) -> Self {
match e {
Error::Interpreter(e) => vm::Error::Wasm(format!("Wasm runtime error: {:?}", e)),
Error::Trap(e) => vm::Error::Wasm(format!("Wasm contract trap: {:?}", e)),
}
}
}
/// Wasm interpreter instance
pub struct WasmInterpreter {
params: ActionParams,
params: ActionParams,
}
impl WasmInterpreter {
pub fn new(params: ActionParams) -> Self {
WasmInterpreter { params }
}
pub fn new(params: ActionParams) -> Self {
WasmInterpreter { params }
}
}
impl From<runtime::Error> for vm::Error {
fn from(e: runtime::Error) -> Self {
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e))
}
fn from(e: runtime::Error) -> Self {
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e))
}
}
enum ExecutionOutcome {
Suicide,
Return,
NotSpecial,
Suicide,
Return,
NotSpecial,
}
impl WasmInterpreter {
pub fn run(self: Box<WasmInterpreter>, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
let (module, data) = parser::payload(&self.params, ext.schedule().wasm())?;
pub fn run(self: Box<WasmInterpreter>, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
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)?;
let instantiation_resolver = env::ImportResolver::with_limit(16, ext.schedule().wasm());
let instantiation_resolver = env::ImportResolver::with_limit(16, ext.schedule().wasm());
let module_instance = wasmi::ModuleInstance::new(
&loaded_module,
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver)
).map_err(Error::Interpreter)?;
let module_instance = wasmi::ModuleInstance::new(
&loaded_module,
&wasmi::ImportsBuilder::new().with_resolver("env", &instantiation_resolver),
)
.map_err(Error::Interpreter)?;
let adjusted_gas = self.params.gas * U256::from(ext.schedule().wasm().opcodes_div) /
U256::from(ext.schedule().wasm().opcodes_mul);
let adjusted_gas = self.params.gas * U256::from(ext.schedule().wasm().opcodes_div)
/ U256::from(ext.schedule().wasm().opcodes_mul);
if adjusted_gas > ::std::u64::MAX.into()
{
return Err(vm::Error::Wasm("Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned()));
}
if adjusted_gas > ::std::u64::MAX.into() {
return Err(vm::Error::Wasm(
"Wasm interpreter cannot run contracts with gas (wasm adjusted) >= 2^64".to_owned(),
));
}
let initial_memory = instantiation_resolver.memory_size().map_err(Error::Interpreter)?;
trace!(target: "wasm", "Contract requested {:?} pages of initial memory", initial_memory);
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_resolver.memory_ref(),
// cannot overflow, checked above
adjusted_gas.low_u64(),
data.to_vec(),
RuntimeContext {
address: self.params.address,
sender: self.params.sender,
origin: self.params.origin,
code_address: self.params.code_address,
value: self.params.value.value(),
},
);
let (gas_left, result) = {
let mut runtime = Runtime::with_params(
ext,
instantiation_resolver.memory_ref(),
// cannot overflow, checked above
adjusted_gas.low_u64(),
data.to_vec(),
RuntimeContext {
address: self.params.address,
sender: self.params.sender,
origin: self.params.origin,
code_address: self.params.code_address,
value: self.params.value.value(),
},
);
// cannot overflow if static_region < 2^16,
// initial_memory ∈ [0..2^32)
// total_charge <- static_region * 2^32 * 2^16
// total_charge ∈ [0..2^64) if static_region ∈ [0..2^16)
// qed
assert!(runtime.schedule().wasm().initial_mem < 1 << 16);
runtime.charge(|s| initial_memory as u64 * s.wasm().initial_mem as u64)?;
// cannot overflow if static_region < 2^16,
// initial_memory ∈ [0..2^32)
// total_charge <- static_region * 2^32 * 2^16
// total_charge ∈ [0..2^64) if static_region ∈ [0..2^16)
// qed
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::Trap)?;
let module_instance = module_instance
.run_start(&mut runtime)
.map_err(Error::Trap)?;
let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);
let invoke_result = module_instance.invoke_export("call", &[], &mut runtime);
let mut execution_outcome = ExecutionOutcome::NotSpecial;
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");
let mut execution_outcome = ExecutionOutcome::NotSpecial;
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");
match **runtime_err {
runtime::Error::Suicide => { execution_outcome = ExecutionOutcome::Suicide; },
runtime::Error::Return => { execution_outcome = ExecutionOutcome::Return; },
_ => {}
}
}
}
match **runtime_err {
runtime::Error::Suicide => {
execution_outcome = ExecutionOutcome::Suicide;
}
runtime::Error::Return => {
execution_outcome = ExecutionOutcome::Return;
}
_ => {}
}
}
}
if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, invoke_result) {
trace!(target: "wasm", "Error executing contract: {:?}", e);
return Err(vm::Error::from(Error::from(e)));
}
if let (ExecutionOutcome::NotSpecial, Err(e)) = (execution_outcome, 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(),
)
};
(
runtime
.gas_left()
.expect("Cannot fail since it was not updated since last charge"),
runtime.into_result(),
)
};
let gas_left =
U256::from(gas_left) * U256::from(ext.schedule().wasm().opcodes_mul)
/ U256::from(ext.schedule().wasm().opcodes_div);
let gas_left = U256::from(gas_left) * U256::from(ext.schedule().wasm().opcodes_mul)
/ U256::from(ext.schedule().wasm().opcodes_div);
if result.is_empty() {
trace!(target: "wasm", "Contract execution result is empty.");
Ok(GasLeft::Known(gas_left))
} else {
let len = result.len();
Ok(GasLeft::NeedsReturn {
gas_left: gas_left,
data: ReturnData::new(
result,
0,
len,
),
apply_state: true,
})
}
}
if result.is_empty() {
trace!(target: "wasm", "Contract execution result is empty.");
Ok(GasLeft::Known(gas_left))
} else {
let len = result.len();
Ok(GasLeft::NeedsReturn {
gas_left: gas_left,
data: ReturnData::new(result, 0, len),
apply_state: true,
})
}
}
}
impl vm::Exec for WasmInterpreter {
fn exec(self: Box<WasmInterpreter>, ext: &mut vm::Ext) -> vm::ExecTrapResult<GasLeft> {
Ok(self.run(ext))
}
fn exec(self: Box<WasmInterpreter>, ext: &mut vm::Ext) -> vm::ExecTrapResult<GasLeft> {
Ok(self.run(ext))
}
}

View File

@@ -19,150 +19,150 @@ use std::io::{self, Read};
#[derive(Debug, PartialEq, Eq)]
pub struct PanicPayload {
pub msg: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
pub col: Option<u32>,
pub msg: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
pub col: Option<u32>,
}
fn read_string(rdr: &mut io::Cursor<&[u8]>) -> io::Result<Option<String>> {
let string_len = rdr.read_u32::<LittleEndian>()?;
let string = if string_len == 0 {
None
} else {
let mut content = vec![0; string_len as usize];
rdr.read_exact(&mut content)?;
Some(String::from_utf8_lossy(&content).into_owned())
};
Ok(string)
let string_len = rdr.read_u32::<LittleEndian>()?;
let string = if string_len == 0 {
None
} else {
let mut content = vec![0; string_len as usize];
rdr.read_exact(&mut content)?;
Some(String::from_utf8_lossy(&content).into_owned())
};
Ok(string)
}
pub fn decode(raw: &[u8]) -> PanicPayload {
let mut rdr = io::Cursor::new(raw);
let msg = read_string(&mut rdr).ok().and_then(|x| x);
let file = read_string(&mut rdr).ok().and_then(|x| x);
let line = rdr.read_u32::<LittleEndian>().ok();
let col = rdr.read_u32::<LittleEndian>().ok();
PanicPayload {
msg: msg,
file: file,
line: line,
col: col,
}
let mut rdr = io::Cursor::new(raw);
let msg = read_string(&mut rdr).ok().and_then(|x| x);
let file = read_string(&mut rdr).ok().and_then(|x| x);
let line = rdr.read_u32::<LittleEndian>().ok();
let col = rdr.read_u32::<LittleEndian>().ok();
PanicPayload {
msg: msg,
file: file,
line: line,
col: col,
}
}
#[cfg(test)]
mod tests {
use super::*;
use byteorder::WriteBytesExt;
use super::*;
use byteorder::WriteBytesExt;
fn write_u32(payload: &mut Vec<u8>, val: u32) {
payload.write_u32::<LittleEndian>(val).unwrap();
}
fn write_u32(payload: &mut Vec<u8>, val: u32) {
payload.write_u32::<LittleEndian>(val).unwrap();
}
fn write_bytes(payload: &mut Vec<u8>, bytes: &[u8]) {
write_u32(payload, bytes.len() as u32);
payload.extend(bytes);
}
fn write_bytes(payload: &mut Vec<u8>, bytes: &[u8]) {
write_u32(payload, bytes.len() as u32);
payload.extend(bytes);
}
#[test]
fn it_works() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
#[test]
fn it_works() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
let payload = decode(&raw);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
#[test]
fn only_msg() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
#[test]
fn only_msg() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
let payload = decode(&raw);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: None,
line: None,
col: None,
}
);
}
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: None,
line: None,
col: None,
}
);
}
#[test]
fn invalid_utf8() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"\xF0\x90\x80msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
#[test]
fn invalid_utf8() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"\xF0\x90\x80msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
let payload = decode(&raw);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("<EFBFBD>msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
assert_eq!(
payload,
PanicPayload {
msg: Some("<EFBFBD>msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
#[test]
fn trailing_data() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
write_u32(&mut raw, 0xdeadbeef);
#[test]
fn trailing_data() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"file");
write_u32(&mut raw, 1);
write_u32(&mut raw, 2);
write_u32(&mut raw, 0xdeadbeef);
let payload = decode(&raw);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: Some("file".to_string()),
line: Some(1),
col: Some(2),
}
);
}
#[test]
fn empty_str_is_none() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"");
#[test]
fn empty_str_is_none() {
let mut raw = Vec::new();
write_bytes(&mut raw, b"msg");
write_bytes(&mut raw, b"");
let payload = decode(&raw);
let payload = decode(&raw);
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: None,
line: None,
col: None,
}
);
}
assert_eq!(
payload,
PanicPayload {
msg: Some("msg".to_string()),
file: None,
line: None,
col: None,
}
);
}
}

View File

@@ -16,83 +16,95 @@
//! ActionParams parser for wasm
use parity_wasm::{
elements::{self, Deserialize},
peek_size,
};
use vm;
use wasm_utils::{self, rules};
use parity_wasm::elements::{self, Deserialize};
use parity_wasm::peek_size;
fn gas_rules(wasm_costs: &vm::WasmCosts) -> rules::Set {
rules::Set::new(
wasm_costs.regular,
{
let mut vals = ::std::collections::BTreeMap::new();
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()
rules::Set::new(wasm_costs.regular, {
let mut vals = ::std::collections::BTreeMap::new();
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
/// loads the module instance from payload and injects gas counter according
/// to schedule.
pub fn payload<'a>(params: &'a vm::ActionParams, wasm_costs: &vm::WasmCosts)
-> Result<(elements::Module, &'a [u8]), vm::Error>
{
let code = match params.code {
Some(ref code) => &code[..],
None => { return Err(vm::Error::Wasm("Invalid wasm call".to_owned())); }
};
pub fn payload<'a>(
params: &'a vm::ActionParams,
wasm_costs: &vm::WasmCosts,
) -> Result<(elements::Module, &'a [u8]), vm::Error> {
let code = match params.code {
Some(ref code) => &code[..],
None => {
return Err(vm::Error::Wasm("Invalid wasm call".to_owned()));
}
};
let (mut cursor, data_position) = match params.params_type {
vm::ParamsType::Embedded => {
let module_size = peek_size(&*code);
(
::std::io::Cursor::new(&code[..module_size]),
module_size
)
},
vm::ParamsType::Separate => {
(::std::io::Cursor::new(&code[..]), 0)
},
};
let (mut cursor, data_position) = match params.params_type {
vm::ParamsType::Embedded => {
let module_size = peek_size(&*code);
(::std::io::Cursor::new(&code[..module_size]), module_size)
}
vm::ParamsType::Separate => (::std::io::Cursor::new(&code[..]), 0),
};
let deserialized_module = elements::Module::deserialize(
&mut cursor
).map_err(|err| {
vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err))
})?;
let deserialized_module = elements::Module::deserialize(&mut cursor)
.map_err(|err| vm::Error::Wasm(format!("Error deserializing contract code ({:?})", err)))?;
if deserialized_module.memory_section().map_or(false, |ms| ms.entries().len() > 0) {
// According to WebAssembly spec, internal memory is hidden from embedder and should not
// be interacted with. So we disable this kind of modules at decoding level.
return Err(vm::Error::Wasm(format!("Malformed wasm module: internal memory")));
}
if deserialized_module
.memory_section()
.map_or(false, |ms| ms.entries().len() > 0)
{
// According to WebAssembly spec, internal memory is hidden from embedder and should not
// be interacted with. So we disable this kind of modules at decoding level.
return Err(vm::Error::Wasm(format!(
"Malformed wasm module: internal memory"
)));
}
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::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 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 => {
if data_position < code.len() { &code[data_position..] } else { &[] }
},
vm::ParamsType::Separate => {
match params.data {
Some(ref s) => &s[..],
None => &[]
}
}
};
let data = match params.params_type {
vm::ParamsType::Embedded => {
if data_position < code.len() {
&code[data_position..]
} else {
&[]
}
}
vm::ParamsType::Separate => match params.data {
Some(ref s) => &s[..],
None => &[],
},
};
Ok((contract_module, data))
Ok((contract_module, data))
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff