wasm mvp continued
This commit is contained in:
parent
5fb32229f9
commit
5180919e52
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
)
|
||||
];
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user