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