Skip to main content

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, 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.
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(&mut 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(&mut 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	/// Return a type that allows the caller to spawn and run virtualization instances.
336	fn virtualization(&mut self) -> &mut dyn Virtualization;
337}
338
339/// Specifies what action to take when running a virtualization instance.
340///
341/// Used by [`Virtualization::run`] to distinguish between starting a new execution
342/// and resuming after a syscall.
343pub enum ExecAction<'a> {
344	/// Start executing the named exported function.
345	Execute(&'a str),
346	/// Resume execution after a syscall, passing back the return value.
347	///
348	/// The value is written into register `a0`.
349	Resume(u64),
350}
351
352/// The outcome of a single virtualization execution step.
353///
354/// Returned by [`Virtualization::run`].
355#[derive(Debug, PartialEq, Eq)]
356pub enum ExecOutcome {
357	/// Execution finished normally.
358	Finished {
359		/// How much gas is remaining after the execution.
360		gas_left: i64,
361	},
362	/// A syscall was encountered. The caller should handle the syscall and then
363	/// call [`Virtualization::run`] again to continue execution.
364	Syscall {
365		/// How much gas is remaining at the point of the syscall.
366		gas_left: i64,
367		/// The 4 byte identifier of the syscall.
368		syscall_no: u32,
369		/// Register arguments a0-a5.
370		a0: u64,
371		a1: u64,
372		a2: u64,
373		a3: u64,
374		a4: u64,
375		a5: u64,
376	},
377}
378
379/// Opaque handle to a virtualization instance.
380#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
381#[repr(transparent)]
382pub struct InstanceId(pub u32);
383
384/// See `sp_virtualization::Virtualization`.
385pub trait Virtualization {
386	fn instantiate(&mut self, program: &[u8]) -> Result<result::Result<InstanceId, u8>>;
387
388	/// Execute or resume a virtualization instance.
389	///
390	/// When `action` is [`ExecAction::Execute`], starts executing the named function.
391	/// When `action` is [`ExecAction::Resume`], resumes after a syscall with the given
392	/// return value.
393	///
394	/// Returns the execution outcome: either finished or a syscall was encountered.
395	fn run(
396		&mut self,
397		instance_id: InstanceId,
398		gas_left: i64,
399		action: ExecAction<'_>,
400	) -> Result<result::Result<ExecOutcome, u8>>;
401
402	fn destroy(&mut self, instance_id: InstanceId) -> Result<result::Result<(), u8>>;
403
404	fn read_memory(
405		&mut self,
406		instance_id: InstanceId,
407		offset: u32,
408		dest: &mut [u8],
409	) -> Result<result::Result<(), u8>>;
410
411	fn write_memory(
412		&mut self,
413		instance_id: InstanceId,
414		offset: u32,
415		src: &[u8],
416	) -> Result<result::Result<(), u8>>;
417}
418
419if_wasmtime_is_enabled! {
420	/// A trait used to statically register host callbacks with the WASM executor,
421	/// so that they call be called from within the runtime with minimal overhead.
422	///
423	/// This is used internally to interface the wasmtime-based executor with the
424	/// host functions' definitions generated through the runtime interface macro,
425	/// and is not meant to be used directly.
426	pub trait HostFunctionRegistry {
427		type State: 'static;
428		type Error;
429		type FunctionContext: FunctionContext;
430
431		/// Wraps the given `caller` in a type which implements `FunctionContext`
432		/// and calls the given `callback`.
433		fn with_function_context<R>(
434			caller: wasmtime::Caller<Self::State>,
435			callback: impl FnOnce(&mut dyn FunctionContext) -> R,
436		) -> R;
437
438		/// Registers a given host function with the WASM executor.
439		///
440		/// The function has to be statically callable, and all of its arguments
441		/// and its return value have to be compatible with WASM FFI.
442		fn register_static<Params, Results>(
443			&mut self,
444			fn_name: &str,
445			func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
446		) -> core::result::Result<(), Self::Error>;
447	}
448}
449
450/// Something that provides implementations for host functions.
451pub trait HostFunctions: 'static + Send + Sync {
452	/// Returns the host functions `Self` provides.
453	fn host_functions() -> Vec<&'static dyn Function>;
454
455	if_wasmtime_is_enabled! {
456		/// Statically registers the host functions.
457		fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
458		where
459			T: HostFunctionRegistry;
460	}
461}
462
463#[impl_trait_for_tuples::impl_for_tuples(30)]
464impl HostFunctions for Tuple {
465	fn host_functions() -> Vec<&'static dyn Function> {
466		let mut host_functions = Vec::new();
467
468		for_tuples!( #( host_functions.extend(Tuple::host_functions()); )* );
469
470		host_functions
471	}
472
473	#[cfg(all(feature = "std", feature = "wasmtime"))]
474	fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
475	where
476		T: HostFunctionRegistry,
477	{
478		for_tuples!(
479			#( Tuple::register_static(registry)?; )*
480		);
481
482		Ok(())
483	}
484}
485
486/// A wrapper which merges two sets of host functions, and allows the second set to override
487/// the host functions from the first set.
488pub struct ExtendedHostFunctions<Base, Overlay> {
489	phantom: PhantomData<(Base, Overlay)>,
490}
491
492impl<Base, Overlay> HostFunctions for ExtendedHostFunctions<Base, Overlay>
493where
494	Base: HostFunctions,
495	Overlay: HostFunctions,
496{
497	fn host_functions() -> Vec<&'static dyn Function> {
498		let mut base = Base::host_functions();
499		let overlay = Overlay::host_functions();
500		base.retain(|host_fn| {
501			!overlay.iter().any(|ext_host_fn| host_fn.name() == ext_host_fn.name())
502		});
503		base.extend(overlay);
504		base
505	}
506
507	if_wasmtime_is_enabled! {
508		fn register_static<T>(registry: &mut T) -> core::result::Result<(), T::Error>
509		where
510			T: HostFunctionRegistry,
511		{
512			struct Proxy<'a, T> {
513				registry: &'a mut T,
514				seen_overlay: std::collections::HashSet<String>,
515				seen_base: std::collections::HashSet<String>,
516				overlay_registered: bool,
517			}
518
519			impl<'a, T> HostFunctionRegistry for Proxy<'a, T>
520			where
521				T: HostFunctionRegistry,
522			{
523				type State = T::State;
524				type Error = T::Error;
525				type FunctionContext = T::FunctionContext;
526
527				fn with_function_context<R>(
528					caller: wasmtime::Caller<Self::State>,
529					callback: impl FnOnce(&mut dyn FunctionContext) -> R,
530				) -> R {
531					T::with_function_context(caller, callback)
532				}
533
534				fn register_static<Params, Results>(
535					&mut self,
536					fn_name: &str,
537					func: impl wasmtime::IntoFunc<Self::State, Params, Results> + 'static,
538				) -> core::result::Result<(), Self::Error> {
539					if self.overlay_registered {
540						if !self.seen_base.insert(fn_name.to_owned()) {
541							log::warn!(
542								target: "extended_host_functions",
543								"Duplicate base host function: '{}'",
544								fn_name,
545							);
546
547							// TODO: Return an error here?
548							return Ok(())
549						}
550
551						if self.seen_overlay.contains(fn_name) {
552							// Was already registered when we went through the overlay, so just ignore it.
553							log::debug!(
554								target: "extended_host_functions",
555								"Overriding base host function: '{}'",
556								fn_name,
557							);
558
559							return Ok(())
560						}
561					} else if !self.seen_overlay.insert(fn_name.to_owned()) {
562						log::warn!(
563							target: "extended_host_functions",
564							"Duplicate overlay host function: '{}'",
565							fn_name,
566						);
567
568						// TODO: Return an error here?
569						return Ok(())
570					}
571
572					self.registry.register_static(fn_name, func)
573				}
574			}
575
576			let mut proxy = Proxy {
577				registry,
578				seen_overlay: Default::default(),
579				seen_base: Default::default(),
580				overlay_registered: false,
581			};
582
583			// The functions from the `Overlay` can override those from the `Base`,
584			// so `Overlay` is registered first, and then we skip those functions
585			// in `Base` if they were already registered from the `Overlay`.
586			Overlay::register_static(&mut proxy)?;
587			proxy.overlay_registered = true;
588			Base::register_static(&mut proxy)?;
589
590			Ok(())
591		}
592	}
593}
594
595/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
596///
597/// This trait is sealed and should not be implemented downstream.
598#[cfg(all(feature = "std", feature = "wasmtime"))]
599pub trait WasmTy: wasmtime::WasmTy + private::Sealed {}
600
601/// A trait for types directly usable at the WASM FFI boundary without any conversion at all.
602///
603/// This trait is sealed and should not be implemented downstream.
604#[cfg(not(all(feature = "std", feature = "wasmtime")))]
605pub trait WasmTy: private::Sealed {}
606
607impl WasmTy for i32 {}
608impl WasmTy for u32 {}
609impl WasmTy for i64 {}
610impl WasmTy for u64 {}
611
612/// Something that can be converted into a wasm compatible `Value`.
613pub trait IntoValue {
614	/// The type of the value in wasm.
615	const VALUE_TYPE: ValueType;
616
617	/// Convert `self` into a wasm `Value`.
618	fn into_value(self) -> Value;
619}
620
621/// Something that can may be created from a wasm `Value`.
622pub trait TryFromValue: Sized {
623	/// Try to convert the given `Value` into `Self`.
624	fn try_from_value(val: Value) -> Option<Self>;
625}
626
627macro_rules! impl_into_and_from_value {
628	(
629		$(
630			$type:ty, $( < $gen:ident >, )? $value_variant:ident,
631		)*
632	) => {
633		$(
634			impl $( <$gen> )? IntoValue for $type {
635				const VALUE_TYPE: ValueType = ValueType::$value_variant;
636				fn into_value(self) -> Value { Value::$value_variant(self as _) }
637			}
638
639			impl $( <$gen> )? TryFromValue for $type {
640				fn try_from_value(val: Value) -> Option<Self> {
641					match val {
642						Value::$value_variant(val) => Some(val as _),
643						_ => None,
644					}
645				}
646			}
647		)*
648	}
649}
650
651impl_into_and_from_value! {
652	u8, I32,
653	u16, I32,
654	u32, I32,
655	u64, I64,
656	i8, I32,
657	i16, I32,
658	i32, I32,
659	i64, I64,
660}
661
662/// Typed value that can be returned from a function.
663///
664/// Basically a `TypedValue` plus `Unit`, for functions which return nothing.
665#[derive(Clone, Copy, PartialEq, codec::Encode, codec::Decode, Debug)]
666pub enum ReturnValue {
667	/// For returning nothing.
668	Unit,
669	/// For returning some concrete value.
670	Value(Value),
671}
672
673impl From<Value> for ReturnValue {
674	fn from(v: Value) -> ReturnValue {
675		ReturnValue::Value(v)
676	}
677}
678
679impl ReturnValue {
680	/// Maximum number of bytes `ReturnValue` might occupy when serialized with `SCALE`.
681	///
682	/// Breakdown:
683	///  1 byte for encoding unit/value variant
684	///  1 byte for encoding value type
685	///  8 bytes for encoding the biggest value types available in wasm: f64, i64.
686	pub const ENCODED_MAX_SIZE: usize = 10;
687}
688
689#[cfg(test)]
690mod tests {
691	use super::*;
692	use codec::Encode;
693
694	#[test]
695	fn pointer_offset_works() {
696		let ptr = Pointer::<u32>::null();
697
698		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(40));
699		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(128));
700
701		let ptr = Pointer::<u64>::null();
702
703		assert_eq!(ptr.offset(10).unwrap(), Pointer::new(80));
704		assert_eq!(ptr.offset(32).unwrap(), Pointer::new(256));
705	}
706
707	#[test]
708	fn return_value_encoded_max_size() {
709		let encoded = ReturnValue::Value(Value::I64(-1)).encode();
710		assert_eq!(encoded.len(), ReturnValue::ENCODED_MAX_SIZE);
711	}
712}