referrerpolicy=no-referrer-when-downgrade

sc_executor_wasmtime/
imports.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use crate::{host::HostContext, runtime::StoreData};
20use sc_executor_common::error::WasmError;
21use sp_wasm_interface::{FunctionContext, HostFunctions};
22use std::collections::HashMap;
23use wasmtime::{ExternType, FuncType, ImportType, Linker, Module};
24
25/// Goes over all imports of a module and prepares the given linker for instantiation of the module.
26/// Returns an error if there are imports that cannot be satisfied.
27pub(crate) fn prepare_imports<H>(
28	linker: &mut Linker<StoreData>,
29	module: &Module,
30	allow_missing_func_imports: bool,
31) -> Result<(), WasmError>
32where
33	H: HostFunctions,
34{
35	let mut pending_func_imports = HashMap::new();
36	for import_ty in module.imports() {
37		let name = import_ty.name();
38
39		if import_ty.module() != "env" {
40			return Err(WasmError::Other(format!(
41				"host doesn't provide any imports from non-env module: {}:{}",
42				import_ty.module(),
43				name,
44			)))
45		}
46
47		match import_ty.ty() {
48			ExternType::Func(func_ty) => {
49				pending_func_imports.insert(name.to_owned(), (import_ty, func_ty));
50			},
51			_ =>
52				return Err(WasmError::Other(format!(
53					"host doesn't provide any non function imports: {}:{}",
54					import_ty.module(),
55					name,
56				))),
57		};
58	}
59
60	let mut registry = Registry { linker, pending_func_imports };
61	H::register_static(&mut registry)?;
62
63	if !registry.pending_func_imports.is_empty() {
64		if allow_missing_func_imports {
65			for (name, (import_ty, func_ty)) in registry.pending_func_imports {
66				let error = format!("call to a missing function {}:{}", import_ty.module(), name);
67				log::debug!("Missing import: '{}' {:?}", name, func_ty);
68				linker
69					.func_new("env", &name, func_ty.clone(), move |_, _, _| {
70					    Err(anyhow::Error::msg(error.clone()))
71					})
72					.expect("adding a missing import stub can only fail when the item already exists, and it is missing here; qed");
73			}
74		} else {
75			let mut names = Vec::new();
76			for (name, (import_ty, _)) in registry.pending_func_imports {
77				names.push(format!("'{}:{}'", import_ty.module(), name));
78			}
79			let names = names.join(", ");
80			return Err(WasmError::Other(format!(
81				"runtime requires function imports which are not present on the host: {}",
82				names
83			)))
84		}
85	}
86
87	Ok(())
88}
89
90struct Registry<'a, 'b> {
91	linker: &'a mut Linker<StoreData>,
92	pending_func_imports: HashMap<String, (ImportType<'b>, FuncType)>,
93}
94
95impl<'a, 'b> sp_wasm_interface::HostFunctionRegistry for Registry<'a, 'b> {
96	type State = StoreData;
97	type Error = WasmError;
98	type FunctionContext = HostContext<'a>;
99
100	fn with_function_context<R>(
101		caller: wasmtime::Caller<Self::State>,
102		callback: impl FnOnce(&mut dyn FunctionContext) -> R,
103	) -> R {
104		callback(&mut HostContext { caller })
105	}
106
107	fn register_static<Params, Results>(
108		&mut self,
109		fn_name: &str,
110		func: impl wasmtime::IntoFunc<Self::State, Params, Results>,
111	) -> Result<(), Self::Error> {
112		if self.pending_func_imports.remove(fn_name).is_some() {
113			self.linker.func_wrap("env", fn_name, func).map_err(|error| {
114				WasmError::Other(format!(
115					"failed to register host function '{}' with the WASM linker: {:#}",
116					fn_name, error
117				))
118			})?;
119		}
120
121		Ok(())
122	}
123}