wasm mvp continued

This commit is contained in:
NikVolf 2017-07-24 17:45:15 +03:00
parent 5fb32229f9
commit 5180919e52
8 changed files with 281 additions and 49 deletions

1
Cargo.lock generated
View File

@ -878,6 +878,7 @@ dependencies = [
"bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"common-types 0.1.0",
"ethcore-logger 1.8.0",
"ethcore-util 1.8.0",
"ethjson 0.1.0",
"evmjit 1.8.0",

View File

@ -14,8 +14,10 @@ lazy_static = "0.2"
log = "0.3"
rlp = { path = "../../util/rlp" }
parity-wasm = "0.12"
ethcore-logger = { path = "../../logger" }
wasm-utils = { git = "https://github.com/paritytech/wasm-utils" }
[dev-dependencies]
rustc-hex = "1.0"

View File

@ -24,6 +24,7 @@ extern crate ethjson;
extern crate rlp;
extern crate parity_wasm;
extern crate wasm_utils;
extern crate ethcore_logger;
#[macro_use]
extern crate lazy_static;

View File

@ -61,6 +61,21 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[I32; 4],
Some(I32),
),
Static(
"_ccall",
&[I32; 6],
Some(I32),
),
Static(
"_dcall",
&[I32; 5],
Some(I32),
),
Static(
"_scall",
&[I32; 5],
Some(I32),
),
Static(
"abort",
&[I32],
@ -71,49 +86,19 @@ pub const SIGNATURES: &'static [UserFunctionDescriptor] = &[
&[],
None,
),
Static(
"invoke_vii",
&[I32; 3],
None,
),
Static(
"invoke_vi",
&[I32; 2],
None,
),
Static(
"invoke_v",
&[I32],
None,
),
Static(
"invoke_iii",
&[I32; 3],
Some(I32),
),
Static(
"___resumeException",
&[I32],
None,
),
Static(
"_rust_begin_unwind",
&[I32; 4],
None,
),
Static(
"___cxa_find_matching_catch_2",
&[],
Some(I32),
),
Static(
"___gxx_personality_v0",
&[I32; 6],
Some(I32),
),
Static(
"_emscripten_memcpy_big",
&[I32; 3],
Some(I32),
),
Static(
"___syscall6",
&[I32; 2],
Some(I32),
)
];

View File

@ -34,7 +34,7 @@ use wasm_utils;
use evm::{self, GasLeft, ReturnData};
use action_params::ActionParams;
use self::runtime::Runtime;
use self::runtime::{Runtime, RuntimeContext};
pub use self::runtime::Error as RuntimeError;
@ -82,6 +82,7 @@ impl evm::Evm for WasmInterpreter {
env_memory,
DEFAULT_STACK_SPACE,
params.gas.low_u64(),
RuntimeContext::new(params.address, params.sender),
);
let mut cursor = ::std::io::Cursor::new(&*code);

View File

