mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 14:41:11 +00:00
Add get_global for Sandbox (#4756)
* Add `get_global` for `Sandbox` This pr adds `get_global` to retrieve a `global` variable from an instantiated sandbox wasm blob. * Bump `spec_version` * Update primitives/wasm-interface/src/lib.rs Co-Authored-By: Sergei Pepyakin <sergei@parity.io> * `get_global` -> `get_global_val` Co-authored-by: Sergei Pepyakin <s.pepyakin@gmail.com> Co-authored-by: Gavin Wood <github@gavwood.com>
This commit is contained in:
Generated
+6
-3
@@ -4206,13 +4206,13 @@ dependencies = [
|
||||
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitvec 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byte-slice-cast 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec-derive 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parity-scale-codec-derive"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -6594,6 +6594,7 @@ dependencies = [
|
||||
"sp-state-machine 0.8.0",
|
||||
"sp-std 2.0.0",
|
||||
"sp-trie 2.0.0",
|
||||
"sp-wasm-interface 2.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6724,6 +6725,7 @@ dependencies = [
|
||||
"sp-core 2.0.0",
|
||||
"sp-io 2.0.0",
|
||||
"sp-std 2.0.0",
|
||||
"sp-wasm-interface 2.0.0",
|
||||
"wabt 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -6857,6 +6859,7 @@ name = "sp-wasm-interface"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"impl-trait-for-tuples 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sp-std 2.0.0",
|
||||
"wasmi 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -8700,7 +8703,7 @@ dependencies = [
|
||||
"checksum parity-multihash 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "df3a17dc27848fd99e4f87eb0f8c9baba6ede0a6d555400c850ca45254ef4ce3"
|
||||
"checksum parity-multihash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70a4d7b05e51bff5ae2c29c7b8c3d889985bbd8f4e15b3542fcc1f6f9666d292"
|
||||
"checksum parity-scale-codec 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f747c06d9f3b2ad387ac881b9667298c81b1243aa9833f086e05996937c35507"
|
||||
"checksum parity-scale-codec-derive 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "492ac3aa93d6caa5d20e4e3e0b75d08e2dcd9dd8a50d19529548b6fe11b3f295"
|
||||
"checksum parity-scale-codec-derive 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "34e513ff3e406f3ede6796dcdc83d0b32ffb86668cea1ccf7363118abeb00476"
|
||||
"checksum parity-send-wrapper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f"
|
||||
"checksum parity-util-mem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8174d85e62c4d615fddd1ef67966bdc5757528891d0742f15b131ad04667b3f9"
|
||||
"checksum parity-util-mem 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "900dd84654b048e5bad420bb341658fc2c4d7fea628c22bcf4621733e54859b4"
|
||||
|
||||
@@ -81,7 +81,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||
// implementation changes and behavior does not, then leave spec_version as
|
||||
// is and increment impl_version.
|
||||
spec_version: 208,
|
||||
impl_version: 0,
|
||||
impl_version: 1,
|
||||
apis: RUNTIME_API_VERSIONS,
|
||||
};
|
||||
|
||||
|
||||
@@ -224,7 +224,8 @@ fn trap(msg: &'static str) -> Trap {
|
||||
}
|
||||
|
||||
fn deserialize_result(serialized_result: &[u8]) -> std::result::Result<Option<RuntimeValue>, Trap> {
|
||||
use self::sandbox_primitives::{HostError, ReturnValue};
|
||||
use self::sandbox_primitives::HostError;
|
||||
use sp_wasm_interface::ReturnValue;
|
||||
let result_val = std::result::Result::<ReturnValue, HostError>::decode(&mut &serialized_result[..])
|
||||
.map_err(|_| trap("Decoding Result<ReturnValue, HostError> failed!"))?;
|
||||
|
||||
@@ -260,7 +261,7 @@ impl<'a, FE: SandboxCapabilities + 'a> Externals for GuestExternals<'a, FE> {
|
||||
let invoke_args_data: Vec<u8> = args.as_ref()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(sandbox_primitives::TypedValue::from)
|
||||
.map(sp_wasm_interface::Value::from)
|
||||
.collect::<Vec<_>>()
|
||||
.encode();
|
||||
|
||||
@@ -362,6 +363,18 @@ impl<FR> SandboxInstance<FR> {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the value from a global with the given `name`.
|
||||
///
|
||||
/// Returns `Some(_)` if the global could be found.
|
||||
pub fn get_global_val(&self, name: &str) -> Option<sp_wasm_interface::Value> {
|
||||
let global = self.instance
|
||||
.export_by_name(name)?
|
||||
.as_global()?
|
||||
.get();
|
||||
|
||||
Some(global.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurred during instantiation of a sandboxed module.
|
||||
|
||||
@@ -17,6 +17,8 @@ use sp_io::{
|
||||
use sp_runtime::{print, traits::{BlakeTwo256, Hash}};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_core::{ed25519, sr25519};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use sp_sandbox::Value;
|
||||
|
||||
extern "C" {
|
||||
#[allow(dead_code)]
|
||||
@@ -133,8 +135,8 @@ sp_core::wasm_export_functions! {
|
||||
execute_sandboxed(
|
||||
&code,
|
||||
&[
|
||||
sp_sandbox::TypedValue::I32(0x12345678),
|
||||
sp_sandbox::TypedValue::I64(0x1234567887654321),
|
||||
Value::I32(0x12345678),
|
||||
Value::I64(0x1234567887654321),
|
||||
],
|
||||
).is_ok()
|
||||
}
|
||||
@@ -143,10 +145,10 @@ sp_core::wasm_export_functions! {
|
||||
let ok = match execute_sandboxed(
|
||||
&code,
|
||||
&[
|
||||
sp_sandbox::TypedValue::I32(0x1336),
|
||||
Value::I32(0x1336),
|
||||
]
|
||||
) {
|
||||
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(0x1337))) => true,
|
||||
Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
@@ -165,6 +167,22 @@ sp_core::wasm_export_functions! {
|
||||
code
|
||||
}
|
||||
|
||||
|
||||
fn test_sandbox_get_global_val(code: Vec<u8>) -> i64 {
|
||||
let env_builder = sp_sandbox::EnvironmentDefinitionBuilder::new();
|
||||
let instance = if let Ok(i) = sp_sandbox::Instance::new(&code, &env_builder, &mut ()) {
|
||||
i
|
||||
} else {
|
||||
return 20;
|
||||
};
|
||||
|
||||
match instance.get_global_val("test_global") {
|
||||
Some(sp_sandbox::Value::I64(val)) => val,
|
||||
None => 30,
|
||||
val => 40,
|
||||
}
|
||||
}
|
||||
|
||||
fn test_offchain_local_storage() -> bool {
|
||||
let kind = sp_core::offchain::StorageKind::PERSISTENT;
|
||||
assert_eq!(sp_io::offchain::local_storage_get(kind, b"test"), None);
|
||||
@@ -262,7 +280,7 @@ sp_core::wasm_export_functions! {
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn execute_sandboxed(
|
||||
code: &[u8],
|
||||
args: &[sp_sandbox::TypedValue],
|
||||
args: &[Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
||||
struct State {
|
||||
counter: u32,
|
||||
@@ -270,7 +288,7 @@ fn execute_sandboxed(
|
||||
|
||||
fn env_assert(
|
||||
_e: &mut State,
|
||||
args: &[sp_sandbox::TypedValue],
|
||||
args: &[Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
||||
if args.len() != 1 {
|
||||
return Err(sp_sandbox::HostError);
|
||||
@@ -284,14 +302,14 @@ fn execute_sandboxed(
|
||||
}
|
||||
fn env_inc_counter(
|
||||
e: &mut State,
|
||||
args: &[sp_sandbox::TypedValue],
|
||||
args: &[Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
||||
if args.len() != 1 {
|
||||
return Err(sp_sandbox::HostError);
|
||||
}
|
||||
let inc_by = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?;
|
||||
e.counter += inc_by as u32;
|
||||
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(e.counter as i32)))
|
||||
Ok(sp_sandbox::ReturnValue::Value(Value::I32(e.counter as i32)))
|
||||
}
|
||||
|
||||
let mut state = State { counter: 0 };
|
||||
|
||||
@@ -88,7 +88,7 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) {
|
||||
#[cfg(feature = "wasmtime")]
|
||||
WasmExecutionMethod::Compiled => assert_eq!(
|
||||
&format!("{:?}", e),
|
||||
"Other(\"call to undefined external function with index 67\")"
|
||||
"Other(\"call to undefined external function with index 68\")"
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) {
|
||||
#[cfg(feature = "wasmtime")]
|
||||
WasmExecutionMethod::Compiled => assert_eq!(
|
||||
&format!("{:?}", e),
|
||||
"Other(\"call to undefined external function with index 68\")"
|
||||
"Other(\"call to undefined external function with index 69\")"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,3 +302,26 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) {
|
||||
2u8.encode(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(WasmExecutionMethod::Interpreted)]
|
||||
#[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))]
|
||||
fn get_global_val_works(wasm_method: WasmExecutionMethod) {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext = ext.ext();
|
||||
|
||||
let code = wabt::wat2wasm(r#"
|
||||
(module
|
||||
(global (export "test_global") i64 (i64.const 500))
|
||||
)
|
||||
"#).unwrap().encode();
|
||||
|
||||
assert_eq!(
|
||||
call_in_wasm(
|
||||
"test_sandbox_get_global_val",
|
||||
&code,
|
||||
wasm_method,
|
||||
&mut ext,
|
||||
).unwrap(),
|
||||
500i64.encode(),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
|
||||
trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id);
|
||||
|
||||
// Deserialize arguments and convert them into wasmi types.
|
||||
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &args[..])
|
||||
let args = Vec::<sp_wasm_interface::Value>::decode(&mut &args[..])
|
||||
.map_err(|_| "Can't decode serialized arguments for the invocation")?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
@@ -226,7 +226,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
|
||||
Ok(None) => Ok(sandbox_primitives::ERR_OK),
|
||||
Ok(Some(val)) => {
|
||||
// Serialize return value and write it back into the memory.
|
||||
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
|
||||
sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| {
|
||||
if val.len() > return_val_len as usize {
|
||||
Err("Return value buffer is too small")?;
|
||||
}
|
||||
@@ -269,6 +269,17 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
|
||||
|
||||
Ok(instance_idx_or_err_code as u32)
|
||||
}
|
||||
|
||||
fn get_global_val(
|
||||
&self,
|
||||
instance_idx: u32,
|
||||
name: &str,
|
||||
) -> WResult<Option<sp_wasm_interface::Value>> {
|
||||
self.sandbox_store
|
||||
.instance(instance_idx)
|
||||
.map(|i| i.get_global_val(name))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Will be used on initialization of a module to resolve function and memory imports.
|
||||
|
||||
@@ -286,7 +286,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
|
||||
trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id);
|
||||
|
||||
// Deserialize arguments and convert them into wasmi types.
|
||||
let args = Vec::<sandbox_primitives::TypedValue>::decode(&mut &args[..])
|
||||
let args = Vec::<sp_wasm_interface::Value>::decode(&mut &args[..])
|
||||
.map_err(|_| "Can't decode serialized arguments for the invocation")?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
@@ -299,7 +299,7 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
|
||||
Ok(None) => Ok(sandbox_primitives::ERR_OK),
|
||||
Ok(Some(val)) => {
|
||||
// Serialize return value and write it back into the memory.
|
||||
sandbox_primitives::ReturnValue::Value(val.into()).using_encoded(|val| {
|
||||
sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| {
|
||||
if val.len() > return_val_len as usize {
|
||||
Err("Return value buffer is too small")?;
|
||||
}
|
||||
@@ -337,6 +337,17 @@ impl<'a> Sandbox for FunctionExecutor<'a> {
|
||||
|
||||
Ok(instance_idx_or_err_code as u32)
|
||||
}
|
||||
|
||||
fn get_global_val(
|
||||
&self,
|
||||
instance_idx: u32,
|
||||
name: &str,
|
||||
) -> WResult<Option<sp_wasm_interface::Value>> {
|
||||
self.sandbox_store
|
||||
.instance(instance_idx)
|
||||
.map(|i| i.get_global_val(name))
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// The storage for a Wasmtime invocation argument.
|
||||
|
||||
@@ -126,7 +126,7 @@ macro_rules! define_func {
|
||||
( < E: $ext_ty:tt > $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => {
|
||||
fn $name< E: $ext_ty >(
|
||||
$ctx: &mut $crate::wasm::Runtime<E>,
|
||||
args: &[sp_sandbox::TypedValue],
|
||||
args: &[sp_sandbox::Value],
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> {
|
||||
#[allow(unused)]
|
||||
let mut args = args.iter();
|
||||
@@ -196,7 +196,7 @@ mod tests {
|
||||
use parity_wasm::elements::FunctionType;
|
||||
use parity_wasm::elements::ValueType;
|
||||
use sp_runtime::traits::Zero;
|
||||
use sp_sandbox::{self, ReturnValue, TypedValue};
|
||||
use sp_sandbox::{ReturnValue, Value};
|
||||
use crate::wasm::tests::MockExt;
|
||||
use crate::wasm::Runtime;
|
||||
use crate::exec::Ext;
|
||||
@@ -206,7 +206,7 @@ mod tests {
|
||||
fn macro_unmarshall_then_body_then_marshall_value_or_trap() {
|
||||
fn test_value(
|
||||
_ctx: &mut u32,
|
||||
args: &[sp_sandbox::TypedValue],
|
||||
args: &[sp_sandbox::Value],
|
||||
) -> Result<ReturnValue, sp_sandbox::HostError> {
|
||||
let mut args = args.iter();
|
||||
unmarshall_then_body_then_marshall!(
|
||||
@@ -224,17 +224,17 @@ mod tests {
|
||||
|
||||
let ctx = &mut 0;
|
||||
assert_eq!(
|
||||
test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(3)]).unwrap(),
|
||||
ReturnValue::Value(TypedValue::I32(5)),
|
||||
test_value(ctx, &[Value::I32(15), Value::I32(3)]).unwrap(),
|
||||
ReturnValue::Value(Value::I32(5)),
|
||||
);
|
||||
assert!(test_value(ctx, &[TypedValue::I32(15), TypedValue::I32(0)]).is_err());
|
||||
assert!(test_value(ctx, &[Value::I32(15), Value::I32(0)]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn macro_unmarshall_then_body_then_marshall_unit() {
|
||||
fn test_unit(
|
||||
ctx: &mut u32,
|
||||
args: &[sp_sandbox::TypedValue],
|
||||
args: &[sp_sandbox::Value],
|
||||
) -> Result<ReturnValue, sp_sandbox::HostError> {
|
||||
let mut args = args.iter();
|
||||
unmarshall_then_body_then_marshall!(
|
||||
@@ -248,7 +248,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let ctx = &mut 0;
|
||||
let result = test_unit(ctx, &[TypedValue::I32(2), TypedValue::I32(3)]).unwrap();
|
||||
let result = test_unit(ctx, &[Value::I32(2), Value::I32(3)]).unwrap();
|
||||
assert_eq!(result, ReturnValue::Unit);
|
||||
assert_eq!(*ctx, 5);
|
||||
}
|
||||
@@ -263,7 +263,7 @@ mod tests {
|
||||
Err(sp_sandbox::HostError)
|
||||
}
|
||||
});
|
||||
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::TypedValue])
|
||||
let _f: fn(&mut Runtime<MockExt>, &[sp_sandbox::Value])
|
||||
-> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError> = ext_gas::<MockExt>;
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn macro_unmarshall_then_body() {
|
||||
let args = vec![TypedValue::I32(5), TypedValue::I32(3)];
|
||||
let args = vec![Value::I32(5), Value::I32(3)];
|
||||
let mut args = args.iter();
|
||||
|
||||
let ctx: &mut u32 = &mut 0;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use super::Runtime;
|
||||
use crate::exec::Ext;
|
||||
|
||||
use sp_sandbox::{self, TypedValue};
|
||||
use sp_sandbox::Value;
|
||||
use parity_wasm::elements::{FunctionType, ValueType};
|
||||
|
||||
#[macro_use]
|
||||
@@ -26,28 +26,28 @@ pub(crate) mod macros;
|
||||
pub trait ConvertibleToWasm: Sized {
|
||||
const VALUE_TYPE: ValueType;
|
||||
type NativeType;
|
||||
fn to_typed_value(self) -> TypedValue;
|
||||
fn from_typed_value(_: TypedValue) -> Option<Self>;
|
||||
fn to_typed_value(self) -> Value;
|
||||
fn from_typed_value(_: Value) -> Option<Self>;
|
||||
}
|
||||
impl ConvertibleToWasm for i32 {
|
||||
type NativeType = i32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_typed_value(self) -> TypedValue {
|
||||
TypedValue::I32(self)
|
||||
fn to_typed_value(self) -> Value {
|
||||
Value::I32(self)
|
||||
}
|
||||
fn from_typed_value(v: TypedValue) -> Option<Self> {
|
||||
fn from_typed_value(v: Value) -> Option<Self> {
|
||||
v.as_i32()
|
||||
}
|
||||
}
|
||||
impl ConvertibleToWasm for u32 {
|
||||
type NativeType = u32;
|
||||
const VALUE_TYPE: ValueType = ValueType::I32;
|
||||
fn to_typed_value(self) -> TypedValue {
|
||||
TypedValue::I32(self as i32)
|
||||
fn to_typed_value(self) -> Value {
|
||||
Value::I32(self as i32)
|
||||
}
|
||||
fn from_typed_value(v: TypedValue) -> Option<Self> {
|
||||
fn from_typed_value(v: Value) -> Option<Self> {
|
||||
match v {
|
||||
TypedValue::I32(v) => Some(v as u32),
|
||||
Value::I32(v) => Some(v as u32),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -55,12 +55,12 @@ impl ConvertibleToWasm for u32 {
|
||||
impl ConvertibleToWasm for u64 {
|
||||
type NativeType = u64;
|
||||
const VALUE_TYPE: ValueType = ValueType::I64;
|
||||
fn to_typed_value(self) -> TypedValue {
|
||||
TypedValue::I64(self as i64)
|
||||
fn to_typed_value(self) -> Value {
|
||||
Value::I64(self as i64)
|
||||
}
|
||||
fn from_typed_value(v: TypedValue) -> Option<Self> {
|
||||
fn from_typed_value(v: Value) -> Option<Self> {
|
||||
match v {
|
||||
TypedValue::I64(v) => Some(v as u64),
|
||||
Value::I64(v) => Some(v as u64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ impl ConvertibleToWasm for u64 {
|
||||
pub(crate) type HostFunc<E> =
|
||||
fn(
|
||||
&mut Runtime<E>,
|
||||
&[sp_sandbox::TypedValue]
|
||||
&[sp_sandbox::Value]
|
||||
) -> Result<sp_sandbox::ReturnValue, sp_sandbox::HostError>;
|
||||
|
||||
pub(crate) trait FunctionImplProvider<E: Ext> {
|
||||
|
||||
@@ -23,9 +23,7 @@ use crate::exec::{
|
||||
use crate::gas::{Gas, GasMeter, Token, GasMeterResult, approx_gas_for_balance};
|
||||
use sp_sandbox;
|
||||
use frame_system;
|
||||
use sp_std::prelude::*;
|
||||
use sp_std::convert::TryInto;
|
||||
use sp_std::mem;
|
||||
use sp_std::{prelude::*, mem, convert::TryInto};
|
||||
use codec::{Decode, Encode};
|
||||
use sp_runtime::traits::{Bounded, SaturatedConversion};
|
||||
|
||||
@@ -89,7 +87,7 @@ pub(crate) fn to_execution_result<E: Ext>(
|
||||
buffer.clear();
|
||||
Ok(ExecReturnValue { status: STATUS_SUCCESS, data: buffer })
|
||||
}
|
||||
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::TypedValue::I32(exit_code))) => {
|
||||
Ok(sp_sandbox::ReturnValue::Value(sp_sandbox::Value::I32(exit_code))) => {
|
||||
let status = (exit_code & 0xFF).try_into()
|
||||
.expect("exit_code is masked into the range of a u8; qed");
|
||||
Ok(ExecReturnValue { status, data: runtime.scratch_buf })
|
||||
|
||||
@@ -24,99 +24,6 @@ use sp_std::vec::Vec;
|
||||
#[derive(crate::RuntimeDebug)]
|
||||
pub struct HostError;
|
||||
|
||||
/// Representation of a typed wasm value.
|
||||
#[derive(Clone, Copy, PartialEq, Encode, Decode)]
|
||||
#[derive(crate::RuntimeDebug)]
|
||||
pub enum TypedValue {
|
||||
/// Value of 32-bit signed or unsigned integer.
|
||||
#[codec(index = "1")]
|
||||
I32(i32),
|
||||
|
||||
/// Value of 64-bit signed or unsigned integer.
|
||||
#[codec(index = "2")]
|
||||
I64(i64),
|
||||
|
||||
/// Value of 32-bit IEEE 754-2008 floating point number represented as a bit pattern.
|
||||
#[codec(index = "3")]
|
||||
F32(i32),
|
||||
|
||||
/// Value of 64-bit IEEE 754-2008 floating point number represented as a bit pattern.
|
||||
#[codec(index = "4")]
|
||||
F64(i64),
|
||||
}
|
||||
|
||||
impl TypedValue {
|
||||
/// Returns `Some` if this value of type `I32`.
|
||||
pub fn as_i32(&self) -> Option<i32> {
|
||||
match *self {
|
||||
TypedValue::I32(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<::wasmi::RuntimeValue> for TypedValue {
|
||||
fn from(val: ::wasmi::RuntimeValue) -> TypedValue {
|
||||
use ::wasmi::RuntimeValue;
|
||||
match val {
|
||||
RuntimeValue::I32(v) => TypedValue::I32(v),
|
||||
RuntimeValue::I64(v) => TypedValue::I64(v),
|
||||
RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32),
|
||||
RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<TypedValue> for ::wasmi::RuntimeValue {
|
||||
fn from(val: TypedValue) -> ::wasmi::RuntimeValue {
|
||||
use ::wasmi::RuntimeValue;
|
||||
use ::wasmi::nan_preserving_float::{F32, F64};
|
||||
match val {
|
||||
TypedValue::I32(v) => RuntimeValue::I32(v),
|
||||
TypedValue::I64(v) => RuntimeValue::I64(v),
|
||||
TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)),
|
||||
TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed value that can be returned from a function.
|
||||
///
|
||||
/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
|
||||
#[derive(Clone, Copy, PartialEq, Encode, Decode)]
|
||||
#[derive(crate::RuntimeDebug)]
|
||||
pub enum ReturnValue {
|
||||
/// For returning nothing.
|
||||
Unit,
|
||||
/// For returning some concrete value.
|
||||
Value(TypedValue),
|
||||
}
|
||||
|
||||
impl From<TypedValue> for ReturnValue {
|
||||
fn from(v: TypedValue) -> ReturnValue {
|
||||
ReturnValue::Value(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnValue {
|
||||
/// Maximum number of bytes `ReturnValue` might occupy when serialized with
|
||||
/// `Codec`.
|
||||
///
|
||||
/// Breakdown:
|
||||
/// 1 byte for encoding unit/value variant
|
||||
/// 1 byte for encoding value type
|
||||
/// 8 bytes for encoding the biggest value types available in wasm: f64, i64.
|
||||
pub const ENCODED_MAX_SIZE: usize = 10;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_value_encoded_max_size() {
|
||||
let encoded = ReturnValue::Value(TypedValue::I64(-1)).encode();
|
||||
assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
|
||||
}
|
||||
|
||||
/// Describes an entity to define or import into the environment.
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[derive(crate::RuntimeDebug)]
|
||||
|
||||
@@ -12,6 +12,7 @@ sp-core = { version = "2.0.0", default-features = false, path = "../core" }
|
||||
sp-std = { version = "2.0.0", default-features = false, path = "../std" }
|
||||
libsecp256k1 = { version = "0.3.4", optional = true }
|
||||
sp-state-machine = { version = "0.8", optional = true, path = "../../primitives/state-machine" }
|
||||
sp-wasm-interface = { version = "2.0.0", path = "../../primitives/wasm-interface", default-features = false }
|
||||
sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../runtime-interface" }
|
||||
sp-trie = { version = "2.0.0", optional = true, path = "../../primitives/trie" }
|
||||
sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" }
|
||||
@@ -29,6 +30,7 @@ std = [
|
||||
"libsecp256k1",
|
||||
"sp-runtime-interface/std",
|
||||
"sp-externalities",
|
||||
"sp-wasm-interface/std",
|
||||
"log",
|
||||
]
|
||||
|
||||
|
||||
@@ -850,6 +850,14 @@ pub trait Sandbox {
|
||||
fn instance_teardown(&mut self, instance_idx: u32) {
|
||||
self.sandbox().instance_teardown(instance_idx).expect("Failed to teardown sandbox instance")
|
||||
}
|
||||
|
||||
/// Get the value from a global with the given `name`. The sandbox is determined by the given
|
||||
/// `instance_idx`.
|
||||
///
|
||||
/// Returns `Some(_)` when the requested global variable could be found.
|
||||
fn get_global_val(&mut self, instance_idx: u32, name: &str) -> Option<sp_wasm_interface::Value> {
|
||||
self.sandbox().get_global_val(instance_idx, name).expect("Failed to get global from sandbox")
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocator used by Substrate when executing the Wasm runtime.
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
sp-wasm-interface = { version = "2.0.0", optional = true, path = "../wasm-interface" }
|
||||
sp-wasm-interface = { version = "2.0.0", path = "../wasm-interface", default-features = false }
|
||||
sp-std = { version = "2.0.0", default-features = false, path = "../std" }
|
||||
sp-runtime-interface-proc-macro = { version = "2.0.0", path = "proc-macro" }
|
||||
sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" }
|
||||
@@ -25,7 +25,7 @@ trybuild = "1.0.17"
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [
|
||||
"sp-wasm-interface",
|
||||
"sp-wasm-interface/std",
|
||||
"sp-std/std",
|
||||
"codec/std",
|
||||
"sp-externalities",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
//! Provides implementations for the runtime interface traits.
|
||||
|
||||
use crate::{
|
||||
RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner},
|
||||
RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner, Enum},
|
||||
util::{unpack_ptr_and_len, pack_ptr_and_len},
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
@@ -523,3 +523,11 @@ macro_rules! for_u128_i128 {
|
||||
|
||||
for_u128_i128!(u128);
|
||||
for_u128_i128!(i128);
|
||||
|
||||
impl PassBy for sp_wasm_interface::ValueType {
|
||||
type PassBy = Enum<sp_wasm_interface::ValueType>;
|
||||
}
|
||||
|
||||
impl PassBy for sp_wasm_interface::Value {
|
||||
type PassBy = Codec<sp_wasm_interface::Value>;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.1.0", default-features = false, features = ["derive"] }
|
||||
codec = { package = "parity-scale-codec", version = "1.1.2", default-features = false, features = ["derive"] }
|
||||
sp-core = { version = "2.0.0", default-features = false, path = "../core" }
|
||||
sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" }
|
||||
sp-arithmetic = { version = "2.0.0", default-features = false, path = "../arithmetic" }
|
||||
|
||||
@@ -10,6 +10,7 @@ wasmi = { version = "0.6.2", optional = true }
|
||||
sp-core = { version = "2.0.0", default-features = false, path = "../core" }
|
||||
sp-std = { version = "2.0.0", default-features = false, path = "../std" }
|
||||
sp-io = { version = "2.0.0", default-features = false, path = "../io" }
|
||||
sp-wasm-interface = { version = "2.0.0", default-features = false, path = "../wasm-interface" }
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -24,5 +25,6 @@ std = [
|
||||
"sp-std/std",
|
||||
"codec/std",
|
||||
"sp-io/std",
|
||||
"sp-wasm-interface/std",
|
||||
]
|
||||
strict = []
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
|
||||
use sp_std::prelude::*;
|
||||
|
||||
pub use sp_core::sandbox::{TypedValue, ReturnValue, HostError};
|
||||
pub use sp_core::sandbox::HostError;
|
||||
pub use sp_wasm_interface::{Value, ReturnValue};
|
||||
|
||||
mod imp {
|
||||
#[cfg(feature = "std")]
|
||||
@@ -75,7 +76,7 @@ impl From<Error> for HostError {
|
||||
/// supervisor in [`EnvironmentDefinitionBuilder`].
|
||||
///
|
||||
/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
|
||||
pub type HostFuncType<T> = fn(&mut T, &[TypedValue]) -> Result<ReturnValue, HostError>;
|
||||
pub type HostFuncType<T> = fn(&mut T, &[Value]) -> Result<ReturnValue, HostError>;
|
||||
|
||||
/// Reference to a sandboxed linear memory, that
|
||||
/// will be used by the guest module.
|
||||
@@ -197,9 +198,16 @@ impl<T> Instance<T> {
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[TypedValue],
|
||||
args: &[Value],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
self.inner.invoke(name, args, state)
|
||||
}
|
||||
|
||||
/// Get the value from a global with the given `name`.
|
||||
///
|
||||
/// Returns `Some(_)` if the global could be found.
|
||||
pub fn get_global_val(&self, name: &str) -> Option<Value> {
|
||||
self.inner.get_global_val(name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use wasmi::{
|
||||
RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind
|
||||
};
|
||||
use wasmi::memory_units::Pages;
|
||||
use super::{Error, TypedValue, ReturnValue, HostFuncType, HostError};
|
||||
use super::{Error, Value, ReturnValue, HostFuncType, HostError};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Memory {
|
||||
@@ -88,27 +88,7 @@ impl fmt::Display for DummyHostError {
|
||||
}
|
||||
}
|
||||
|
||||
impl wasmi::HostError for DummyHostError {
|
||||
}
|
||||
|
||||
fn from_runtime_value(v: RuntimeValue) -> TypedValue {
|
||||
match v {
|
||||
RuntimeValue::I32(v) => TypedValue::I32(v),
|
||||
RuntimeValue::I64(v) => TypedValue::I64(v),
|
||||
RuntimeValue::F32(v) => TypedValue::F32(v.to_bits() as i32),
|
||||
RuntimeValue::F64(v) => TypedValue::F64(v.to_bits() as i64),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_runtime_value(v: TypedValue) -> RuntimeValue {
|
||||
use wasmi::nan_preserving_float::{F32, F64};
|
||||
match v {
|
||||
TypedValue::I32(v) => RuntimeValue::I32(v as i32),
|
||||
TypedValue::I64(v) => RuntimeValue::I64(v as i64),
|
||||
TypedValue::F32(v_bits) => RuntimeValue::F32(F32::from_bits(v_bits as u32)),
|
||||
TypedValue::F64(v_bits) => RuntimeValue::F64(F64::from_bits(v_bits as u64)),
|
||||
}
|
||||
}
|
||||
impl wasmi::HostError for DummyHostError {}
|
||||
|
||||
struct GuestExternals<'a, T: 'a> {
|
||||
state: &'a mut T,
|
||||
@@ -124,13 +104,13 @@ impl<'a, T> Externals for GuestExternals<'a, T> {
|
||||
let args = args.as_ref()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(from_runtime_value)
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let result = (self.defined_host_functions.funcs[index])(self.state, &args);
|
||||
match result {
|
||||
Ok(value) => Ok(match value {
|
||||
ReturnValue::Value(v) => Some(to_runtime_value(v)),
|
||||
ReturnValue::Value(v) => Some(v.into()),
|
||||
ReturnValue::Unit => None,
|
||||
}),
|
||||
Err(HostError) => Err(TrapKind::Host(Box::new(DummyHostError)).into()),
|
||||
@@ -253,7 +233,7 @@ impl<T> ImportResolver for EnvironmentDefinitionBuilder<T> {
|
||||
pub struct Instance<T> {
|
||||
instance: ModuleRef,
|
||||
defined_host_functions: DefinedHostFunctions<T>,
|
||||
_marker: ::std::marker::PhantomData<T>,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Instance<T> {
|
||||
@@ -281,14 +261,14 @@ impl<T> Instance<T> {
|
||||
Ok(Instance {
|
||||
instance,
|
||||
defined_host_functions,
|
||||
_marker: ::std::marker::PhantomData::<T>,
|
||||
_marker: std::marker::PhantomData::<T>,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[TypedValue],
|
||||
args: &[Value],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
let args = args.iter().cloned().map(Into::into).collect::<Vec<_>>();
|
||||
@@ -306,20 +286,29 @@ impl<T> Instance<T> {
|
||||
Err(_err) => Err(Error::Execution),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_global_val(&self, name: &str) -> Option<Value> {
|
||||
let global = self.instance
|
||||
.export_by_name(name)?
|
||||
.as_global()?
|
||||
.get();
|
||||
|
||||
Some(global.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use wabt;
|
||||
use crate::{Error, TypedValue, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
|
||||
use crate::{Error, Value, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance};
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
fn execute_sandboxed(code: &[u8], args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
fn execute_sandboxed(code: &[u8], args: &[Value]) -> Result<ReturnValue, HostError> {
|
||||
struct State {
|
||||
counter: u32,
|
||||
}
|
||||
|
||||
fn env_assert(_e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
fn env_assert(_e: &mut State, args: &[Value]) -> Result<ReturnValue, HostError> {
|
||||
if args.len() != 1 {
|
||||
return Err(HostError);
|
||||
}
|
||||
@@ -330,16 +319,16 @@ mod tests {
|
||||
Err(HostError)
|
||||
}
|
||||
}
|
||||
fn env_inc_counter(e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
fn env_inc_counter(e: &mut State, args: &[Value]) -> 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)))
|
||||
Ok(ReturnValue::Value(Value::I32(e.counter as i32)))
|
||||
}
|
||||
/// Function that takes one argument of any type and returns that value.
|
||||
fn env_polymorphic_id(_e: &mut State, args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
fn env_polymorphic_id(_e: &mut State, args: &[Value]) -> Result<ReturnValue, HostError> {
|
||||
if args.len() != 1 {
|
||||
return Err(HostError);
|
||||
}
|
||||
@@ -387,8 +376,8 @@ mod tests {
|
||||
let result = execute_sandboxed(
|
||||
&code,
|
||||
&[
|
||||
TypedValue::I32(0x12345678),
|
||||
TypedValue::I64(0x1234567887654321),
|
||||
Value::I32(0x12345678),
|
||||
Value::I64(0x1234567887654321),
|
||||
]
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
@@ -410,10 +399,10 @@ mod tests {
|
||||
let return_val = execute_sandboxed(
|
||||
&code,
|
||||
&[
|
||||
TypedValue::I32(0x1336),
|
||||
Value::I32(0x1336),
|
||||
]
|
||||
).unwrap();
|
||||
assert_eq!(return_val, ReturnValue::Value(TypedValue::I32(0x1337)));
|
||||
assert_eq!(return_val, ReturnValue::Value(Value::I32(0x1337)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -453,8 +442,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cant_return_unmatching_type() {
|
||||
fn env_returns_i32(_e: &mut (), _args: &[TypedValue]) -> Result<ReturnValue, HostError> {
|
||||
Ok(ReturnValue::Value(TypedValue::I32(42)))
|
||||
fn env_returns_i32(_e: &mut (), _args: &[Value]) -> Result<ReturnValue, HostError> {
|
||||
Ok(ReturnValue::Value(Value::I32(42)))
|
||||
}
|
||||
|
||||
let mut env_builder = EnvironmentDefinitionBuilder::new();
|
||||
|
||||
@@ -18,7 +18,7 @@ use codec::{Decode, Encode};
|
||||
use sp_core::sandbox as sandbox_primitives;
|
||||
use sp_io::sandbox;
|
||||
use sp_std::{prelude::*, slice, marker, mem, vec, rc::Rc};
|
||||
use super::{Error, TypedValue, ReturnValue, HostFuncType};
|
||||
use super::{Error, Value, ReturnValue, HostFuncType};
|
||||
|
||||
mod ffi {
|
||||
use sp_std::mem;
|
||||
@@ -183,7 +183,7 @@ extern "C" fn dispatch_thunk<T>(
|
||||
slice::from_raw_parts(serialized_args_ptr, serialized_args_len)
|
||||
}
|
||||
};
|
||||
let args = Vec::<TypedValue>::decode(&mut &serialized_args[..]).expect(
|
||||
let args = Vec::<Value>::decode(&mut &serialized_args[..]).expect(
|
||||
"serialized args should be provided by the runtime;
|
||||
correctly serialized data should be deserializable;
|
||||
qed",
|
||||
@@ -244,11 +244,11 @@ impl<T> Instance<T> {
|
||||
pub fn invoke(
|
||||
&mut self,
|
||||
name: &str,
|
||||
args: &[TypedValue],
|
||||
args: &[Value],
|
||||
state: &mut T,
|
||||
) -> Result<ReturnValue, Error> {
|
||||
let serialized_args = args.to_vec().encode();
|
||||
let mut return_val = vec![0u8; sandbox_primitives::ReturnValue::ENCODED_MAX_SIZE];
|
||||
let mut return_val = vec![0u8; ReturnValue::ENCODED_MAX_SIZE];
|
||||
|
||||
let result = sandbox::invoke(
|
||||
self.instance_idx,
|
||||
@@ -261,7 +261,7 @@ impl<T> Instance<T> {
|
||||
|
||||
match result {
|
||||
sandbox_primitives::ERR_OK => {
|
||||
let return_val = sandbox_primitives::ReturnValue::decode(&mut &return_val[..])
|
||||
let return_val = ReturnValue::decode(&mut &return_val[..])
|
||||
.map_err(|_| Error::Execution)?;
|
||||
Ok(return_val)
|
||||
}
|
||||
@@ -269,6 +269,10 @@ impl<T> Instance<T> {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_global_val(&self, name: &str) -> Option<Value> {
|
||||
sandbox::get_global_val(self.instance_idx, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Instance<T> {
|
||||
|
||||
@@ -9,7 +9,8 @@ license = "GPL-3.0"
|
||||
wasmi = { version = "0.6.2", optional = true }
|
||||
impl-trait-for-tuples = "0.1.2"
|
||||
sp-std = { version = "2.0.0", path = "../std", default-features = false }
|
||||
codec = { package = "parity-scale-codec", version = "1.1.2", default-features = false, features = ["derive"] }
|
||||
|
||||
[features]
|
||||
default = [ "std" ]
|
||||
std = [ "wasmi", "sp-std/std" ]
|
||||
std = [ "wasmi", "sp-std/std", "codec/std" ]
|
||||
|
||||
@@ -44,8 +44,33 @@ pub enum ValueType {
|
||||
F64,
|
||||
}
|
||||
|
||||
impl From<ValueType> for u8 {
|
||||
fn from(val: ValueType) -> u8 {
|
||||
match val {
|
||||
ValueType::I32 => 0,
|
||||
ValueType::I64 => 1,
|
||||
ValueType::F32 => 2,
|
||||
ValueType::F64 => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sp_std::convert::TryFrom<u8> for ValueType {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(val: u8) -> sp_std::result::Result<ValueType, ()> {
|
||||
match val {
|
||||
0 => Ok(Self::I32),
|
||||
1 => Ok(Self::I64),
|
||||
2 => Ok(Self::F32),
|
||||
3 => Ok(Self::F64),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Values supported by Substrate on the boundary between host/Wasm.
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)]
|
||||
pub enum Value {
|
||||
/// A 32-bit integer.
|
||||
I32(i32),
|
||||
@@ -71,6 +96,14 @@ impl Value {
|
||||
Value::F64(_) => ValueType::F64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `Self` as `i32`.
|
||||
pub fn as_i32(&self) -> Option<i32> {
|
||||
match self {
|
||||
Self::I32(val) => Some(*val),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides `Sealed` trait to prevent implementing trait `PointerType` outside of this crate.
|
||||
@@ -298,6 +331,12 @@ pub trait Sandbox {
|
||||
raw_env_def: &[u8],
|
||||
state: u32,
|
||||
) -> Result<u32>;
|
||||
|
||||
/// Get the value from a global with the given `name`. The sandbox is determined by the
|
||||
/// given `instance_idx` instance.
|
||||
///
|
||||
/// Returns `Some(_)` when the requested global variable could be found.
|
||||
fn get_global_val(&self, instance_idx: u32, name: &str) -> Result<Option<Value>>;
|
||||
}
|
||||
|
||||
/// Something that provides implementations for host functions.
|
||||
@@ -409,9 +448,37 @@ impl ReadPrimitive<u64> for &mut dyn FunctionContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed value that can be returned from a function.
|
||||
///
|
||||
/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
|
||||
#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
|
||||
pub enum ReturnValue {
|
||||
/// For returning nothing.
|
||||
Unit,
|
||||
/// For returning some concrete value.
|
||||
Value(Value),
|
||||
}
|
||||
|
||||
impl From<Value> for ReturnValue {
|
||||
fn from(v: Value) -> ReturnValue {
|
||||
ReturnValue::Value(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnValue {
|
||||
/// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`.
|
||||
///
|
||||
/// Breakdown:
|
||||
/// 1 byte for encoding unit/value variant
|
||||
/// 1 byte for encoding value type
|
||||
/// 8 bytes for encoding the biggest value types available in wasm: f64, i64.
|
||||
pub const ENCODED_MAX_SIZE: usize = 10;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::Encode;
|
||||
|
||||
#[test]
|
||||
fn pointer_offset_works() {
|
||||
@@ -425,4 +492,11 @@ mod tests {
|
||||
assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
|
||||
assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn return_value_encoded_max_size() {
|
||||
let encoded = ReturnValue::Value(Value::I64(-1)).encode();
|
||||
assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user