Skip to main content

gsp_wasm_interface/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Types and traits for interfacing between the host and the wasm runtime.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate alloc;
23
24use alloc::{vec, vec::Vec};
25use core::{iter::Iterator, marker::PhantomData, result};
26pub use sp_wasm_interface_common::{
27    self as common, HostPointer, IntoValue, MemoryId, Pointer, PointerType, ReturnValue, Signature,
28    TryFromValue, Value, ValueType, WordSize,
29};
30
31if_wasmtime_is_enabled! {
32    mod host_state;
33    pub use host_state::HostState;
34
35    mod store_data;
36    pub use store_data::StoreData;
37
38    mod memory_wrapper;
39    pub use memory_wrapper::MemoryWrapper;
40
41    pub mod util;
42}
43
44#[cfg(not(all(feature = "std", feature = "wasmtime")))]
45pub struct StoreData;
46
47#[cfg(not(all(feature = "std", feature = "wasmtime")))]
48#[macro_export]
49macro_rules! if_wasmtime_is_enabled {
50    ($($token:tt)*) => {};
51}
52
53#[cfg(all(feature = "std", feature = "wasmtime"))]
54#[macro_export]
55macro_rules! if_wasmtime_is_enabled {
56    ($($token:tt)*) => {
57        $($token)*
58    }
59}
60
61if_wasmtime_is_enabled! {
62    // Reexport wasmtime so that its types are accessible from the procedural macro.
63    pub use wasmtime;
64
65    // Wasmtime uses anyhow types but doesn't reexport them.
66    pub use anyhow;
67}
68
69/// Result type used by traits in this crate.
70#[cfg(feature = "std")]
71pub type Result<T> = result::Result<T, String>;
72#[cfg(not(feature = "std"))]
73pub type Result<T> = result::Result<T, &'static str>;
74
75/// Provides `Sealed` trait to prevent implementing trait `PointerType` and `WasmTy` outside of this
76/// crate.
77mod private {
78    pub trait Sealed {}
79
80    impl Sealed for u8 {}
81    impl Sealed for u16 {}
82    impl Sealed for u32 {}
83    impl Sealed for u64 {}
84
85    impl Sealed for i32 {}
86    impl Sealed for i64 {}
87}
88
89/// A trait that requires `RefUnwindSafe` when `feature = std`.
90#[cfg(feature = "std")]
91pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {}
92#[cfg(feature = "std")]
93impl<T: std::panic::RefUnwindSafe> MaybeRefUnwindSafe for T {}
94
95/// A trait that requires `RefUnwindSafe` when `feature = std`.
96#[cfg(not(feature = "std"))]
97pub trait MaybeRefUnwindSafe {}
98#[cfg(not(feature = "std"))]
99impl<T> MaybeRefUnwindSafe for T {}
100
101/// Something that provides a function implementation on the host for a wasm function.
102pub trait Function: MaybeRefUnwindSafe + Send + Sync {
103    /// Returns the name of this function.
104    fn name(&self) -> &str;
105    /// Returns the signature of this function.
106    fn signature(&self) -> Signature;
107    /// Execute this function with the given arguments.
108    fn execute(
109        &self,
110        context: &mut dyn FunctionContext,
111        args: &mut dyn Iterator<Item = Value>,
112    ) -> Result<Option<Value>>;
113}
114
115impl PartialEq for dyn Function {
116    fn eq(&self, other: &Self) -> bool {
117        other.name() == self.name() && other.signature() == self.signature()
118    }
119}
120
121#[cfg(not(all(feature = "std", feature = "wasmtime")))]
122pub struct Caller<'a, T>(PhantomData<&'a T>);
123
124#[cfg(all(feature = "std", feature = "wasmtime"))]
125pub use wasmtime::Caller;
126
127/// Context used by `Function` to interact with the allocator and the memory of the wasm instance.
128pub trait FunctionContext {
129    /// Read memory from `address` into a vector.
130    fn read_memory(&self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
131        let mut vec = vec![0; size as usize];
132        self.read_memory_into(address, &mut vec)?;
133        Ok(vec)
134    }
135    /// Read memory into the given `dest` buffer from `address`.
136    fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()>;
137    /// Write the given data at `address` into the memory.
138    fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> Result<()>;
139    /// Allocate a memory instance of `size` bytes.
140    fn allocate_memory(&mut self, size: WordSize) -> Result<Pointer<u8>>;
141    /// Deallocate a given memory instance.
142    fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> Result<()>;
143    /// Registers a panic error message within the executor.
144    ///
145    /// This is meant to be used in situations where the runtime
146    /// encounters an unrecoverable error and intends to panic.
147    ///
148    /// Panicking in WASM is done through the [`unreachable`](https://webassembly.github.io/spec/core/syntax/instructions.html#syntax-instr-control)
149    /// instruction which causes an unconditional trap and immediately aborts
150    /// the execution. It does not however allow for any diagnostics to be
151    /// passed through to the host, so while we do know that *something* went
152    /// wrong we don't have any direct indication of what *exactly* went wrong.
153    ///
154    /// As a workaround we use this method right before the execution is
155    /// actually aborted to pass an error message to the host so that it
156    /// can associate it with the next trap, and return that to the caller.
157    ///
158    /// A WASM trap should be triggered immediately after calling this method;
159    /// otherwise the error message might be associated with a completely
160    /// unrelated trap.
161    ///
162    /// It should only be called once, however calling it more than once
163    /// is harmless and will overwrite the previously set error message.
164    fn register_panic_error_message(&mut self, message: &str);
165}
166
167if_wasmtime_is_enabled! {
168    /// A trait used to statically register host callbacks with the WASM executor,
169    /// so that they can be called from within the runtime with minimal overhead.
170    ///
171    /// This is used internally to interface the wasmtime-based executor with the
172    /// host functions' definitions generated through the runtime interface macro,
173    /// and is not meant to be used directly.
174    pub trait HostFunctionRegistry {
175        type State: 'static;
176        type Error;
177        type FunctionContext: FunctionContext;
178
179        /// Wraps the given `caller` in a type which implements `FunctionContext`
180        /// and calls the given `callback`.
181        fn with_function_context<R>(
182            caller: wasmtime::Caller<Self::State>,
183            callback: impl FnOnce(&mut dyn FunctionContext) -> R,
184        ) -> R;
185
186        /// Registers a given host function with the WASM executor.
187        ///
188        /// The function has to be statically callable, and all of its arguments
189        /// and its return value have to be compatible with WASM FFI.
190        fn register_static<Params, Results>(
191            &mut self,
192            fn_name: &str,
193            func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
194        ) -> core::result::Result<(), Self::Error>;
195    }
196}
197
198/// Something that provides implementations for host functions.
199pub trait HostFunctions: 'static + Send + Sync {
200    /// Returns the host functions `Self` provides.
201    fn host_functions() -> Vec<&'static dyn Function>;
202
203    if_wasmtime_is_enabled! {
204        /// Statically registers the host functions.
205        fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
206        where
207            T: HostFunctionRegistry;
208    }
209}
210
211#[impl_trait_for_tuples::impl_for_tuples(30)]
212impl HostFunctions for Tuple {
213    #[allow(clippy::let_and_return)]
214    fn host_functions() -> Vec<&'static dyn Function> {
215        let mut host_functions = Vec::new();
216
217        for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* );
218
219        host_functions
220    }
221
222    #[cfg(all(feature = "std", feature = "wasmtime"))]
223    fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
224    where
225        T: HostFunctionRegistry,
226    {
227        for_tuples!(
228            #( Tuple::register_static(registry)?; )*
229        );
230
231        Ok(())
232    }
233}
234
235/// A wrapper which merges two sets of host functions, and allows the second set to override
236/// the host functions from the first set.
237pub struct ExtendedHostFunctions<Base, Overlay> {
238    phantom: PhantomData<(Base, Overlay)>,
239}
240
241impl<Base, Overlay> HostFunctions for ExtendedHostFunctions<Base, Overlay>
242where
243    Base: HostFunctions,
244    Overlay: HostFunctions,
245{
246    fn host_functions() -> Vec<&'static dyn Function> {
247        let mut base = Base::host_functions();
248        let overlay = Overlay::host_functions();
249        base.retain(|host_fn| {
250            !overlay
251                .iter()
252                .any(|ext_host_fn| host_fn.name() == ext_host_fn.name())
253        });
254        base.extend(overlay);
255        base
256    }
257
258    if_wasmtime_is_enabled! {
259        fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
260        where
261            T: HostFunctionRegistry,
262        {
263            struct Proxy<'a, T> {
264                registry: &'a mut T,
265                seen_overlay: std::collections::HashSet<String>,
266                seen_base: std::collections::HashSet<String>,
267                overlay_registered: bool,
268            }
269
270            impl<'a, T> HostFunctionRegistry for Proxy<'a, T>
271            where
272                T: HostFunctionRegistry,
273            {
274                type State = T::State;
275                type Error = T::Error;
276                type FunctionContext = T::FunctionContext;
277
278                fn with_function_context<R>(
279                    caller: wasmtime::Caller<Self::State>,
280                    callback: impl FnOnce(&mut dyn FunctionContext) -> R,
281                ) -> R {
282                    T::with_function_context(caller, callback)
283                }
284
285                fn register_static<Params, Results>(
286                    &mut self,
287                    fn_name: &str,
288                    func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
289                ) -> core::result::Result<(), Self::Error> {
290                    if self.overlay_registered {
291                        if !self.seen_base.insert(fn_name.to_owned()) {
292                            log::warn!(
293                                target: "extended_host_functions",
294                                "Duplicate base host function: '{}'",
295                                fn_name,
296                            );
297
298                            // TODO: Return an error here?
299                            return Ok(())
300                        }
301
302                        if self.seen_overlay.contains(fn_name) {
303                            // Was already registered when we went through the overlay, so just ignore it.
304                            log::debug!(
305                                target: "extended_host_functions",
306                                "Overriding base host function: '{}'",
307                                fn_name,
308                            );
309
310                            return Ok(())
311                        }
312                    } else if !self.seen_overlay.insert(fn_name.to_owned()) {
313                        log::warn!(
314                            target: "extended_host_functions",
315                            "Duplicate overlay host function: '{}'",
316                            fn_name,
317                        );
318
319                        // TODO: Return an error here?
320                        return Ok(())
321                    }
322
323                    self.registry.register_static(fn_name, func)
324                }
325            }
326
327            let mut proxy = Proxy {
328                registry,
329                seen_overlay: Default::default(),
330                seen_base: Default::default(),
331                overlay_registered: false,
332            };
333
334            // The functions from the `Overlay` can override those from the `Base`,
335            // so `Overlay` is registered first, and then we skip those functions
336            // in `Base` if they were already registered from the `Overlay`.
337            Overlay::register_static(&mut proxy)?;
338            proxy.overlay_registered = true;
339            Base::register_static(&mut proxy)?;
340
341            Ok(())
342        }
343    }
344}
345
346/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
347///
348/// This trait is sealed and should not be implemented downstream.
349#[cfg(all(feature = "std", feature = "wasmtime"))]
350pub trait WasmTy: wasmtime::WasmTy + private::Sealed {}
351
352/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
353///
354/// This trait is sealed and should not be implemented downstream.
355#[cfg(not(all(feature = "std", feature = "wasmtime")))]
356pub trait WasmTy: private::Sealed {}
357
358impl WasmTy for i32 {}
359impl WasmTy for u32 {}
360impl WasmTy for i64 {}
361impl WasmTy for u64 {}