Skip to main content

sp_virtualization/
host_functions.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
18use crate::{DestroyError, ExecBuffer, ExecError, InstantiateError, MemoryError, EXEC_BUFFER_SIZE};
19use sp_runtime_interface::{
20	pass_by::{
21		ConvertAndReturnAs, PassFatPointerAndRead, PassFatPointerAndWrite, PassPointerAndWrite,
22	},
23	runtime_interface,
24};
25use strum::EnumCount;
26
27#[cfg(not(substrate_runtime))]
28use crate::ExecStatus;
29
30#[derive(EnumCount)]
31#[repr(i8)]
32pub enum RIInstantiateError {
33	InvalidImage = -1,
34}
35
36impl From<RIInstantiateError> for i64 {
37	fn from(error: RIInstantiateError) -> Self {
38		error as i64
39	}
40}
41
42impl TryFrom<i64> for RIInstantiateError {
43	type Error = ();
44
45	fn try_from(value: i64) -> Result<Self, Self::Error> {
46		match value {
47			-1 => Ok(RIInstantiateError::InvalidImage),
48			_ => Err(()),
49		}
50	}
51}
52
53impl From<InstantiateError> for RIInstantiateError {
54	fn from(error: InstantiateError) -> Self {
55		match error {
56			InstantiateError::InvalidImage => RIInstantiateError::InvalidImage,
57		}
58	}
59}
60
61impl From<RIInstantiateError> for InstantiateError {
62	fn from(error: RIInstantiateError) -> Self {
63		match error {
64			RIInstantiateError::InvalidImage => InstantiateError::InvalidImage,
65		}
66	}
67}
68
69#[derive(EnumCount)]
70#[repr(i8)]
71pub enum RIExecError {
72	InvalidInstance = -1,
73	InvalidImage = -2,
74	OutOfGas = -3,
75	Trap = -4,
76}
77
78impl From<RIExecError> for i64 {
79	fn from(error: RIExecError) -> Self {
80		error as i64
81	}
82}
83
84impl TryFrom<i64> for RIExecError {
85	type Error = ();
86
87	fn try_from(value: i64) -> Result<Self, Self::Error> {
88		match value {
89			-1 => Ok(RIExecError::InvalidInstance),
90			-2 => Ok(RIExecError::InvalidImage),
91			-3 => Ok(RIExecError::OutOfGas),
92			-4 => Ok(RIExecError::Trap),
93			_ => Err(()),
94		}
95	}
96}
97
98impl From<RIExecError> for ExecError {
99	fn from(error: RIExecError) -> Self {
100		match error {
101			RIExecError::InvalidInstance => ExecError::InvalidInstance,
102			RIExecError::InvalidImage => ExecError::InvalidImage,
103			RIExecError::OutOfGas => ExecError::OutOfGas,
104			RIExecError::Trap => ExecError::Trap,
105		}
106	}
107}
108
109impl From<ExecError> for RIExecError {
110	fn from(error: ExecError) -> Self {
111		match error {
112			ExecError::InvalidInstance => RIExecError::InvalidInstance,
113			ExecError::InvalidImage => RIExecError::InvalidImage,
114			ExecError::OutOfGas => RIExecError::OutOfGas,
115			ExecError::Trap => RIExecError::Trap,
116		}
117	}
118}
119
120#[derive(EnumCount)]
121#[repr(i8)]
122pub enum RIDestroyError {
123	InvalidInstance = -1,
124}
125
126impl From<RIDestroyError> for i64 {
127	fn from(error: RIDestroyError) -> Self {
128		error as i64
129	}
130}
131
132impl TryFrom<i64> for RIDestroyError {
133	type Error = ();
134
135	fn try_from(value: i64) -> Result<Self, Self::Error> {
136		match value {
137			-1 => Ok(RIDestroyError::InvalidInstance),
138			_ => Err(()),
139		}
140	}
141}
142
143impl From<RIDestroyError> for DestroyError {
144	fn from(error: RIDestroyError) -> Self {
145		match error {
146			RIDestroyError::InvalidInstance => DestroyError::InvalidInstance,
147		}
148	}
149}
150
151impl From<DestroyError> for RIDestroyError {
152	fn from(error: DestroyError) -> Self {
153		match error {
154			DestroyError::InvalidInstance => RIDestroyError::InvalidInstance,
155		}
156	}
157}
158
159#[derive(EnumCount)]
160#[repr(i8)]
161pub enum RIMemoryError {
162	InvalidInstance = -1,
163	OutOfBounds = -2,
164}
165
166impl From<RIMemoryError> for i64 {
167	fn from(error: RIMemoryError) -> Self {
168		error as i64
169	}
170}
171
172impl TryFrom<i64> for RIMemoryError {
173	type Error = ();
174
175	fn try_from(value: i64) -> Result<Self, Self::Error> {
176		match value {
177			-1 => Ok(RIMemoryError::InvalidInstance),
178			-2 => Ok(RIMemoryError::OutOfBounds),
179			_ => Err(()),
180		}
181	}
182}
183
184impl From<RIMemoryError> for MemoryError {
185	fn from(error: RIMemoryError) -> Self {
186		match error {
187			RIMemoryError::InvalidInstance => MemoryError::InvalidInstance,
188			RIMemoryError::OutOfBounds => MemoryError::OutOfBounds,
189		}
190	}
191}
192
193impl From<MemoryError> for RIMemoryError {
194	fn from(error: MemoryError) -> Self {
195		match error {
196			MemoryError::InvalidInstance => RIMemoryError::InvalidInstance,
197			MemoryError::OutOfBounds => RIMemoryError::OutOfBounds,
198		}
199	}
200}
201
202// The following code is an excerpt from RFC-145 implementation (still to be adopted)
203// ---vvv--- 8< CUT HERE 8< ---vvv---
204
205/// Used to return less-than-64-bit value passed as `i64` through the FFI boundary.
206/// Negative values are used to represent error variants.
207pub enum RIIntResult<R, E> {
208	/// Successful result
209	Ok(R),
210	/// Error result
211	Err(E),
212}
213
214impl<R, E, OR, OE> From<Result<OR, OE>> for RIIntResult<R, E>
215where
216	R: From<OR>,
217	E: From<OE>,
218{
219	fn from(result: Result<OR, OE>) -> Self {
220		match result {
221			Ok(value) => Self::Ok(value.into()),
222			Err(error) => Self::Err(error.into()),
223		}
224	}
225}
226
227impl<R, E, OR, OE> From<RIIntResult<R, E>> for Result<OR, OE>
228where
229	OR: From<R>,
230	OE: From<E>,
231{
232	fn from(result: RIIntResult<R, E>) -> Self {
233		match result {
234			RIIntResult::Ok(value) => Ok(value.into()),
235			RIIntResult::Err(error) => Err(error.into()),
236		}
237	}
238}
239
240trait IntoI64: Into<i64> {
241	const MAX: i64;
242}
243
244impl IntoI64 for u8 {
245	const MAX: i64 = u8::MAX as i64;
246}
247impl IntoI64 for u32 {
248	const MAX: i64 = u32::MAX as i64;
249}
250
251impl<R: Into<i64> + IntoI64, E: Into<i64> + strum::EnumCount> From<RIIntResult<R, E>> for i64 {
252	fn from(result: RIIntResult<R, E>) -> Self {
253		match result {
254			RIIntResult::Ok(value) => value.into(),
255			RIIntResult::Err(e) => {
256				let error_code: i64 = e.into();
257				assert!(
258					error_code < 0 && error_code >= -(E::COUNT as i64),
259					"Error variant index out of bounds"
260				);
261				error_code
262			},
263		}
264	}
265}
266
267impl<R: TryFrom<i64> + IntoI64, E: TryFrom<i64> + strum::EnumCount> TryFrom<i64>
268	for RIIntResult<R, E>
269{
270	type Error = ();
271
272	fn try_from(value: i64) -> Result<Self, Self::Error> {
273		if value >= 0 && value <= R::MAX.into() {
274			Ok(RIIntResult::Ok(value.try_into().map_err(|_| ())?))
275		} else if value < 0 && value >= -(E::COUNT as i64) {
276			Ok(RIIntResult::Err(value.try_into().map_err(|_| ())?))
277		} else {
278			Err(())
279		}
280	}
281}
282
283pub struct VoidResult;
284
285impl IntoI64 for VoidResult {
286	const MAX: i64 = 0;
287}
288
289impl From<VoidResult> for u32 {
290	fn from(_: VoidResult) -> Self {
291		0
292	}
293}
294
295impl From<u32> for VoidResult {
296	fn from(_: u32) -> Self {
297		VoidResult
298	}
299}
300
301impl From<()> for VoidResult {
302	fn from(_: ()) -> Self {
303		VoidResult
304	}
305}
306
307impl From<VoidResult> for () {
308	fn from(_: VoidResult) -> Self {
309		()
310	}
311}
312
313impl From<VoidResult> for i64 {
314	fn from(_: VoidResult) -> Self {
315		0
316	}
317}
318
319impl TryFrom<i64> for VoidResult {
320	type Error = ();
321
322	fn try_from(value: i64) -> Result<Self, Self::Error> {
323		if value == 0 {
324			Ok(VoidResult)
325		} else {
326			Err(())
327		}
328	}
329}
330
331// ---^^^--- 8< CUT HERE 8< ---^^^---
332
333/// Host functions used to spawn and call into PolkaVM instances.
334///
335/// Use [`crate::Virt`] instead of these raw host functions. This will also make sure that
336/// everything works when running the code in native (test code) as this is a `wasm_only` interface.
337///
338/// # ⚠️ Unstable — Do Not Use in Production ⚠️
339///
340/// **This interface is unstable and subject to breaking changes without notice.**
341///
342/// These host functions are **not available on Polkadot** (or any other production
343/// relay/parachain) until the API has been stabilized. If you use them in a production
344/// runtime, your runtime **will break** when the API changes.
345///
346/// Only use for local testing, development, and experimentation on test networks.
347/// There is no stability guarantee and no deprecation period.
348#[runtime_interface(wasm_only)]
349pub trait Virtualization {
350	/// See `sp_virtualization::Virt::instantiate`.
351	///
352	/// Returns the `instance_id` which needs to be passed to reference this instance
353	/// when using the other functions of this trait.
354	fn instantiate(
355		&mut self,
356		program: PassFatPointerAndRead<&[u8]>,
357	) -> ConvertAndReturnAs<Result<u32, InstantiateError>, RIIntResult<u32, RIInstantiateError>, i64>
358	{
359		use std::sync::Once;
360		static WARN_ONCE: Once = Once::new();
361		WARN_ONCE.call_once(|| {
362			log::warn!(
363				target: crate::LOG_TARGET,
364				"Virtualization host functions are UNSTABLE and subject to breaking changes. \
365				They are NOT available on Polkadot and using them in production will cause breakage. \
366				Only use for testing and experimentation.",
367			);
368		});
369		self.virtualization()
370			.instantiate(program)
371			.expect("instantiation failed")
372			.map(|id| id.0)
373			.map_err(|err| TryFrom::try_from(err).expect("Invalid error"))
374	}
375
376	/// Start execution of a function on the given instance.
377	///
378	/// Returns [`ExecStatus::Finished`] or [`ExecStatus::Syscall`] as `u8`.
379	/// When a syscall occurs, the syscall arguments are written into the
380	/// `exec_buffer` via [`PassPointerAndWrite`].
381	fn execute(
382		&mut self,
383		instance_id: u32,
384		function: PassFatPointerAndRead<&str>,
385		gas_left: i64,
386		exec_buffer: PassPointerAndWrite<&mut ExecBuffer, { EXEC_BUFFER_SIZE }>,
387	) -> ConvertAndReturnAs<Result<u8, ExecError>, RIIntResult<u8, RIExecError>, i64> {
388		let instance_id = sp_wasm_interface::InstanceId(instance_id);
389		self.virtualization()
390			.run(instance_id, gas_left, sp_wasm_interface::ExecAction::Execute(function))
391			.expect("execution failed")
392			.map(|outcome| {
393				*exec_buffer = ExecBuffer::from_outcome(&outcome);
394				ExecStatus::from_outcome(&outcome).into()
395			})
396			.map_err(|err| TryFrom::try_from(err).expect("Invalid error"))
397	}
398
399	/// Resume execution after a syscall.
400	///
401	/// Returns [`ExecStatus::Finished`] or [`ExecStatus::Syscall`] as `u8`.
402	/// When a syscall occurs, the syscall arguments are written into the
403	/// `exec_buffer` via [`PassPointerAndWrite`].
404	fn resume(
405		&mut self,
406		instance_id: u32,
407		gas_left: i64,
408		return_value: u64,
409		exec_buffer: PassPointerAndWrite<&mut ExecBuffer, { EXEC_BUFFER_SIZE }>,
410	) -> ConvertAndReturnAs<Result<u8, ExecError>, RIIntResult<u8, RIExecError>, i64> {
411		let instance_id = sp_wasm_interface::InstanceId(instance_id);
412		self.virtualization()
413			.run(instance_id, gas_left, sp_wasm_interface::ExecAction::Resume(return_value))
414			.expect("resume failed")
415			.map(|outcome| {
416				*exec_buffer = ExecBuffer::from_outcome(&outcome);
417				ExecStatus::from_outcome(&outcome).into()
418			})
419			.map_err(|err| TryFrom::try_from(err).expect("Invalid error"))
420	}
421
422	/// Destroy this instance.
423	///
424	/// Any attempt accessing an instance after destruction will yield the `InvalidInstance` error.
425	fn destroy(
426		&mut self,
427		instance_id: u32,
428	) -> ConvertAndReturnAs<Result<(), DestroyError>, RIIntResult<VoidResult, RIDestroyError>, i64>
429	{
430		let instance_id = sp_wasm_interface::InstanceId(instance_id);
431		self.virtualization()
432			.destroy(instance_id)
433			.expect("memory access error")
434			.map_err(|err| TryFrom::try_from(err).expect("Invalid error"))
435	}
436
437	/// See `sp_virtualization::Memory::read`.
438	fn read_memory(
439		&mut self,
440		instance_id: u32,
441		offset: u32,
442		dest: PassFatPointerAndWrite<&mut [u8]>,
443	) -> ConvertAndReturnAs<Result<(), MemoryError>, RIIntResult<VoidResult, RIMemoryError>, i64> {
444		let instance_id = sp_wasm_interface::InstanceId(instance_id);
445		self.virtualization()
446			.read_memory(instance_id, offset, dest)
447			.expect("memory access error")
448			.map_err(|err| TryFrom::try_from(err).expect("Invalid error"))
449	}
450
451	/// See `sp_virtualization::Memory::write`.
452	fn write_memory(
453		&mut self,
454		instance_id: u32,
455		offset: u32,
456		src: PassFatPointerAndRead<&[u8]>,
457	) -> ConvertAndReturnAs<Result<(), MemoryError>, RIIntResult<VoidResult, RIMemoryError>, i64> {
458		let instance_id = sp_wasm_interface::InstanceId(instance_id);
459		self.virtualization()
460			.write_memory(instance_id, offset, src)
461			.expect("memory access error")
462			.map_err(|err| TryFrom::try_from(err).expect("Invalid error"))
463	}
464}