e8b13cb77e
* Basic implementation for kip4 * Add KIP-4 config flags * typo: docs fix * Fix args offset * Add tests for create2 * tests: evm * Update wasm-tests and fix all gas costs * Update wasm-tests * Update wasm-tests and fix gas costs
185 lines
5.2 KiB
Rust
185 lines
5.2 KiB
Rust
// Copyright 2015-2018 Parity Technologies (UK) Ltd.
|
|
// This file is part of Parity.
|
|
|
|
// Parity is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Parity is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Wasm Interpreter
|
|
|
|
extern crate byteorder;
|
|
extern crate ethcore_logger;
|
|
extern crate ethereum_types;
|
|
#[macro_use] extern crate log;
|
|
extern crate libc;
|
|
extern crate parity_wasm;
|
|
extern crate vm;
|
|
extern crate pwasm_utils as wasm_utils;
|
|
extern crate wasmi;
|
|
|
|
mod runtime;
|
|
#[cfg(test)]
|
|
mod tests;
|
|
mod env;
|
|
mod panic_payload;
|
|
mod parser;
|
|
|
|
use vm::{GasLeft, ReturnData, ActionParams};
|
|
use wasmi::{Error as InterpreterError, Trap};
|
|
|
|
use runtime::{Runtime, RuntimeContext};
|
|
|
|
use ethereum_types::U256;
|
|
|
|
/// Wrapped interpreter error
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
Interpreter(InterpreterError),
|
|
Trap(Trap),
|
|
}
|
|
|
|
impl From<InterpreterError> for Error {
|
|
fn from(e: InterpreterError) -> Self {
|
|
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 {
|
|
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;
|
|
|
|
impl From<runtime::Error> for vm::Error {
|
|
fn from(e: runtime::Error) -> Self {
|
|
vm::Error::Wasm(format!("Wasm runtime error: {:?}", e))
|
|
}
|
|
}
|
|
|
|
enum ExecutionOutcome {
|
|
Suicide,
|
|
Return,
|
|
NotSpecial,
|
|
}
|
|
|
|
impl vm::Vm for WasmInterpreter {
|
|
|
|
fn exec(&mut self, params: ActionParams, ext: &mut vm::Ext) -> vm::Result<GasLeft> {
|
|
let (module, data) = parser::payload(¶ms, ext.schedule().wasm())?;
|
|
|
|
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 module_instance = wasmi::ModuleInstance::new(
|
|
&loaded_module,
|
|
&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);
|
|
|
|
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 (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: params.address,
|
|
sender: params.sender,
|
|
origin: params.origin,
|
|
code_address: params.code_address,
|
|
value: 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)?;
|
|
|
|
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 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; },
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
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(),
|
|
)
|
|
};
|
|
|
|
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,
|
|
})
|
|
}
|
|
}
|
|
}
|