@ -25,6 +25,7 @@ use ext;
use parity_wasm::interpreter;
use util::{Address, H256, U256};
use call_type::CallType;
use super::ptr::{WasmPtr, Error as PtrError};
use super::call_args::CallArgs;
@ -57,6 +58,20 @@ impl From<PtrError> for Error {
}
}
pub struct RuntimeContext {
address: Address,
sender: Address,
}
impl RuntimeContext {
pub fn new(address: Address, sender: Address) -> Self {
RuntimeContext {
address: address,
sender: sender,
}
}
}
/// Runtime enviroment data for wasm contract execution
pub struct Runtime<'a> {
gas_counter: u64,
@ -64,6 +79,7 @@ pub struct Runtime<'a> {
dynamic_top: u32,
ext: &'a mut ext::Ext,
memory: Arc<interpreter::MemoryInstance>,
context: RuntimeContext,
}
impl<'a> Runtime<'a> {
@ -73,6 +89,7 @@ impl<'a> Runtime<'a> {
memory: Arc<interpreter::MemoryInstance>,
stack_space: u32,
gas_limit: u64,
context: RuntimeContext,
) -> Runtime<'b> {
Runtime {
gas_counter: 0,
@ -80,6 +97,7 @@ impl<'a> Runtime<'a> {
dynamic_top: stack_space,
memory: memory,
ext: ext,
context: context,
}
}
@ -139,7 +157,7 @@ impl<'a> Runtime<'a> {
trace!(target: "wasm", "runtime: create contract");
let mut context = context;
let result_ptr = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " result_ptr: {:?}", result_ptr);
trace!(target: "wasm", "result_ptr: {:?}", result_ptr);
let code_len = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " code_len: {:?}", code_len);
let code_ptr = context.value_stack.pop_as::<i32>()? as u32;
@ -167,6 +185,127 @@ impl<'a> Runtime<'a> {
}
}
pub fn call(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
//
// method signature:
// fn (
// address: *const u8,
// val_ptr: *const u8,
// input_ptr: *const u8,
// input_len: u32,
// result_ptr: *mut u8,
// result_len: u32,
// ) -> i32
self.do_call(true, CallType::Call, context)
}
fn call_code(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
//
// signature (same as static call):
// fn (
// address: *const u8,
// input_ptr: *const u8,
// input_len: u32,
// result_ptr: *mut u8,
// result_len: u32,
// ) -> i32
self.do_call(false, CallType::CallCode, context)
}
fn do_call(
&mut self,
use_val: bool,
call_type: CallType,
context: interpreter::CallerContext,
)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
trace!(target: "wasm", "runtime: call code");
let mut context = context;
let result_alloc_len = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " result_len: {:?}", result_alloc_len);
let result_ptr = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " result_ptr: {:?}", result_ptr);
let input_len = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " input_len: {:?}", input_len);
let input_ptr = context.value_stack.pop_as::<i32>()? as u32;
trace!(target: "wasm", " input_ptr: {:?}", input_ptr);
let val = if use_val { Some(self.pop_u256(&mut context)?) }
else { None };
trace!(target: "wasm", " val: {:?}", val);
let address = self.pop_address(&mut context)?;
trace!(target: "wasm", " address: {:?}", address);
if let Some(ref val) = val {
let address_balance = self.ext.balance(&self.context.address)
.map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?;
if &address_balance < val {
trace!(target: "wasm", "runtime: call failed due to balance check");
return Ok(Some((-1i32).into()));
}
}
let mut result = Vec::with_capacity(result_alloc_len as usize);
result.resize(result_alloc_len as usize, 0);
let gas = self.gas_left()
.map_err(|_| interpreter::Error::Trap("Gas state error".to_owned()))?
.into();
// todo: optimize to use memory views once it's in
let payload = self.memory.get(input_ptr, input_len as usize)?;
let call_result = self.ext.call(
&gas,
&self.context.sender,
&self.context.address,
val,
&payload,
&address,
&mut result[..],
call_type,
);
match call_result {
ext::MessageCallResult::Success(gas_left, _) => {
self.gas_counter = self.gas_limit - gas_left.low_u64();
self.memory.set(result_ptr, &result)?;
Ok(Some(0i32.into()))
},
ext::MessageCallResult::Failed => {
Ok(Some((-1i32).into()))
}
}
}
pub fn static_call(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
{
// signature (same as code call):
// fn (
// address: *const u8,
// input_ptr: *const u8,
// input_len: u32,
// result_ptr: *mut u8,
// result_len: u32,
// ) -> i32
self.do_call(false, CallType::StaticCall, context)
}
/// Allocate memory using the wasm stack params
pub fn malloc(&mut self, context: interpreter::CallerContext)
-> Result<Option<interpreter::RuntimeValue>, interpreter::Error>
@ -338,6 +477,15 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
"_create" => {
self.create(context)
},
"_ccall" => {
self.call(context)
},
"_dcall" => {
self.call_code(context)
},
"_scall" => {
self.static_call(context)
},
"_debug" => {
self.debug_log(context)
},
@ -348,7 +496,7 @@ impl<'a> interpreter::UserFunctionExecutor for Runtime<'a> {
self.mem_copy(context)
},
_ => {
trace!("Unknown env func: '{}'", name);
trace!(target: "wasm", "Trapped due to unhandled function: '{}'", name);
self.user_trap(context)
}
}

View File

