referrerpolicy=no-referrer-when-downgrade

sp_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::{borrow::Cow, string::String, vec, vec::Vec};
25use core::{iter::Iterator, marker::PhantomData, mem};
26
27#[cfg(not(all(feature = "std", feature = "wasmtime")))]
28#[macro_export]
29macro_rules! if_wasmtime_is_enabled {
30	($($token:tt)*) => {};
31}
32
33#[cfg(all(feature = "std", feature = "wasmtime"))]
34#[macro_export]
35macro_rules! if_wasmtime_is_enabled {
36    ($($token:tt)*) => {
37        $($token)*
38    }
39}
40
41if_wasmtime_is_enabled! {
42	// Reexport wasmtime so that its types are accessible from the procedural macro.
43	pub use wasmtime;
44
45	// Wasmtime uses anyhow types but doesn't reexport them.
46	pub use anyhow;
47}
48
49/// Result type used by traits in this crate.
50pub type Result<T> = core::result::Result<T, String>;
51
52/// Value types supported by Substrate on the boundary between host/Wasm.
53#[derive(Copy, Clone, PartialEq, Debug, Eq)]
54pub enum ValueType {
55	/// An `i32` value type.
56	I32,
57	/// An `i64` value type.
58	I64,
59	/// An `f32` value type.
60	F32,
61	/// An `f64` value type.
62	F64,
63}
64
65impl From<ValueType> for u8 {
66	fn from(val: ValueType) -> u8 {
67		match val {
68			ValueType::I32 => 0,
69			ValueType::I64 => 1,
70			ValueType::F32 => 2,
71			ValueType::F64 => 3,
72		}
73	}
74}
75
76impl TryFrom<u8> for ValueType {
77	type Error = ();
78
79	fn try_from(val: u8) -> core::result::Result<ValueType, ()> {
80		match val {
81			0 => Ok(Self::I32),
82			1 => Ok(Self::I64),
83			2 => Ok(Self::F32),
84			3 => Ok(Self::F64),
85			_ => Err(()),
86		}
87	}
88}
89
90/// Values supported by Substrate on the boundary between host/Wasm.
91#[derive(PartialEq, Debug, Clone, Copy, codec::Encode, codec::Decode)]
92pub enum Value {
93	/// A 32-bit integer.
94	I32(i32),
95	/// A 64-bit integer.
96	I64(i64),
97	/// A 32-bit floating-point number stored as raw bit pattern.
98	///
99	/// You can materialize this value using `f32::from_bits`.
100	F32(u32),
101	/// A 64-bit floating-point number stored as raw bit pattern.
102	///
103	/// You can materialize this value using `f64::from_bits`.
104	F64(u64),
105}
106
107impl Value {
108	/// Returns the type of this value.
109	pub fn value_type(&self) -> ValueType {
110		match self {
111			Value::I32(_) => ValueType::I32,
112			Value::I64(_) => ValueType::I64,
113			Value::F32(_) => ValueType::F32,
114			Value::F64(_) => ValueType::F64,
115		}
116	}
117
118	/// Return `Self` as `i32`.
119	pub fn as_i32(&self) -> Option<i32> {
120		match self {
121			Self::I32(val) => Some(*val),
122			_ => None,
123		}
124	}
125}
126
127/// Provides `Sealed` trait to prevent implementing trait `PointerType` and `WasmTy` outside of this
128/// crate.
129mod private {
130	pub trait Sealed {}
131
132	impl Sealed for u8 {}
133	impl Sealed for u16 {}
134	impl Sealed for u32 {}
135	impl Sealed for u64 {}
136
137	impl Sealed for i32 {}
138	impl Sealed for i64 {}
139}
140
141/// Something that can be wrapped in a wasm `Pointer`.
142///
143/// This trait is sealed.
144pub trait PointerType: Sized + private::Sealed {
145	/// The size of the type in wasm.
146	const SIZE: u32 = mem::size_of::<Self>() as u32;
147}
148
149impl PointerType for u8 {}
150impl PointerType for u16 {}
151impl PointerType for u32 {}
152impl PointerType for u64 {}
153
154/// Type to represent a pointer in wasm at the host.
155#[derive(Debug, PartialEq, Eq)]
156pub struct Pointer<T> {
157	ptr: u32,
158	_marker: PhantomData<T>,
159}
160
161impl<T> Copy for Pointer<T> {}
162impl<T> Clone for Pointer<T> {
163	fn clone(&self) -> Self {
164		Pointer { ptr: self.ptr, _marker: PhantomData }
165	}
166}
167
168impl<T: PointerType> Pointer<T> {
169	/// Create a new instance of `Self`.
170	pub fn new(ptr: u32) -> Self {
171		Self { ptr, _marker: Default::default() }
172	}
173
174	/// Calculate the offset from this pointer.
175	///
176	/// `offset` is in units of `T`. So, `3` means `3 * mem::size_of::<T>()` as offset to the
177	/// pointer.
178	///
179	/// Returns an `Option` to respect that the pointer could probably overflow.
180	pub fn offset(self, offset: u32) -> Option<Self> {
181		offset
182			.checked_mul(T::SIZE)
183			.and_then(|o| self.ptr.checked_add(o))
184			.map(|ptr| Self { ptr, _marker: Default::default() })
185	}
186
187	/// Create a null pointer.
188	pub fn null() -> Self {
189		Self::new(0)
190	}
191
192	/// Cast this pointer of type `T` to a pointer of type `R`.
193	pub fn cast<R: PointerType>(self) -> Pointer<R> {
194		Pointer::new(self.ptr)
195	}
196}
197
198impl<T: PointerType> From<u32> for Pointer<T> {
199	fn from(ptr: u32) -> Self {
200		Pointer::new(ptr)
201	}
202}
203
204impl<T: PointerType> From<Pointer<T>> for u32 {
205	fn from(ptr: Pointer<T>) -> Self {
206		ptr.ptr
207	}
208}
209
210impl<T: PointerType> From<Pointer<T>> for u64 {
211	fn from(ptr: Pointer<T>) -> Self {
212		u64::from(ptr.ptr)
213	}
214}
215
216impl<T: PointerType> From<Pointer<T>> for usize {
217	fn from(ptr: Pointer<T>) -> Self {
218		ptr.ptr as _
219	}
220}
221
222impl<T: PointerType> IntoValue for Pointer<T> {
223	const VALUE_TYPE: ValueType = ValueType::I32;
224	fn into_value(self) -> Value {
225		Value::I32(self.ptr as _)
226	}
227}
228
229impl<T: PointerType> TryFromValue for Pointer<T> {
230	fn try_from_value(val: Value) -> Option<Self> {
231		match val {
232			Value::I32(val) => Some(Self::new(val as _)),
233			_ => None,
234		}
235	}
236}
237
238/// The word size used in wasm. Normally known as `usize` in Rust.
239pub type WordSize = u32;
240
241/// The Signature of a function
242#[derive(Eq, PartialEq, Debug, Clone)]
243pub struct Signature {
244	/// The arguments of a function.
245	pub args: Cow<'static, [ValueType]>,
246	/// The optional return value of a function.
247	pub return_value: Option<ValueType>,
248}
249
250impl Signature {
251	/// Create a new instance of `Signature`.
252	pub fn new<T: Into<Cow<'static, [ValueType]>>>(
253		args: T,
254		return_value: Option<ValueType>,
255	) -> Self {
256		Self { args: args.into(), return_value }
257	}
258
259	/// Create a new instance of `Signature` with the given `args` and without any return value.
260	pub fn new_with_args<T: Into<Cow<'static, [ValueType]>>>(args: T) -> Self {
261		Self { args: args.into(), return_value: None }
262	}
263}
264
265/// A trait that requires `RefUnwindSafe` when `feature = std`.
266#[cfg(feature = "std")]
267pub trait MaybeRefUnwindSafe: std::panic::RefUnwindSafe {}
268#[cfg(feature = "std")]
269impl<T: std::panic::RefUnwindSafe> MaybeRefUnwindSafe for T {}
270
271/// A trait that requires `RefUnwindSafe` when `feature = std`.
272#[cfg(not(feature = "std"))]
273pub trait MaybeRefUnwindSafe {}
274#[cfg(not(feature = "std"))]
275impl<T> MaybeRefUnwindSafe for T {}
276
277/// Something that provides a function implementation on the host for a wasm function.
278pub trait Function: MaybeRefUnwindSafe + Send + Sync {
279	/// Returns the name of this function.
280	fn name(&self) -> &str;
281	/// Returns the signature of this function.
282	fn signature(&self) -> Signature;
283	/// Execute this function with the given arguments.
284	fn execute(
285		&self,
286		context: &mut dyn FunctionContext,
287		args: &mut dyn Iterator<Item = Value>,
288	) -> Result<Option<Value>>;
289}
290
291impl PartialEq for dyn Function {
292	fn eq(&self, other: &Self) -> bool {
293		other.name() == self.name() && other.signature() == self.signature()
294	}
295}
296
297/// Context used by `Function` to interact with the allocator and the memory of the wasm instance.
298pub trait FunctionContext {
299	/// Read memory from `address` into a vector.
300	fn read_memory(&self, address: Pointer<u8>, size: WordSize) -> Result<Vec<u8>> {
301		let mut vec = vec![0; size as usize];
302		self.read_memory_into(address, &mut vec)?;
303		Ok(vec)
304	}
305	/// Read memory into the given `dest` buffer from `address`.
306	fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> Result<()>;
307	/// Write the given data at `address` into the memory.
308	fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> Result<()>;
309	/// Allocate a memory instance of `size` bytes.
310	fn allocate_memory(&mut self, size: WordSize) -> Result<Pointer<u8>>;
311	/// Deallocate a given memory instance.
312	fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> Result<()>;
313	/// Registers a panic error message within the executor.
314	///
315	/// This is meant to be used in situations where the runtime
316	/// encounters an unrecoverable error and intends to panic.
317	///
318	/// Panicking in WASM is done through the [`unreachable`](https://webassembly.github.io/spec/core/syntax/instructions.html#syntax-instr-control)
319	/// instruction which causes an unconditional trap and immediately aborts
320	/// the execution. It does not however allow for any diagnostics to be
321	/// passed through to the host, so while we do know that *something* went
322	/// wrong we don't have any direct indication of what *exactly* went wrong.
323	///
324	/// As a workaround we use this method right before the execution is
325	/// actually aborted to pass an error message to the host so that it
326	/// can associate it with the next trap, and return that to the caller.
327	///
328	/// A WASM trap should be triggered immediately after calling this method;
329	/// otherwise the error message might be associated with a completely
330	/// unrelated trap.
331	///
332	/// It should only be called once, however calling it more than once
333	/// is harmless and will overwrite the previously set error message.
334	fn register_panic_error_message(&mut self, message: &str);
335}
336
337if_wasmtime_is_enabled! {
338	/// A trait used to statically register host callbacks with the WASM executor,
339	/// so that they call be called from within the runtime with minimal overhead.
340	///
341	/// This is used internally to interface the wasmtime-based executor with the
342	/// host functions' definitions generated through the runtime interface macro,
343	/// and is not meant to be used directly.
344	pub trait HostFunctionRegistry {
345		type State;
346		type Error;
347		type FunctionContext: FunctionContext;
348
349		/// Wraps the given `caller` in a type which implements `FunctionContext`
350		/// and calls the given `callback`.
351		fn with_function_context<R>(
352			caller: wasmtime::Caller<Self::State>,
353			callback: impl FnOnce(&mut dyn FunctionContext) -> R,
354		) -> R;
355
356		/// Registers a given host function with the WASM executor.
357		///
358		/// The function has to be statically callable, and all of its arguments
359		/// and its return value have to be compatible with WASM FFI.
360		fn register_static<Params, Results>(
361			&mut self,
362			fn_name: &str,
363			func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
364		) -> core::result::Result<(), Self::Error>;
365	}
366}
367
368/// Something that provides implementations for host functions.
369pub trait HostFunctions: 'static + Send + Sync {
370	/// Returns the host functions `Self` provides.
371	fn host_functions() -> Vec<&'static dyn Function>;
372
373	if_wasmtime_is_enabled! {
374		/// Statically registers the host functions.
375		fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
376		where
377			T: HostFunctionRegistry;
378	}
379}
380
381#[impl_trait_for_tuples::impl_for_tuples(30)]
382impl HostFunctions for Tuple {
383	fn host_functions() -> Vec<&'static dyn Function> {
384		let mut host_functions = Vec::new();
385
386		for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* );
387
388		host_functions
389	}
390
391	#[cfg(all(feature = "std", feature = "wasmtime"))]
392	fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
393	where
394		T: HostFunctionRegistry,
395	{
396		for_tuples!(
397			#( Tuple::register_static(registry)?; )*
398		);
399
400		Ok(())
401	}
402}
403
404/// A wrapper which merges two sets of host functions, and allows the second set to override
405/// the host functions from the first set.
406pub struct ExtendedHostFunctions<Base, Overlay> {
407	phantom: PhantomData<(Base, Overlay)>,
408}
409
410impl<Base, Overlay> HostFunctions for ExtendedHostFunctions<Base, Overlay>
411where
412	Base: HostFunctions,
413	Overlay: HostFunctions,
414{
415	fn host_functions() -> Vec<&'static dyn Function> {
416		let mut base = Base::host_functions();
417		let overlay = Overlay::host_functions();
418		base.retain(|host_fn| {
419			!overlay.iter().any(|ext_host_fn| host_fn.name() == ext_host_fn.name())
420		});
421		base.extend(overlay);
422		base
423	}
424
425	if_wasmtime_is_enabled! {
426		fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
427		where
428			T: HostFunctionRegistry,
429		{
430			struct Proxy<'a, T> {
431				registry: &'a mut T,
432				seen_overlay: std::collections::HashSet<String>,
433				seen_base: std::collections::HashSet<String>,
434				overlay_registered: bool,
435			}
436
437			impl<'a, T> HostFunctionRegistry for Proxy<'a, T>
438			where
439				T: HostFunctionRegistry,
440			{
441				type State = T::State;
442				type Error = T::Error;
443				type FunctionContext = T::FunctionContext;
444
445				fn with_function_context<R>(
446					caller: wasmtime::Caller<Self::State>,
447					callback: impl FnOnce(&mut dyn FunctionContext) -> R,
448				) -> R {
449					T::with_function_context(caller, callback)
450				}
451
452				fn register_static<Params, Results>(
453					&mut self,
454					fn_name: &str,
455					func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
456				) -> core::result::Result<(), Self::Error> {
457					if self.overlay_registered {
458						if !self.seen_base.insert(fn_name.to_owned()) {
459							log::warn!(
460								target: "extended_host_functions",
461								"Duplicate base host function: '{}'",
462								fn_name,
463							);
464
465							// TODO: Return an error here?
466							return Ok(())
467						}
468
469						if self.seen_overlay.contains(fn_name) {
470							// Was already registered when we went through the overlay, so just ignore it.
471							log::debug!(
472								target: "extended_host_functions",
473								"Overriding base host function: '{}'",
474								fn_name,
475							);
476
477							return Ok(())
478						}
479					} else if !self.seen_overlay.insert(fn_name.to_owned()) {
480						log::warn!(
481							target: "extended_host_functions",
482							"Duplicate overlay host function: '{}'",
483							fn_name,
484						);
485
486						// TODO: Return an error here?
487						return Ok(())
488					}
489
490					self.registry.register_static(fn_name, func)
491				}
492			}
493
494			let mut proxy = Proxy {
495				registry,
496				seen_overlay: Default::default(),
497				seen_base: Default::default(),
498				overlay_registered: false,
499			};
500
501			// The functions from the `Overlay` can override those from the `Base`,
502			// so `Overlay` is registered first, and then we skip those functions
503			// in `Base` if they were already registered from the `Overlay`.
504			Overlay::register_static(&mut proxy)?;
505			proxy.overlay_registered = true;
506			Base::register_static(&mut proxy)?;
507
508			Ok(())
509		}
510	}
511}
512
513/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
514///
515/// This trait is sealed and should not be implemented downstream.
516#[cfg(all(feature = "std", feature = "wasmtime"))]
517pub trait WasmTy: wasmtime::WasmTy + private::Sealed {}
518
519/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
520///
521/// This trait is sealed and should not be implemented downstream.
522#[cfg(not(all(feature = "std", feature = "wasmtime")))]
523pub trait WasmTy: private::Sealed {}
524
525impl WasmTy for i32 {}
526impl WasmTy for u32 {}
527impl WasmTy for i64 {}
528impl WasmTy for u64 {}
529
530/// Something that can be converted into a wasm compatible `Value`.
531pub trait IntoValue {
532	/// The type of the value in wasm.
533	const VALUE_TYPE: ValueType;
534
535	/// Convert `self` into a wasm `Value`.
536	fn into_value(self) -> Value;
537}
538
539/// Something that can may be created from a wasm `Value`.
540pub trait TryFromValue: Sized {
541	/// Try to convert the given `Value` into `Self`.
542	fn try_from_value(val: Value) -> Option<Self>;
543}
544
545macro_rules! impl_into_and_from_value {
546	(
547		$(
548			$type:ty, $( < $gen:ident >, )? $value_variant:ident,
549		)*
550	) => {
551		$(
552			impl $( <$gen> )? IntoValue for $type {
553				const VALUE_TYPE: ValueType = ValueType::$value_variant;
554				fn into_value(self) -> Value { Value::$value_variant(self as _) }
555			}
556
557			impl $( <$gen> )? TryFromValue for $type {
558				fn try_from_value(val: Value) -> Option<Self> {
559					match val {
560						Value::$value_variant(val) => Some(val as _),
561						_ => None,
562					}
563				}
564			}
565		)*
566	}
567}
568
569impl_into_and_from_value! {
570	u8, I32,
571	u16, I32,
572	u32, I32,
573	u64, I64,
574	i8, I32,
575	i16, I32,
576	i32, I32,
577	i64, I64,
578}
579
580/// Typed value that can be returned from a function.
581///
582/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
583#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
584pub enum ReturnValue {
585	/// For returning nothing.
586	Unit,
587	/// For returning some concrete value.
588	Value(Value),
589}
590
591impl From<Value> for ReturnValue {
592	fn from(v: Value) -> ReturnValue {
593		ReturnValue::Value(v)
594	}
595}
596
597impl ReturnValue {
598	/// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`.
599	///
600	/// Breakdown:
601	///  1 byte for encoding unit/value variant
602	///  1 byte for encoding value type
603	///  8 bytes for encoding the biggest value types available in wasm: f64, i64.
604	pub const ENCODED_MAX_SIZE: usize = 10;
605}
606
607#[cfg(test)]
608mod tests {
609	use super::*;
610	use codec::Encode;
611
612	#[test]
613	fn pointer_offset_works() {
614		let ptr = Pointer::<u32>::null();
615
616		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
617		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
618
619		let ptr = Pointer::<u64>::null();
620
621		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
622		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
623	}
624
625	#[test]
626	fn return_value_encoded_max_size() {
627		let encoded = ReturnValue::Value(Value::I64(-1)).encode();
628		assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
629	}
630}