// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Types and traits for interfacing between the host and the wasm runtime. #![cfg_attr(not(feature = "std"), no_std)] use sp_std::{iter::Iterator, marker::PhantomData, result, vec, vec::Vec}; pub use sp_wasm_interface_common::{ self as common, HostPointer, IntoValue, MemoryId, Pointer, PointerType, ReturnValue, Signature, TryFromValue, Value, ValueType, WordSize, }; if_wasmtime_is_enabled! { mod host_state; pub use host_state::HostState; mod store_data; pub use store_data::StoreData; mod memory_wrapper; pub use memory_wrapper::MemoryWrapper; pub mod util; } #[cfg(not(all(feature = "std", feature = "wasmtime")))] pub struct StoreData; #[cfg(not(all(feature = "std", feature = "wasmtime")))] #[macro_export] macro_rules! if_wasmtime_is_enabled { ($($token:tt)*) => {}; } #[cfg(all(feature = "std", feature = "wasmtime"))] #[macro_export] macro_rules! if_wasmtime_is_enabled { ($($token:tt)*) => { $($token)* } } if_wasmtime_is_enabled! { // Reexport wasmtime so that its types are accessible from the procedural macro. pub use wasmtime; // Wasmtime uses anyhow types but doesn't reexport them. pub use anyhow; } /// Result type used by traits in this crate. #[cfg(feature = "std")] pub type Result = result::Result; #[cfg(not(feature = "std"))] pub type Result = result::Result; /// Provides `Sealed` trait to prevent implementing trait `PointerType` and `WasmTy` outside of this /// crate. mod private { pub trait Sealed {} impl Sealed for u8 {} impl Sealed for u16 {} impl Sealed for u32 {} impl Sealed for u64 {} impl Sealed for i32 {} impl Sealed for i64 {} pub(super) struct Token; } /// A trait that requires `RefUnwindSafe` when `feature = std`. #[cfg(feature = "std")] pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {} #[cfg(feature = "std")] impl MaybeRefUnwindSafe for T {} /// A trait that requires `RefUnwindSafe` when `feature = std`. #[cfg(not(feature = "std"))] pub trait MaybeRefUnwindSafe {} #[cfg(not(feature = "std"))] impl MaybeRefUnwindSafe for T {} /// Something that provides a function implementation on the host for a wasm function. pub trait Function: MaybeRefUnwindSafe + Send + Sync { /// Returns the name of this function. fn name(&self) -> &str; /// Returns the signature of this function. fn signature(&self) -> Signature; /// Execute this function with the given arguments. fn execute( &self, context: &mut dyn FunctionContext, args: &mut dyn Iterator, ) -> Result>; } impl PartialEq for dyn Function { fn eq(&self, other: &Self) -> bool { other.name() == self.name() && other.signature() == self.signature() } } #[cfg(not(all(feature = "std", feature = "wasmtime")))] pub struct Caller<'a, T>(PhantomData<&'a T>); #[cfg(all(feature = "std", feature = "wasmtime"))] pub use wasmtime::Caller; pub struct FunctionContextToken(private::Token); impl FunctionContextToken { fn new() -> Self { Self(private::Token) } } /// Context used by `Function` to interact with the allocator and the memory of the wasm instance. pub trait FunctionContext { /// Read memory from `address` into a vector. fn read_memory(&self, address: Pointer, size: WordSize) -> Result> { let mut vec = vec![0; size as usize]; self.read_memory_into(address, &mut vec)?; Ok(vec) } /// Read memory into the given `dest` buffer from `address`. fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> Result<()>; /// Write the given data at `address` into the memory. fn write_memory(&mut self, address: Pointer, data: &[u8]) -> Result<()>; /// Allocate a memory instance of `size` bytes. fn allocate_memory(&mut self, size: WordSize) -> Result>; /// Deallocate a given memory instance. fn deallocate_memory(&mut self, ptr: Pointer) -> Result<()>; /// Registers a panic error message within the executor. /// /// This is meant to be used in situations where the runtime /// encounters an unrecoverable error and intends to panic. /// /// Panicking in WASM is done through the [`unreachable`](https://webassembly.github.io/spec/core/syntax/instructions.html#syntax-instr-control) /// instruction which causes an unconditional trap and immediately aborts /// the execution. It does not however allow for any diagnostics to be /// passed through to the host, so while we do know that *something* went /// wrong we don't have any direct indication of what *exactly* went wrong. /// /// As a workaround we use this method right before the execution is /// actually aborted to pass an error message to the host so that it /// can associate it with the next trap, and return that to the caller. /// /// A WASM trap should be triggered immediately after calling this method; /// otherwise the error message might be associated with a completely /// unrelated trap. /// /// It should only be called once, however calling it more than once /// is harmless and will overwrite the previously set error message. fn register_panic_error_message(&mut self, message: &str); fn with_caller_mut_impl( &mut self, _: FunctionContextToken, context: *mut (), callback: fn(*mut (), &mut Caller), ); } pub fn with_caller_mut)>( context: &mut dyn FunctionContext, mut callback: T, ) { let callback: *mut T = &mut callback; context.with_caller_mut_impl(FunctionContextToken::new(), callback.cast(), |callback, caller| { let callback: *mut T = callback.cast(); let callback: &mut T = unsafe { callback.as_mut().expect("we own the value, obtain mutable reference to it and cast to pointer (correct (not null) and aligned properly); qed") }; callback(caller); }) } if_wasmtime_is_enabled! { /// A trait used to statically register host callbacks with the WASM executor, /// so that they call be called from within the runtime with minimal overhead. /// /// This is used internally to interface the wasmtime-based executor with the /// host functions' definitions generated through the runtime interface macro, /// and is not meant to be used directly. pub trait HostFunctionRegistry { type State; type Error; type FunctionContext: FunctionContext; /// Wraps the given `caller` in a type which implements `FunctionContext` /// and calls the given `callback`. fn with_function_context( caller: wasmtime::Caller, callback: impl FnOnce(&mut dyn FunctionContext) -> R, ) -> R; /// Registers a given host function with the WASM executor. /// /// The function has to be statically callable, and all of its arguments /// and its return value have to be compatible with WASM FFI. fn register_static( &mut self, fn_name: &str, func: impl wasmtime::IntoFunc + 'static, ) -> core::result::Result<(), Self::Error>; } } /// Something that provides implementations for host functions. pub trait HostFunctions: 'static + Send + Sync { /// Returns the host functions `Self` provides. fn host_functions() -> Vec<&'static dyn Function>; if_wasmtime_is_enabled! { /// Statically registers the host functions. fn register_static(registry: &mut T) -> core::result::Result<(), T::Error> where T: HostFunctionRegistry; } } #[impl_trait_for_tuples::impl_for_tuples(30)] impl HostFunctions for Tuple { fn host_functions() -> Vec<&'static dyn Function> { let mut host_functions = Vec::new(); for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* ); host_functions } #[cfg(all(feature = "std", feature = "wasmtime"))] fn register_static(registry: &mut T) -> core::result::Result<(), T::Error> where T: HostFunctionRegistry, { for_tuples!( #( Tuple::register_static(registry)?; )* ); Ok(()) } } /// A wrapper which merges two sets of host functions, and allows the second set to override /// the host functions from the first set. pub struct ExtendedHostFunctions { phantom: PhantomData<(Base, Overlay)>, } impl HostFunctions for ExtendedHostFunctions where Base: HostFunctions, Overlay: HostFunctions, { fn host_functions() -> Vec<&'static dyn Function> { let mut base = Base::host_functions(); let overlay = Overlay::host_functions(); base.retain(|host_fn| { !overlay.iter().any(|ext_host_fn| host_fn.name() == ext_host_fn.name()) }); base.extend(overlay); base } if_wasmtime_is_enabled! { fn register_static(registry: &mut T) -> core::result::Result<(), T::Error> where T: HostFunctionRegistry, { struct Proxy<'a, T> { registry: &'a mut T, seen_overlay: std::collections::HashSet, seen_base: std::collections::HashSet, overlay_registered: bool, } impl<'a, T> HostFunctionRegistry for Proxy<'a, T> where T: HostFunctionRegistry, { type State = T::State; type Error = T::Error; type FunctionContext = T::FunctionContext; fn with_function_context( caller: wasmtime::Caller, callback: impl FnOnce(&mut dyn FunctionContext) -> R, ) -> R { T::with_function_context(caller, callback) } fn register_static( &mut self, fn_name: &str, func: impl wasmtime::IntoFunc + 'static, ) -> core::result::Result<(), Self::Error> { if self.overlay_registered { if !self.seen_base.insert(fn_name.to_owned()) { log::warn!( target: "extended_host_functions", "Duplicate base host function: '{}'", fn_name, ); // TODO: Return an error here? return Ok(()) } if self.seen_overlay.contains(fn_name) { // Was already registered when we went through the overlay, so just ignore it. log::debug!( target: "extended_host_functions", "Overriding base host function: '{}'", fn_name, ); return Ok(()) } } else if !self.seen_overlay.insert(fn_name.to_owned()) { log::warn!( target: "extended_host_functions", "Duplicate overlay host function: '{}'", fn_name, ); // TODO: Return an error here? return Ok(()) } self.registry.register_static(fn_name, func) } } let mut proxy = Proxy { registry, seen_overlay: Default::default(), seen_base: Default::default(), overlay_registered: false, }; // The functions from the `Overlay` can override those from the `Base`, // so `Overlay` is registered first, and then we skip those functions // in `Base` if they were already registered from the `Overlay`. Overlay::register_static(&mut proxy)?; proxy.overlay_registered = true; Base::register_static(&mut proxy)?; Ok(()) } } } /// A trait for types directly usable at the WASM FFI boundary without any conversion at all. /// /// This trait is sealed and should not be implemented downstream. #[cfg(all(feature = "std", feature = "wasmtime"))] pub trait WasmTy: wasmtime::WasmTy + private::Sealed {} /// A trait for types directly usable at the WASM FFI boundary without any conversion at all. /// /// This trait is sealed and should not be implemented downstream. #[cfg(not(all(feature = "std", feature = "wasmtime")))] pub trait WasmTy: private::Sealed {} impl WasmTy for i32 {} impl WasmTy for u32 {} impl WasmTy for i64 {} impl WasmTy for u64 {}