@ -21,6 +21,7 @@ use super::WasmInterpreter;
use evm::{self, Evm, GasLeft};
use action_params::{ActionParams, ActionValue};
use util::{U256, H256, Address};
use byteorder::{LittleEndian, ByteOrder};
macro_rules! load_sample {
($name: expr) => {
@ -85,7 +86,7 @@ fn logger() {
};
println!("ext.store: {:?}", ext.store);
assert_eq!(gas_left, U256::from(99581));
assert_eq!(gas_left, U256::from(99590));
let address_val: H256 = address.into();
assert_eq!(
ext.store.get(&"0100000000000000000000000000000000000000000000000000000000000000".parse().unwrap()).expect("storage key to exist"),
@ -136,7 +137,7 @@ fn identity() {
}
};
assert_eq!(gas_left, U256::from(99_689));
assert_eq!(gas_left, U256::from(99_687));
assert_eq!(
Address::from_slice(&result),
@ -170,7 +171,7 @@ fn dispersion() {
}
};
assert_eq!(gas_left, U256::from(99_402));
assert_eq!(gas_left, U256::from(99_423));
assert_eq!(
result,
@ -199,7 +200,7 @@ fn suicide_not() {
}
};
assert_eq!(gas_left, U256::from(99_703));
assert_eq!(gas_left, U256::from(99_656));
assert_eq!(
result,
@ -233,12 +234,14 @@ fn suicide() {
}
};
assert_eq!(gas_left, U256::from(99_747));
assert_eq!(gas_left, U256::from(99_740));
assert!(ext.suicides.contains(&refund));
}
#[test]
fn create() {
::ethcore_logger::init_log();
let mut params = ActionParams::default();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("creator.wasm")));
@ -262,7 +265,7 @@ fn create() {
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Create,
gas: U256::from(99_778),
gas: U256::from(99_767),
sender_address: None,
receive_address: None,
value: Some(1_000_000_000.into()),
@ -270,5 +273,96 @@ fn create() {
code_address: None,
}
));
assert_eq!(gas_left, U256::from(99_768));
assert_eq!(gas_left, U256::from(99_759));
}
#[test]
fn call_code() {
::ethcore_logger::init_log();
let sender: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap();
let receiver: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap();
let mut params = ActionParams::default();
params.sender = sender.clone();
params.address = receiver.clone();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("call_code.wasm")));
params.data = Some(Vec::new());
params.value = ActionValue::transfer(1_000_000_000);
let mut ext = FakeExt::new();
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("Call test should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
trace!(target: "wasm", "fake_calls: {:?}", &ext.calls);
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
gas: U256::from(99_061),
sender_address: Some(sender),
receive_address: Some(receiver),
value: None,
data: vec![1u8, 2, 3, 5, 7, 11],
code_address: Some("0d13710000000000000000000000000000000000".parse().unwrap()),
}
));
assert_eq!(gas_left, U256::from(94196));
// siphash result
let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 4198595614);
}
#[test]
fn call_static() {
::ethcore_logger::init_log();
let sender: Address = "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6".parse().unwrap();
let receiver: Address = "01030507090b0d0f11131517191b1d1f21232527".parse().unwrap();
let mut params = ActionParams::default();
params.sender = sender.clone();
params.address = receiver.clone();
params.gas = U256::from(100_000);
params.code = Some(Arc::new(load_sample!("call_static.wasm")));
params.data = Some(Vec::new());
params.value = ActionValue::transfer(1_000_000_000);
let mut ext = FakeExt::new();
let (gas_left, result) = {
let mut interpreter = wasm_interpreter();
let result = interpreter.exec(params, &mut ext).expect("Interpreter to execute without any errors");
match result {
GasLeft::Known(_) => { panic!("Static call test should return payload"); },
GasLeft::NeedsReturn { gas_left: gas, data: result, apply_state: _apply } => (gas, result.to_vec()),
}
};
trace!(target: "wasm", "fake_calls: {:?}", &ext.calls);
assert!(ext.calls.contains(
&FakeCall {
call_type: FakeCallType::Call,
gas: U256::from(99_061),
sender_address: Some(sender),
receive_address: Some(receiver),
value: None,
data: vec![1u8, 2, 3, 5, 7, 11],
code_address: Some("13077bfb00000000000000000000000000000000".parse().unwrap()),
}
));
assert_eq!(gas_left, U256::from(94196));
// siphash result
let res = LittleEndian::read_u32(&result[..]);
assert_eq!(res, 317632590);
}

@ -1 +1 @@
Subproject commit 9ed6304313fa949ed92aa0570fb2bc759fb6dc58
Subproject commit 8361f18c7ea133d9b85edf7dea02f05b9feb1938