mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-09 15:18:00 +00:00
Substrate runtime interface 2.0 (#4057)
* Adds first version of traits for generating the host functions * First steps of the procedural macro * Implements generation of the host extern functions * Prefix ext host function with snake case trait name * Implement host functions implementation on the host * Change `HostFunctions` interface * Implement `HostFunctions` for tuples * Make `WasmExecutor` generic over the host functions * Begin to add a test and make it compile * Make the test succeed * Add test to ensure that host functions are not found * It's alive! Make the `set_storage` test work * Add test for mutable references * Code cleanup and documentation etc * Add marker trait for types that should be passed as SCALE encoded * Inherit the visibility from the trait and more improvements * More impls and move them into their own file * Code simplification by dropping one trait * Give it a better name * Implement traits for arrays * Refactor code to support pass by codec/inner * Docs * Implement pass by inner for some crypto types and add a test * Implement exchangeable function support * Rewrite sr-io with as runtime interface * Start reworking after master merge * Adds `PassByCodec` derive * Adds `PassByInner` derive * Fix compilation errors * More implementations * Implement runtime interface traits for `str` * Make `sr-io` compile again * Fix more compilation errors * More progress on getting stuff back to compile * More compilation fixes * Fix warnings * Remove le conversions * Add support for `wasm_only` interfaces * Implement `Allocator` interface * Improve error message * Move `WasmAllocator` to `sr-io` and more clean ups * Use correct function signature for wasm functions * Store the host functions with the Wasm runtime * Docs update * Fix compilation after master merge * Remove `sr-io/without_std` * Make `srml-support` tests run again * More compilation error fixes * Use correct doc syntax * Fix test-runtime * Fix compilation * Catch native panics when executing the wasm runtime As with the native runtime, we now catch all native panics when we execute the wasm runtime. The panics inside the wasm runtime were already catched before by the wasm executor automatically, but any panic in the host functions could bring down the node. The recent switch to execute the native counterpart of the host function in `sr-io`, makes this change required. The native `sr-io` functions just `panic` when something is not provided or any other error occured. * Fix compilation * Don't panic in a panic * Move `sr-sandbox` to new runtime interface * Fixes tests after sandbox changes * Make sure we detect invalid utf8 * Fixes after master merge * Adds pass by enum strategy * Fix wasmtime integration * Some macro structure clean up * Rework and test exchangebale host functions * PassBy derive macros documentation * Docs for `runtime_interface` macro * Support wild card argument names * Adds ui tests * Make sure that we are backwards compatible to the old runtime interfaces * Documentation * Fixes after latest master merge * Make `wasmtime` happy * Make `full_crypto` work * Make the new interface versionable * Rename `Sanboxing` to `Sandbox` * Don't finalize in test while importing * Fix Performance regression * Fix test
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Traits required by the runtime interface from the host side.
|
||||
|
||||
use crate::RIType;
|
||||
|
||||
use wasm_interface::{FunctionContext, Result};
|
||||
|
||||
/// Something that can be converted into a ffi value.
|
||||
pub trait IntoFFIValue: RIType {
|
||||
/// Convert `self` into a ffi value.
|
||||
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<Self::FFIType>;
|
||||
}
|
||||
|
||||
/// Something that can be converted into a preallocated ffi value.
|
||||
///
|
||||
/// Every type parameter that should be given as `&mut` into a runtime interface function, needs
|
||||
/// to implement this trait. After executing the host implementation of the runtime interface
|
||||
/// function, the value is copied into the preallocated wasm memory.
|
||||
///
|
||||
/// This should only be used for types which have a fixed size, like slices. Other types like a vec
|
||||
/// do not work with this interface, as we can not call into wasm to reallocate memory. So, this
|
||||
/// trait should be implemented carefully.
|
||||
pub trait IntoPreallocatedFFIValue: RIType {
|
||||
/// As `Self` can be an unsized type, it needs to be represented by a sized type at the host.
|
||||
/// This `SelfInstance` is the sized type.
|
||||
type SelfInstance;
|
||||
|
||||
/// Convert `self_instance` into the given preallocated ffi value.
|
||||
fn into_preallocated_ffi_value(
|
||||
self_instance: Self::SelfInstance,
|
||||
context: &mut dyn FunctionContext,
|
||||
allocated: Self::FFIType,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
/// Something that can be created from a ffi value.
|
||||
pub trait FromFFIValue: RIType {
|
||||
/// As `Self` can be an unsized type, it needs to be represented by a sized type at the host.
|
||||
/// This `SelfInstance` is the sized type.
|
||||
type SelfInstance;
|
||||
|
||||
/// Create `SelfInstance` from the given
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<Self::SelfInstance>;
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Provides implementations for the runtime interface traits.
|
||||
|
||||
use crate::{RIType, Pointer, pass_by::{PassBy, Codec, Inner, PassByInner}};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::host::*;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use crate::wasm::*;
|
||||
|
||||
#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))]
|
||||
use static_assertions::assert_eq_size;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use wasm_interface::{FunctionContext, Result};
|
||||
|
||||
use codec::{Encode, Decode};
|
||||
|
||||
use rstd::{any::TypeId, mem, vec::Vec};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use rstd::borrow::Cow;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::{slice, boxed::Box};
|
||||
|
||||
// Make sure that our assumptions for storing a pointer + its size in `u64` is valid.
|
||||
#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))]
|
||||
assert_eq_size!(usize, u32);
|
||||
#[cfg(all(not(feature = "std"), not(feature = "disable_target_static_assertions")))]
|
||||
assert_eq_size!(*const u8, u32);
|
||||
|
||||
/// Converts a pointer and length into an `u64`.
|
||||
pub fn pointer_and_len_to_u64(ptr: u32, len: u32) -> u64 {
|
||||
// The static assertions from above are changed into a runtime check.
|
||||
#[cfg(all(feature = "std", not(feature = "disable_target_static_assertions")))]
|
||||
assert_eq!(4, rstd::mem::size_of::<usize>());
|
||||
|
||||
(u64::from(len) << 32) | u64::from(ptr)
|
||||
}
|
||||
|
||||
/// Splits an `u64` into the pointer and length.
|
||||
pub fn pointer_and_len_from_u64(val: u64) -> (u32, u32) {
|
||||
// The static assertions from above are changed into a runtime check.
|
||||
#[cfg(all(feature = "std", not(feature = "disable_target_static_assertions")))]
|
||||
assert_eq!(4, rstd::mem::size_of::<usize>());
|
||||
|
||||
let ptr = (val & (!0u32 as u64)) as u32;
|
||||
let len = (val >> 32) as u32;
|
||||
|
||||
(ptr, len)
|
||||
}
|
||||
|
||||
/// Implement the traits for the given primitive traits.
|
||||
macro_rules! impl_traits_for_primitives {
|
||||
(
|
||||
$(
|
||||
$rty:ty, $fty:ty,
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
/// The type is passed directly.
|
||||
impl RIType for $rty {
|
||||
type FFIType = $fty;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl IntoFFIValue for $rty {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<$fty> {
|
||||
(*self as $fty).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl FromFFIValue for $rty {
|
||||
fn from_ffi_value(arg: $fty) -> $rty {
|
||||
arg as $rty
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl FromFFIValue for $rty {
|
||||
type SelfInstance = $rty;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: $fty) -> Result<$rty> {
|
||||
Ok(arg as $rty)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoFFIValue for $rty {
|
||||
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<$fty> {
|
||||
Ok(self as $fty)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_traits_for_primitives! {
|
||||
u8, u8,
|
||||
u16, u16,
|
||||
u32, u32,
|
||||
u64, u64,
|
||||
i8, i8,
|
||||
i16, i16,
|
||||
i32, i32,
|
||||
i64, i64,
|
||||
}
|
||||
|
||||
/// `bool` is passed as `u8`.
|
||||
///
|
||||
/// - `1`: true
|
||||
/// - `0`: false
|
||||
impl RIType for bool {
|
||||
type FFIType = u8;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl IntoFFIValue for bool {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u8> {
|
||||
if *self { 1 } else { 0 }.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl FromFFIValue for bool {
|
||||
fn from_ffi_value(arg: u8) -> bool {
|
||||
arg == 1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl FromFFIValue for bool {
|
||||
type SelfInstance = bool;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u8) -> Result<bool> {
|
||||
Ok(arg == 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoFFIValue for bool {
|
||||
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<u8> {
|
||||
Ok(if self { 1 } else { 0 })
|
||||
}
|
||||
}
|
||||
|
||||
/// The type is passed as `u64`.
|
||||
///
|
||||
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
|
||||
///
|
||||
/// If `T == u8` the length and the pointer are taken directly from the `Self`.
|
||||
/// Otherwise `Self` is encoded and the length and the pointer are taken from the encoded vector.
|
||||
impl<T> RIType for Vec<T> {
|
||||
type FFIType = u64;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: 'static + Encode> IntoFFIValue for Vec<T> {
|
||||
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<u64> {
|
||||
let vec: Cow<'_, [u8]> = if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
unsafe { Cow::Borrowed(mem::transmute(&self[..])) }
|
||||
} else {
|
||||
Cow::Owned(self.encode())
|
||||
};
|
||||
|
||||
let ptr = context.allocate_memory(vec.as_ref().len() as u32)?;
|
||||
context.write_memory(ptr, &vec)?;
|
||||
|
||||
Ok(pointer_and_len_to_u64(ptr.into(), vec.len() as u32))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: 'static + Decode> FromFFIValue for Vec<T> {
|
||||
type SelfInstance = Vec<T>;
|
||||
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<Vec<T>> {
|
||||
<[T] as FromFFIValue>::from_ffi_value(context, arg)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: 'static + Encode> IntoFFIValue for Vec<T> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u64, Vec<u8>> {
|
||||
self[..].into_ffi_value()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: 'static + Decode> FromFFIValue for Vec<T> {
|
||||
fn from_ffi_value(arg: u64) -> Vec<T> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
let len = len as usize;
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
unsafe { mem::transmute(Vec::from_raw_parts(ptr as *mut u8, len, len)) }
|
||||
} else {
|
||||
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) };
|
||||
Self::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The type is passed as `u64`.
|
||||
///
|
||||
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
|
||||
///
|
||||
/// If `T == u8` the length and the pointer are taken directly from the `Self`.
|
||||
/// Otherwise `Self` is encoded and the length and the pointer are taken from the encoded vector.
|
||||
impl<T> RIType for [T] {
|
||||
type FFIType = u64;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: 'static + Decode> FromFFIValue for [T] {
|
||||
type SelfInstance = Vec<T>;
|
||||
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<Vec<T>> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
Ok(unsafe { mem::transmute(vec) })
|
||||
} else {
|
||||
Ok(Vec::<T>::decode(&mut &vec[..]).expect("Wasm to host values are encoded correctly; qed"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoPreallocatedFFIValue for [u8] {
|
||||
type SelfInstance = Vec<u8>;
|
||||
|
||||
fn into_preallocated_ffi_value(
|
||||
self_instance: Self::SelfInstance,
|
||||
context: &mut dyn FunctionContext,
|
||||
allocated: u64,
|
||||
) -> Result<()> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(allocated);
|
||||
|
||||
if (len as usize) < self_instance.len() {
|
||||
Err(
|
||||
format!(
|
||||
"Preallocated buffer is not big enough (given {} vs needed {})!",
|
||||
len,
|
||||
self_instance.len()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
context.write_memory(Pointer::new(ptr), &self_instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: 'static + Encode> IntoFFIValue for [T] {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u64, Vec<u8>> {
|
||||
if TypeId::of::<T>() == TypeId::of::<u8>() {
|
||||
let slice = unsafe { mem::transmute::<&[T], &[u8]>(self) };
|
||||
pointer_and_len_to_u64(slice.as_ptr() as u32, slice.len() as u32).into()
|
||||
} else {
|
||||
let data = self.encode();
|
||||
let ffi_value = pointer_and_len_to_u64(data.as_ptr() as u32, data.len() as u32);
|
||||
(ffi_value, data).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implement the traits for the `[u8; N]` arrays, where `N` is the input to this macro.
|
||||
macro_rules! impl_traits_for_arrays {
|
||||
(
|
||||
$(
|
||||
$n:expr
|
||||
),*
|
||||
$(,)?
|
||||
) => {
|
||||
$(
|
||||
/// The type is passed as `u32`.
|
||||
///
|
||||
/// The `u32` is the pointer to the array.
|
||||
impl RIType for [u8; $n] {
|
||||
type FFIType = u32;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl IntoFFIValue for [u8; $n] {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u32> {
|
||||
(self.as_ptr() as u32).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl FromFFIValue for [u8; $n] {
|
||||
fn from_ffi_value(arg: u32) -> [u8; $n] {
|
||||
let mut res = unsafe { mem::MaybeUninit::<[u8; $n]>::zeroed().assume_init() };
|
||||
res.copy_from_slice(unsafe { slice::from_raw_parts(arg as *const u8, $n) });
|
||||
|
||||
// Make sure we free the pointer.
|
||||
let _ = unsafe { Box::from_raw(arg as *mut u8) };
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl FromFFIValue for [u8; $n] {
|
||||
type SelfInstance = [u8; $n];
|
||||
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u32) -> Result<[u8; $n]> {
|
||||
let data = context.read_memory(Pointer::new(arg), $n)?;
|
||||
let mut res = unsafe { mem::MaybeUninit::<[u8; $n]>::zeroed().assume_init() };
|
||||
res.copy_from_slice(&data);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoFFIValue for [u8; $n] {
|
||||
fn into_ffi_value(self, context: &mut dyn FunctionContext) -> Result<u32> {
|
||||
let addr = context.allocate_memory($n)?;
|
||||
context.write_memory(addr, &self)?;
|
||||
Ok(addr.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl IntoPreallocatedFFIValue for [u8; $n] {
|
||||
type SelfInstance = [u8; $n];
|
||||
|
||||
fn into_preallocated_ffi_value(
|
||||
self_instance: Self::SelfInstance,
|
||||
context: &mut dyn FunctionContext,
|
||||
allocated: u32,
|
||||
) -> Result<()> {
|
||||
context.write_memory(Pointer::new(allocated), &self_instance)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_traits_for_arrays! {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
|
||||
75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
|
||||
}
|
||||
|
||||
impl<T: codec::Codec, E: codec::Codec> PassBy for rstd::result::Result<T, E> {
|
||||
type PassBy = Codec<Self>;
|
||||
}
|
||||
|
||||
impl<T: codec::Codec> PassBy for Option<T> {
|
||||
type PassBy = Codec<Self>;
|
||||
}
|
||||
|
||||
/// Implement `PassBy` with `Inner` for the given fixed sized hash types.
|
||||
macro_rules! for_primitive_types {
|
||||
{ $( $hash:ident $n:expr ),* $(,)? } => {
|
||||
$(
|
||||
impl PassBy for primitive_types::$hash {
|
||||
type PassBy = Inner<Self, [u8; $n]>;
|
||||
}
|
||||
|
||||
impl PassByInner for primitive_types::$hash {
|
||||
type Inner = [u8; $n];
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.0
|
||||
}
|
||||
|
||||
fn into_inner(self) -> Self::Inner {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn from_inner(inner: Self::Inner) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
for_primitive_types! {
|
||||
H160 20,
|
||||
H256 32,
|
||||
H512 64,
|
||||
}
|
||||
|
||||
/// The type is passed as `u64`.
|
||||
///
|
||||
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
|
||||
///
|
||||
/// The length and the pointer are taken directly from the `Self`.
|
||||
impl RIType for str {
|
||||
type FFIType = u64;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl FromFFIValue for str {
|
||||
type SelfInstance = String;
|
||||
|
||||
fn from_ffi_value(context: &mut dyn FunctionContext, arg: u64) -> Result<String> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
|
||||
// The data is valid utf8, as it is stored as `&str` in wasm.
|
||||
String::from_utf8(vec).map_err(|_| "Invalid utf8 data provided".into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl IntoFFIValue for str {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u64, ()> {
|
||||
let bytes = self.as_bytes();
|
||||
pointer_and_len_to_u64(bytes.as_ptr() as u32, bytes.len() as u32).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: wasm_interface::PointerType> RIType for Pointer<T> {
|
||||
type FFIType = u32;
|
||||
}
|
||||
|
||||
/// The type is passed as `u32`.
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> RIType for Pointer<T> {
|
||||
type FFIType = u32;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> IntoFFIValue for Pointer<T> {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<u32> {
|
||||
(*self as u32).into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T> FromFFIValue for Pointer<T> {
|
||||
fn from_ffi_value(arg: u32) -> Self {
|
||||
arg as _
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: wasm_interface::PointerType> FromFFIValue for Pointer<T> {
|
||||
type SelfInstance = Self;
|
||||
|
||||
fn from_ffi_value(_: &mut dyn FunctionContext, arg: u32) -> Result<Self> {
|
||||
Ok(Pointer::new(arg))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: wasm_interface::PointerType> IntoFFIValue for Pointer<T> {
|
||||
fn into_ffi_value(self, _: &mut dyn FunctionContext) -> Result<u32> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate runtime interface
|
||||
//!
|
||||
//! This crate provides types, traits and macros around runtime interfaces. A runtime interface is
|
||||
//! a fixed interface between a Substrate runtime and a Substrate node. For a native runtime the
|
||||
//! interface maps to a direct function call of the implementation. For a wasm runtime the interface
|
||||
//! maps to an external function call. These external functions are exported by the wasm executor
|
||||
//! and they map to the same implementation as the native calls.
|
||||
//!
|
||||
//! # Using a type in a runtime interface
|
||||
//!
|
||||
//! Any type that should be used in a runtime interface as argument or return value needs to
|
||||
//! implement [`RIType`]. The associated type `FFIType` is the type that is used in the FFI
|
||||
//! function to represent the actual type. For example `[T]` is represented by an `u64`. The slice
|
||||
//! pointer and the length will be mapped to an `u64` value. For more information, see the
|
||||
//! implementation of [`RIType`] for [`T`]. The FFI function definition is used when calling from
|
||||
//! the wasm runtime into the node.
|
||||
//!
|
||||
//! Traits are used to convert from a type to the corresponding [`RIType::FFIType`].
|
||||
//! Depending on where and how a type should be used in a function signature, a combination of the
|
||||
//! following traits need to be implemented:
|
||||
//!
|
||||
//! 1. Pass as function argument: [`wasm::IntoFFIValue`] and [`host::FromFFIValue`]
|
||||
//! 2. As function return value: [`wasm::FromFFIValue`] and [`host::IntoFFIValue`]
|
||||
//! 3. Pass as mutable function argument: [`host::IntoPreallocatedFFIValue`]
|
||||
//!
|
||||
//! The traits are implemented for most of the common types like `[T]`, `Vec<T>`, arrays and
|
||||
//! primitive types.
|
||||
//!
|
||||
//! For custom types, we provide the [`PassBy`](pass_by::PassBy) trait and strategies that define
|
||||
//! how a type is passed between the wasm runtime and the node. Each strategy also provides a derive
|
||||
//! macro to simplify the implementation.
|
||||
//!
|
||||
//! # Performance
|
||||
//!
|
||||
//! To not waste any more performance when calling into the node, not all types are SCALE encoded
|
||||
//! when being passed as arguments between the wasm runtime and the node. For most types that
|
||||
//! are raw bytes like `Vec<u8>`, `[u8]` or `[u8; N]` we pass them directly, without SCALE encoding
|
||||
//! them in front of. The implementation of [`RIType`] each type provides more information on how
|
||||
//! the data is passed.
|
||||
//!
|
||||
//! # Declaring a runtime interface
|
||||
//!
|
||||
//! Declaring a runtime interface is similar to declaring a trait in Rust:
|
||||
//!
|
||||
//! ```
|
||||
//! #[substrate_runtime_interface::runtime_interface]
|
||||
//! trait RuntimeInterface {
|
||||
//! fn some_function(value: &[u8]) -> bool {
|
||||
//! value.iter().all(|v| *v > 125)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! For more information on declaring a runtime interface, see
|
||||
//! [`#[runtime_interface]`](attr.runtime_interface.html).
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "std")]
|
||||
pub use wasm_interface;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use rstd;
|
||||
|
||||
pub use substrate_runtime_interface_proc_macro::runtime_interface;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "std")]
|
||||
pub use externalities::{
|
||||
set_and_run_with_externalities, with_externalities, Externalities, ExternalitiesExt, ExtensionStore,
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use codec;
|
||||
|
||||
pub(crate) mod impls;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod host;
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub mod wasm;
|
||||
pub mod pass_by;
|
||||
|
||||
/// Something that can be used by the runtime interface as type to communicate between wasm and the
|
||||
/// host.
|
||||
///
|
||||
/// Every type that should be used in a runtime interface function signature needs to implement
|
||||
/// this trait.
|
||||
pub trait RIType {
|
||||
/// The ffi type that is used to represent `Self`.
|
||||
#[cfg(feature = "std")]
|
||||
type FFIType: wasm_interface::IntoValue + wasm_interface::TryFromValue;
|
||||
#[cfg(not(feature = "std"))]
|
||||
type FFIType;
|
||||
}
|
||||
|
||||
/// A pointer that can be used in a runtime interface function signature.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub type Pointer<T> = *mut T;
|
||||
|
||||
/// A pointer that can be used in a runtime interface function signature.
|
||||
#[cfg(feature = "std")]
|
||||
pub type Pointer<T> = wasm_interface::Pointer<T>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_wasm::{WASM_BINARY, test_api::HostFunctions};
|
||||
use wasm_interface::HostFunctions as HostFunctionsT;
|
||||
|
||||
type TestExternalities = state_machine::TestExternalities<primitives::Blake2Hasher, u64>;
|
||||
|
||||
fn call_wasm_method<HF: HostFunctionsT>(method: &str) -> TestExternalities {
|
||||
let mut ext = TestExternalities::default();
|
||||
let mut ext_ext = ext.ext();
|
||||
|
||||
executor::call_in_wasm::<
|
||||
_,
|
||||
(
|
||||
HF,
|
||||
runtime_io::SubstrateHostFunctions,
|
||||
executor::deprecated_host_interface::SubstrateExternals
|
||||
)
|
||||
>(
|
||||
method,
|
||||
&[],
|
||||
executor::WasmExecutionMethod::Interpreted,
|
||||
&mut ext_ext,
|
||||
&WASM_BINARY[..],
|
||||
8,
|
||||
).expect(&format!("Executes `{}`", method));
|
||||
|
||||
ext
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_return_data() {
|
||||
call_wasm_method::<HostFunctions>("test_return_data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_return_option_data() {
|
||||
call_wasm_method::<HostFunctions>("test_return_option_data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_storage() {
|
||||
let mut ext = call_wasm_method::<HostFunctions>("test_set_storage");
|
||||
|
||||
let expected = "world";
|
||||
assert_eq!(expected.as_bytes(), &ext.ext().storage("hello".as_bytes()).unwrap()[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_return_value_into_mutable_reference() {
|
||||
call_wasm_method::<HostFunctions>("test_return_value_into_mutable_reference");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_and_return_array() {
|
||||
call_wasm_method::<HostFunctions>("test_get_and_return_array");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_array_as_mutable_reference() {
|
||||
call_wasm_method::<HostFunctions>("test_array_as_mutable_reference");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_return_input_public_key() {
|
||||
call_wasm_method::<HostFunctions>("test_return_input_public_key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected = "Other(\"Instantiation: Export ext_test_api_return_input_version_1 not found\")"
|
||||
)]
|
||||
fn host_function_not_found() {
|
||||
call_wasm_method::<()>("test_return_data");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(
|
||||
expected =
|
||||
"FunctionExecution(\"ext_test_api_invalid_utf8_data_version_1\", \
|
||||
\"Invalid utf8 data provided\")"
|
||||
)]
|
||||
fn test_invalid_utf8_data_should_return_an_error() {
|
||||
call_wasm_method::<HostFunctions>("test_invalid_utf8_data_should_return_an_error");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overwrite_native_function_implementation() {
|
||||
call_wasm_method::<HostFunctions>("test_overwrite_native_function_implementation");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Provides the [`PassBy`](pass_by::PassBy) trait to simplify the implementation of the
|
||||
//! runtime interface traits for custom types.
|
||||
//!
|
||||
//! [`Codec`](pass_by::Codec), [`Inner`](pass_by::Inner) and [`Enum`](pass_by::Enum) are the
|
||||
//! provided strategy implementations.
|
||||
|
||||
use crate::{RIType, impls::{pointer_and_len_from_u64, pointer_and_len_to_u64}};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::host::*;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use crate::wasm::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use wasm_interface::{FunctionContext, Pointer, Result};
|
||||
|
||||
use rstd::{marker::PhantomData, convert::TryFrom};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use rstd::{slice, vec::Vec};
|
||||
|
||||
pub use substrate_runtime_interface_proc_macro::{PassByCodec, PassByInner, PassByEnum};
|
||||
|
||||
/// Something that should be passed between wasm and the host using the given strategy.
|
||||
///
|
||||
/// See [`Codec`], [`Inner`] or [`Enum`] for more information about the provided strategies.
|
||||
pub trait PassBy: Sized {
|
||||
/// The strategy that should be used to pass the type.
|
||||
type PassBy: PassByImpl<Self>;
|
||||
}
|
||||
|
||||
/// Something that provides a strategy for passing a type between wasm and the host.
|
||||
///
|
||||
/// This trait exposes the same functionality as [`crate::host::IntoFFIValue`] and
|
||||
/// [`crate::host::FromFFIValue`] to delegate the implementation for a type to a different type.
|
||||
///
|
||||
/// This trait is used for the host implementation.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait PassByImpl<T>: RIType {
|
||||
/// Convert the given instance to the ffi value.
|
||||
///
|
||||
/// For more information see: [`crate::host::IntoFFIValue::into_ffi_value`]
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType>;
|
||||
|
||||
/// Create `T` from the given ffi value.
|
||||
///
|
||||
/// For more information see: [`crate::host::FromFFIValue::from_ffi_value`]
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<T>;
|
||||
}
|
||||
|
||||
/// Something that provides a strategy for passing a type between wasm and the host.
|
||||
///
|
||||
/// This trait exposes the same functionality as [`crate::wasm::IntoFFIValue`] and
|
||||
/// [`crate::wasm::FromFFIValue`] to delegate the implementation for a type to a different type.
|
||||
///
|
||||
/// This trait is used for the wasm implementation.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub trait PassByImpl<T>: RIType {
|
||||
/// The owned rust type that is stored with the ffi value in [`crate::wasm::WrappedFFIValue`].
|
||||
type Owned;
|
||||
|
||||
/// Convert the given `instance` into [`crate::wasm::WrappedFFIValue`].
|
||||
///
|
||||
/// For more information see: [`crate::wasm::IntoFFIValue::into_ffi_value`]
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned>;
|
||||
|
||||
/// Create `T` from the given ffi value.
|
||||
///
|
||||
/// For more information see: [`crate::wasm::FromFFIValue::from_ffi_value`]
|
||||
fn from_ffi_value(arg: Self::FFIType) -> T;
|
||||
}
|
||||
|
||||
impl<T: PassBy> RIType for T {
|
||||
type FFIType = <T::PassBy as RIType>::FFIType;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: PassBy> IntoFFIValue for T {
|
||||
fn into_ffi_value(
|
||||
self,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<<T::PassBy as RIType>::FFIType> {
|
||||
T::PassBy::into_ffi_value(self, context)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: PassBy> FromFFIValue for T {
|
||||
type SelfInstance = Self;
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: <T::PassBy as RIType>::FFIType,
|
||||
) -> Result<Self> {
|
||||
T::PassBy::from_ffi_value(context, arg)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: PassBy> IntoFFIValue for T {
|
||||
type Owned = <T::PassBy as PassByImpl<T>>::Owned;
|
||||
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<<T::PassBy as RIType>::FFIType, Self::Owned> {
|
||||
T::PassBy::into_ffi_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: PassBy> FromFFIValue for T {
|
||||
fn from_ffi_value(arg: <T::PassBy as RIType>::FFIType) -> Self {
|
||||
T::PassBy::from_ffi_value(arg)
|
||||
}
|
||||
}
|
||||
|
||||
/// The implementation of the pass by codec strategy. This strategy uses a SCALE encoded
|
||||
/// representation of the type between wasm and the host.
|
||||
///
|
||||
/// Use this type as associated type for [`PassBy`] to implement this strategy for a type.
|
||||
///
|
||||
/// This type expects the type that wants to implement this strategy as generic parameter.
|
||||
///
|
||||
/// [`PassByCodec`](derive.PassByCodec.html) is a derive macro to implement this strategy.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use substrate_runtime_interface::pass_by::{PassBy, Codec};
|
||||
/// #[derive(codec::Encode, codec::Decode)]
|
||||
/// struct Test;
|
||||
///
|
||||
/// impl PassBy for Test {
|
||||
/// type PassBy = Codec<Self>;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Codec<T: codec::Codec>(PhantomData<T>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: codec::Codec> PassByImpl<T> for Codec<T> {
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
let vec = instance.encode();
|
||||
let ptr = context.allocate_memory(vec.len() as u32)?;
|
||||
context.write_memory(ptr, &vec)?;
|
||||
|
||||
Ok(pointer_and_len_to_u64(ptr.into(), vec.len() as u32))
|
||||
}
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<T> {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
let vec = context.read_memory(Pointer::new(ptr), len)?;
|
||||
T::decode(&mut &vec[..])
|
||||
.map_err(|e| format!("Could not decode value from wasm: {}", e.what()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: codec::Codec> PassByImpl<T> for Codec<T> {
|
||||
type Owned = Vec<u8>;
|
||||
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
|
||||
let data = instance.encode();
|
||||
let ffi_value = pointer_and_len_to_u64(data.as_ptr() as u32, data.len() as u32);
|
||||
(ffi_value, data).into()
|
||||
}
|
||||
|
||||
fn from_ffi_value(arg: Self::FFIType) -> T {
|
||||
let (ptr, len) = pointer_and_len_from_u64(arg);
|
||||
let len = len as usize;
|
||||
|
||||
let slice = unsafe { slice::from_raw_parts(ptr as *const u8, len) };
|
||||
T::decode(&mut &slice[..]).expect("Host to wasm values are encoded correctly; qed")
|
||||
}
|
||||
}
|
||||
|
||||
/// The type is passed as `u64`.
|
||||
///
|
||||
/// The `u64` value is build by `length 32bit << 32 | pointer 32bit`
|
||||
///
|
||||
/// `Self` is encoded and the length and the pointer are taken from the encoded vector.
|
||||
impl<T: codec::Codec> RIType for Codec<T> {
|
||||
type FFIType = u64;
|
||||
}
|
||||
|
||||
/// Trait that needs to be implemented by a type that should be passed between wasm and the host,
|
||||
/// by using the inner type. See [`Inner`] for more information.
|
||||
pub trait PassByInner: Sized {
|
||||
/// The inner type that is wrapped by `Self`.
|
||||
type Inner: RIType;
|
||||
|
||||
/// Consumes `self` and returns the inner type.
|
||||
fn into_inner(self) -> Self::Inner;
|
||||
|
||||
/// Returns the reference to the inner type.
|
||||
fn inner(&self) -> &Self::Inner;
|
||||
|
||||
/// Construct `Self` from the given `inner`.
|
||||
fn from_inner(inner: Self::Inner) -> Self;
|
||||
}
|
||||
|
||||
/// The implementation of the pass by inner type strategy. The type that uses this strategy will be
|
||||
/// passed between wasm and the host by using the wrapped inner type. So, this strategy is only
|
||||
/// usable by newtype structs.
|
||||
///
|
||||
/// Use this type as associated type for [`PassBy`] to implement this strategy for a type. Besides
|
||||
/// that the `PassByInner` trait need to be implemented as well.
|
||||
///
|
||||
/// This type expects the type that wants to use this strategy as generic parameter `T` and the
|
||||
/// inner type as generic parameter `I`.
|
||||
///
|
||||
/// [`PassByInner`](derive.PassByInner.html) is a derive macro to implement this strategy.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use substrate_runtime_interface::pass_by::{PassBy, Inner, PassByInner};
|
||||
/// struct Test([u8; 32]);
|
||||
///
|
||||
/// impl PassBy for Test {
|
||||
/// type PassBy = Inner<Self, [u8; 32]>;
|
||||
/// }
|
||||
///
|
||||
/// impl PassByInner for Test {
|
||||
/// type Inner = [u8; 32];
|
||||
///
|
||||
/// fn into_inner(self) -> [u8; 32] {
|
||||
/// self.0
|
||||
/// }
|
||||
/// fn inner(&self) -> &[u8; 32] {
|
||||
/// &self.0
|
||||
/// }
|
||||
/// fn from_inner(inner: [u8; 32]) -> Self {
|
||||
/// Self(inner)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Inner<T: PassByInner<Inner = I>, I: RIType>(PhantomData<(T, I)>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: PassByInner<Inner = I>, I: RIType> PassByImpl<T> for Inner<T, I>
|
||||
where I: IntoFFIValue + FromFFIValue<SelfInstance=I>
|
||||
{
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
context: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
instance.into_inner().into_ffi_value(context)
|
||||
}
|
||||
|
||||
fn from_ffi_value(
|
||||
context: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<T> {
|
||||
I::from_ffi_value(context, arg).map(T::from_inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: PassByInner<Inner = I>, I: RIType> PassByImpl<T> for Inner<T, I>
|
||||
where I: IntoFFIValue + FromFFIValue
|
||||
{
|
||||
type Owned = I::Owned;
|
||||
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
|
||||
instance.inner().into_ffi_value()
|
||||
}
|
||||
|
||||
fn from_ffi_value(arg: Self::FFIType) -> T {
|
||||
T::from_inner(I::from_ffi_value(arg))
|
||||
}
|
||||
}
|
||||
|
||||
/// The type is passed as the inner type.
|
||||
impl<T: PassByInner<Inner = I>, I: RIType> RIType for Inner<T, I> {
|
||||
type FFIType = I::FFIType;
|
||||
}
|
||||
|
||||
/// The implementation of the pass by enum strategy. This strategy uses an `u8` internally to pass
|
||||
/// the enum between wasm and the host. So, this strategy only supports enums with unit variants.
|
||||
///
|
||||
/// Use this type as associated type for [`PassBy`] to implement this strategy for a type.
|
||||
///
|
||||
/// This type expects the type that wants to implement this strategy as generic parameter. Besides
|
||||
/// that the type needs to implement `TryFrom<u8>` and `From<Self> for u8`.
|
||||
///
|
||||
/// [`PassByEnum`](derive.PassByEnum.html) is a derive macro to implement this strategy.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use substrate_runtime_interface::pass_by::{PassBy, Enum};
|
||||
/// #[derive(Clone, Copy)]
|
||||
/// enum Test {
|
||||
/// Test1,
|
||||
/// Test2,
|
||||
/// }
|
||||
///
|
||||
/// impl From<Test> for u8 {
|
||||
/// fn from(val: Test) -> u8 {
|
||||
/// match val {
|
||||
/// Test::Test1 => 0,
|
||||
/// Test::Test2 => 1,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl std::convert::TryFrom<u8> for Test {
|
||||
/// type Error = ();
|
||||
///
|
||||
/// fn try_from(val: u8) -> Result<Test, ()> {
|
||||
/// match val {
|
||||
/// 0 => Ok(Test::Test1),
|
||||
/// 1 => Ok(Test::Test2),
|
||||
/// _ => Err(()),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl PassBy for Test {
|
||||
/// type PassBy = Enum<Self>;
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Enum<T: Copy + Into<u8> + TryFrom<u8>>(PhantomData<T>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Copy + Into<u8> + TryFrom<u8>> PassByImpl<T> for Enum<T> {
|
||||
fn into_ffi_value(
|
||||
instance: T,
|
||||
_: &mut dyn FunctionContext,
|
||||
) -> Result<Self::FFIType> {
|
||||
Ok(instance.into())
|
||||
}
|
||||
|
||||
fn from_ffi_value(
|
||||
_: &mut dyn FunctionContext,
|
||||
arg: Self::FFIType,
|
||||
) -> Result<T> {
|
||||
T::try_from(arg).map_err(|_| format!("Invalid enum discriminant: {}", arg))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<T: Copy + Into<u8> + TryFrom<u8, Error = ()>> PassByImpl<T> for Enum<T> {
|
||||
type Owned = ();
|
||||
|
||||
fn into_ffi_value(instance: &T) -> WrappedFFIValue<Self::FFIType, Self::Owned> {
|
||||
let value: u8 = (*instance).into();
|
||||
value.into()
|
||||
}
|
||||
|
||||
fn from_ffi_value(arg: Self::FFIType) -> T {
|
||||
T::try_from(arg).expect("Host to wasm provides a valid enum discriminant; qed")
|
||||
}
|
||||
}
|
||||
|
||||
/// The type is passed as `u8`.
|
||||
///
|
||||
/// The value is corresponds to the discriminant of the variant.
|
||||
impl<T: Copy + Into<u8> + TryFrom<u8>> RIType for Enum<T> {
|
||||
type FFIType = u8;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Traits required by the runtime interface from the wasm side.
|
||||
|
||||
use crate::RIType;
|
||||
|
||||
use rstd::cell::Cell;
|
||||
|
||||
/// Something that can be created from a ffi value.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It is unsafe behavior to call `Something::into_ffi_value().get()` and take this as input for
|
||||
/// `from_ffi_value`. Implementations are safe to assume that the `arg` given to `from_ffi_value`
|
||||
/// is only generated by the corresponding `host::IntoFFIValue` implementation.
|
||||
pub trait FromFFIValue: Sized + RIType {
|
||||
/// Create `Self` from the given ffi value.
|
||||
fn from_ffi_value(arg: Self::FFIType) -> Self;
|
||||
}
|
||||
|
||||
/// Something that can be converted into a ffi value.
|
||||
pub trait IntoFFIValue: RIType {
|
||||
/// The owned rust type that is stored with the ffi value in [`WrappedFFIValue`].
|
||||
///
|
||||
/// If no owned value is required, `()` can be used as a type.
|
||||
type Owned;
|
||||
|
||||
/// Convert `self` into a [`WrappedFFIValue`].
|
||||
fn into_ffi_value(&self) -> WrappedFFIValue<Self::FFIType, Self::Owned>;
|
||||
}
|
||||
|
||||
/// Represents a wrapped ffi value.
|
||||
///
|
||||
/// It is either the ffi value itself or the ffi value plus some other owned value. By providing
|
||||
/// support for storing another owned value besides the actual ffi value certain performance
|
||||
/// optimizations can be applied. For example using the pointer to a `Vec<u8>`, while using the
|
||||
/// pointer to a SCALE encoded `Vec<u8>` that is stored in this wrapper for any other `Vec<T>`.
|
||||
pub enum WrappedFFIValue<T, O = ()> {
|
||||
Wrapped(T),
|
||||
WrappedAndOwned(T, O),
|
||||
}
|
||||
|
||||
impl<T: Copy, O> WrappedFFIValue<T, O> {
|
||||
/// Returns the wrapped ffi value.
|
||||
pub fn get(&self) -> T {
|
||||
match self {
|
||||
Self::Wrapped(data) | Self::WrappedAndOwned(data, _) => *data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, O> From<T> for WrappedFFIValue<T, O> {
|
||||
fn from(val: T) -> Self {
|
||||
WrappedFFIValue::Wrapped(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, O> From<(T, O)> for WrappedFFIValue<T, O> {
|
||||
fn from(val: (T, O)) -> Self {
|
||||
WrappedFFIValue::WrappedAndOwned(val.0, val.1)
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of an exchangeable function.
|
||||
#[derive(Clone, Copy)]
|
||||
enum ExchangeableFunctionState {
|
||||
/// Original function is present
|
||||
Original,
|
||||
/// The function has been replaced.
|
||||
Replaced,
|
||||
}
|
||||
|
||||
/// A function which implementation can be exchanged.
|
||||
///
|
||||
/// Internally this works by swapping function pointers.
|
||||
pub struct ExchangeableFunction<T>(Cell<(T, ExchangeableFunctionState)>);
|
||||
|
||||
impl<T> ExchangeableFunction<T> {
|
||||
/// Create a new instance of `ExchangeableFunction`.
|
||||
pub const fn new(impl_: T) -> Self {
|
||||
Self(Cell::new((impl_, ExchangeableFunctionState::Original)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> ExchangeableFunction<T> {
|
||||
/// Replace the implementation with `new_impl`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when trying to replace an already replaced implementation.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// Returns the original implementation wrapped in [`RestoreImplementation`].
|
||||
pub fn replace_implementation(&'static self, new_impl: T) -> RestoreImplementation<T> {
|
||||
if let ExchangeableFunctionState::Replaced = self.0.get().1 {
|
||||
panic!("Trying to replace an already replaced implementation!")
|
||||
}
|
||||
|
||||
let old = self.0.replace((new_impl, ExchangeableFunctionState::Replaced));
|
||||
|
||||
RestoreImplementation(self, Some(old.0))
|
||||
}
|
||||
|
||||
/// Restore the original implementation.
|
||||
fn restore_orig_implementation(&self, orig: T) {
|
||||
self.0.set((orig, ExchangeableFunctionState::Original));
|
||||
}
|
||||
|
||||
/// Returns the internal function pointer.
|
||||
pub fn get(&self) -> T {
|
||||
self.0.get().0
|
||||
}
|
||||
}
|
||||
|
||||
// Wasm does not support threads, so this is safe; qed.
|
||||
unsafe impl<T> Sync for ExchangeableFunction<T> {}
|
||||
|
||||
/// Restores a function implementation on drop.
|
||||
///
|
||||
/// Stores a static reference to the function object and the original implementation.
|
||||
pub struct RestoreImplementation<T: 'static + Copy>(&'static ExchangeableFunction<T>, Option<T>);
|
||||
|
||||
impl<T: Copy> Drop for RestoreImplementation<T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.restore_orig_implementation(self.1.take().expect("Value is only taken on drop; qed"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user