mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-12 08:45:42 +00:00
Argument passing and returning values when invoking sandboxed funcs (#189)
This commit is contained in:
@@ -14,6 +14,9 @@ substrate-runtime-std = { path = "../runtime-std", default_features = false }
|
||||
substrate-runtime-io = { path = "../runtime-io", default_features = false }
|
||||
substrate-codec = { path = "../codec", default_features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
wabt = "0.1.7"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
|
||||
@@ -40,9 +40,13 @@
|
||||
|
||||
extern crate substrate_codec as codec;
|
||||
extern crate substrate_runtime_io as runtime_io;
|
||||
#[cfg_attr(not(feature = "std"), macro_use)]
|
||||
extern crate substrate_runtime_std as rstd;
|
||||
extern crate substrate_primitives as primitives;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate wabt;
|
||||
|
||||
use rstd::prelude::*;
|
||||
|
||||
pub use primitives::sandbox::{TypedValue, ReturnValue, HostError};
|
||||
|
||||
@@ -20,9 +20,11 @@ use rstd::collections::btree_map::BTreeMap;
|
||||
use rstd::fmt;
|
||||
|
||||
|
||||
use self::wasmi::{Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, ImportResolver,
|
||||
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind};
|
||||
use self::wasmi::{
|
||||
Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, ImportResolver,
|
||||
MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef,
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind
|
||||
};
|
||||
use self::wasmi::memory_units::Pages;
|
||||
use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError};
|
||||
|
||||
@@ -208,8 +210,9 @@ impl<T> ImportResolver for EnvironmentDefinitionBuilder<T> {
|
||||
_field_name: &str,
|
||||
_global_type: &GlobalDescriptor,
|
||||
) -> Result<GlobalRef, wasmi::Error> {
|
||||
// TODO: Implement sandboxed globals.
|
||||
unimplemented!()
|
||||
Err(wasmi::Error::Instantiation(format!(
|
||||
"Importing globals is not supported yet"
|
||||
)))
|
||||
}
|
||||
|
||||
fn resolve_memory(
|
||||
@@ -243,8 +246,9 @@ impl<T> ImportResolver for EnvironmentDefinitionBuilder<T> {
|
||||
_field_name: &str,
|
||||
_table_type: &TableDescriptor,
|
||||
) -> Result<TableRef, wasmi::Error> {
|
||||
// TODO: Implement sandboxed tables.
|
||||
unimplemented!()
|
||||
Err(wasmi::Error::Instantiation(format!(
|
||||
"Importing tables is not supported yet"
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,10 +288,7 @@ impl<T> Instance<T> {
|
||||
args: &[TypedValue],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
if args.len() > 0 {
|
||||
// TODO: Convert args into `RuntimeValue` and use it.
|
||||
unimplemented!();
|
||||
}
|
||||
let args = args.iter().cloned().map(Into::into).collect::<Vec<_>>();
|
||||
|
||||
let name = ::std::str::from_utf8(name).map_err(|_| Error::Execution)?;
|
||||
let mut externals = GuestExternals {
|
||||
@@ -295,15 +296,112 @@ impl<T> Instance<T> {
|
||||
defined_host_functions: &self.defined_host_functions,
|
||||
};
|
||||
let result = self.instance
|
||||
.invoke_export(&name, &[], &mut externals);
|
||||
.invoke_export(&name, &args, &mut externals);
|
||||
|
||||
match result {
|
||||
Ok(None) => Ok(ReturnValue::Unit),
|
||||
Ok(_val) => {
|
||||
// TODO: Convert result value into `TypedValue` and return it.
|
||||
unimplemented!();
|
||||
}
|
||||
Ok(Some(val)) => Ok(ReturnValue::Value(val.into())),
|
||||
Err(_err) => Err(Error::Execution),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use wabt;
|
||||
use ::{TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
|
||||
|
||||
fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
struct State {
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
fn env_assert(_e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
if args.len() != 1 {
|
||||
return Err(HostError);
|
||||
}
|
||||
let condition = args[0].as_i32().ok_or_else(|| HostError)?;
|
||||
if condition != 0 {
|
||||
Ok(ReturnValue::Unit)
|
||||
} else {
|
||||
Err(HostError)
|
||||
}
|
||||
}
|
||||
fn env_inc_counter(e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
if args.len() != 1 {
|
||||
return Err(HostError);
|
||||
}
|
||||
let inc_by = args[0].as_i32().ok_or_else(|| HostError)?;
|
||||
e.counter += inc_by as u32;
|
||||
Ok(ReturnValue::Value(TypedValue::I32(e.counter as i32)))
|
||||
}
|
||||
|
||||
let mut state = State { counter: 0 };
|
||||
|
||||
let mut env_builder = EnvironmentDefinitionBuilder::new();
|
||||
env_builder.add_host_func("env", "assert", env_assert);
|
||||
env_builder.add_host_func("env", "inc_counter", env_inc_counter);
|
||||
|
||||
let mut instance = Instance::new(code, &env_builder, &mut state)?;
|
||||
let result = instance.invoke(b"call", args, &mut state);
|
||||
|
||||
result.map_err(|_| HostError)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invoke_args() {
|
||||
let code = wabt::wat2wasm(r#"
|
||||
(module
|
||||
(import "env" "assert" (func $assert (param i32)))
|
||||
|
||||
(func (export "call") (param $x i32) (param $y i64)
|
||||
;; assert that $x = 0x12345678
|
||||
(call $assert
|
||||
(i32.eq
|
||||
(get_local $x)
|
||||
(i32.const 0x12345678)
|
||||
)
|
||||
)
|
||||
|
||||
(call $assert
|
||||
(i64.eq
|
||||
(get_local $y)
|
||||
(i64.const 0x1234567887654321)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#).unwrap();
|
||||
|
||||
let result = execute_sandboxed(
|
||||
&code,
|
||||
&[
|
||||
TypedValue::I32(0x12345678),
|
||||
TypedValue::I64(0x1234567887654321),
|
||||
]
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_value() {
|
||||
let code = wabt::wat2wasm(r#"
|
||||
(module
|
||||
(func (export "call") (param $x i32) (result i32)
|
||||
(i32.add
|
||||
(get_local $x)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#).unwrap();
|
||||
|
||||
let return_val = execute_sandboxed(
|
||||
&code,
|
||||
&[
|
||||
TypedValue::I32(0x1336),
|
||||
]
|
||||
).unwrap();
|
||||
assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ mod ffi {
|
||||
instance_idx: u32,
|
||||
export_ptr: *const u8,
|
||||
export_len: usize,
|
||||
args_ptr: *const u8,
|
||||
args_len: usize,
|
||||
return_val_ptr: *mut u8,
|
||||
return_val_len: usize,
|
||||
state: usize,
|
||||
) -> u32;
|
||||
pub fn ext_sandbox_memory_new(initial: u32, maximum: u32) -> u32;
|
||||
@@ -260,16 +264,29 @@ impl<T> Instance<T> {
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &[u8],
|
||||
_args: &[TypedValue],
|
||||
args: &[TypedValue],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
// TODO: Serialize arguments and pass them thru.
|
||||
let result =
|
||||
unsafe { ffi::ext_sandbox_invoke(self.instance_idx, name.as_ptr(), name.len(), state as *const T as usize) };
|
||||
let serialized_args = args.to_vec().encode();
|
||||
let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE];
|
||||
|
||||
let result = unsafe {
|
||||
ffi::ext_sandbox_invoke(
|
||||
self.instance_idx,
|
||||
name.as_ptr(),
|
||||
name.len(),
|
||||
serialized_args.as_ptr(),
|
||||
serialized_args.len(),
|
||||
return_val.as_mut_ptr(),
|
||||
return_val.len(),
|
||||
state as *const T as usize,
|
||||
)
|
||||
};
|
||||
match result {
|
||||
sandbox_primitives::ERR_OK => {
|
||||
// TODO: Fetch the result of the execution.
|
||||
Ok(ReturnValue::Unit)
|
||||
let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..])
|
||||
.ok_or(Error::Execution)?;
|
||||
Ok(return_val)
|
||||
}
|
||||
sandbox_primitives::ERR_EXECUTION => Err(Error::Execution),
|
||||
_ => unreachable!(),
|
||||
|
||||
Reference in New Issue
Block a user