Skip to main content

sc_executor/
executor.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::{
20	error::{Error, Result},
21	wasm_runtime::{RuntimeCache, WasmExecutionMethod},
22	RuntimeVersionOf,
23};
24
25use std::{
26	marker::PhantomData,
27	panic::{AssertUnwindSafe, UnwindSafe},
28	path::PathBuf,
29	sync::Arc,
30};
31
32use codec::Encode;
33use sc_executor_common::{
34	runtime_blob::RuntimeBlob,
35	wasm_runtime::{
36		AllocationStats, HeapAllocStrategy, WasmInstance, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY,
37	},
38};
39use sp_core::traits::{CallContext, CodeExecutor, Externalities, RuntimeCode};
40use sp_version::{GetNativeVersion, NativeVersion, RuntimeVersion};
41use sp_wasm_interface::{ExtendedHostFunctions, HostFunctions};
42
43/// Set up the externalities and safe calling environment to execute runtime calls.
44///
45/// If the inner closure panics, it will be caught and return an error.
46pub fn with_externalities_safe<F, U>(ext: &mut dyn Externalities, f: F) -> Result<U>
47where
48	F: UnwindSafe + FnOnce() -> U,
49{
50	sp_externalities::set_and_run_with_externalities(ext, move || {
51		// Substrate uses custom panic hook that terminates process on panic. Disable
52		// termination for the native call.
53		let _guard = sp_panic_handler::AbortGuard::force_unwind();
54		std::panic::catch_unwind(f).map_err(|e| {
55			if let Some(err) = e.downcast_ref::<String>() {
56				Error::RuntimePanicked(err.clone())
57			} else if let Some(err) = e.downcast_ref::<&'static str>() {
58				Error::RuntimePanicked(err.to_string())
59			} else {
60				Error::RuntimePanicked("Unknown panic".into())
61			}
62		})
63	})
64}
65
66/// Delegate for dispatching a CodeExecutor call.
67///
68/// By dispatching we mean that we execute a runtime function specified by it's name.
69pub trait NativeExecutionDispatch: Send + Sync {
70	/// Host functions for custom runtime interfaces that should be callable from within the runtime
71	/// besides the default Substrate runtime interfaces.
72	type ExtendHostFunctions: HostFunctions;
73
74	/// Dispatch a method in the runtime.
75	fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>>;
76
77	/// Provide native runtime version.
78	fn native_version() -> NativeVersion;
79}
80
81fn unwrap_heap_pages(pages: Option<HeapAllocStrategy>) -> HeapAllocStrategy {
82	pages.unwrap_or_else(|| DEFAULT_HEAP_ALLOC_STRATEGY)
83}
84
85/// Builder for creating a [`WasmExecutor`] instance.
86pub struct WasmExecutorBuilder<H = sp_io::SubstrateHostFunctions> {
87	_phantom: PhantomData<H>,
88	method: WasmExecutionMethod,
89	onchain_heap_alloc_strategy: Option<HeapAllocStrategy>,
90	offchain_heap_alloc_strategy: Option<HeapAllocStrategy>,
91	ignore_onchain_heap_pages: bool,
92	max_runtime_instances: usize,
93	cache_path: Option<PathBuf>,
94	allow_missing_host_functions: bool,
95	runtime_cache_size: u8,
96}
97
98impl<H> WasmExecutorBuilder<H> {
99	/// Create a new instance of `Self`
100	///
101	/// - `method`: The wasm execution method that should be used by the executor.
102	pub fn new() -> Self {
103		Self {
104			_phantom: PhantomData,
105			method: WasmExecutionMethod::default(),
106			onchain_heap_alloc_strategy: None,
107			offchain_heap_alloc_strategy: None,
108			ignore_onchain_heap_pages: false,
109			max_runtime_instances: 2,
110			runtime_cache_size: 4,
111			allow_missing_host_functions: false,
112			cache_path: None,
113		}
114	}
115
116	/// Create the wasm executor with execution method that should be used by the executor.
117	pub fn with_execution_method(mut self, method: WasmExecutionMethod) -> Self {
118		self.method = method;
119		self
120	}
121
122	/// Create the wasm executor with the given number of `heap_alloc_strategy` for onchain runtime
123	/// calls.
124	pub fn with_onchain_heap_alloc_strategy(
125		mut self,
126		heap_alloc_strategy: HeapAllocStrategy,
127	) -> Self {
128		self.onchain_heap_alloc_strategy = Some(heap_alloc_strategy);
129		self
130	}
131
132	/// Create the wasm executor with the given number of `heap_alloc_strategy` for offchain runtime
133	/// calls.
134	pub fn with_offchain_heap_alloc_strategy(
135		mut self,
136		heap_alloc_strategy: HeapAllocStrategy,
137	) -> Self {
138		self.offchain_heap_alloc_strategy = Some(heap_alloc_strategy);
139		self
140	}
141
142	/// Create the wasm executor and follow/ignore onchain heap pages value.
143	///
144	/// By default this the onchain heap pages value is followed.
145	pub fn with_ignore_onchain_heap_pages(mut self, ignore_onchain_heap_pages: bool) -> Self {
146		self.ignore_onchain_heap_pages = ignore_onchain_heap_pages;
147		self
148	}
149
150	/// Create the wasm executor with the given maximum number of `instances`.
151	///
152	/// The number of `instances` defines how many different instances of a runtime the cache is
153	/// storing.
154	///
155	/// By default the maximum number of `instances` is `2`.
156	pub fn with_max_runtime_instances(mut self, instances: usize) -> Self {
157		self.max_runtime_instances = instances;
158		self
159	}
160
161	/// Create the wasm executor with the given `cache_path`.
162	///
163	/// The `cache_path` is A path to a directory where the executor can place its files for
164	/// purposes of caching. This may be important in cases when there are many different modules
165	/// with the compiled execution method is used.
166	///
167	/// By default there is no `cache_path` given.
168	pub fn with_cache_path(mut self, cache_path: impl Into<PathBuf>) -> Self {
169		self.cache_path = Some(cache_path.into());
170		self
171	}
172
173	/// Create the wasm executor and allow/forbid missing host functions.
174	///
175	/// If missing host functions are forbidden, the instantiation of a wasm blob will fail
176	/// for imported host functions that the executor is not aware of. If they are allowed,
177	/// a stub is generated that will return an error when being called while executing the wasm.
178	///
179	/// By default missing host functions are forbidden.
180	pub fn with_allow_missing_host_functions(mut self, allow: bool) -> Self {
181		self.allow_missing_host_functions = allow;
182		self
183	}
184
185	/// Create the wasm executor with the given `runtime_cache_size`.
186	///
187	/// Defines the number of different runtimes/instantiated wasm blobs the cache stores.
188	/// Runtimes/wasm blobs are differentiated based on the hash and the number of heap pages.
189	///
190	/// By default this value is set to `4`.
191	pub fn with_runtime_cache_size(mut self, runtime_cache_size: u8) -> Self {
192		self.runtime_cache_size = runtime_cache_size;
193		self
194	}
195
196	/// Build the configured [`WasmExecutor`].
197	pub fn build(self) -> WasmExecutor<H> {
198		WasmExecutor {
199			method: self.method,
200			default_offchain_heap_alloc_strategy: unwrap_heap_pages(
201				self.offchain_heap_alloc_strategy,
202			),
203			default_onchain_heap_alloc_strategy: unwrap_heap_pages(
204				self.onchain_heap_alloc_strategy,
205			),
206			ignore_onchain_heap_pages: self.ignore_onchain_heap_pages,
207			cache: Arc::new(RuntimeCache::new(
208				self.max_runtime_instances,
209				self.cache_path.clone(),
210				self.runtime_cache_size,
211			)),
212			cache_path: self.cache_path,
213			allow_missing_host_functions: self.allow_missing_host_functions,
214			phantom: PhantomData,
215		}
216	}
217}
218
219/// An abstraction over Wasm code executor. Supports selecting execution backend and
220/// manages runtime cache.
221pub struct WasmExecutor<H = sp_io::SubstrateHostFunctions> {
222	/// Method used to execute fallback Wasm code.
223	method: WasmExecutionMethod,
224	/// The heap allocation strategy for onchain Wasm calls.
225	default_onchain_heap_alloc_strategy: HeapAllocStrategy,
226	/// The heap allocation strategy for offchain Wasm calls.
227	default_offchain_heap_alloc_strategy: HeapAllocStrategy,
228	/// Ignore onchain heap pages value.
229	ignore_onchain_heap_pages: bool,
230	/// WASM runtime cache.
231	cache: Arc<RuntimeCache>,
232	/// The path to a directory which the executor can leverage for a file cache, e.g. put there
233	/// compiled artifacts.
234	cache_path: Option<PathBuf>,
235	/// Ignore missing function imports.
236	allow_missing_host_functions: bool,
237	phantom: PhantomData<H>,
238}
239
240impl<H> Clone for WasmExecutor<H> {
241	fn clone(&self) -> Self {
242		Self {
243			method: self.method,
244			default_onchain_heap_alloc_strategy: self.default_onchain_heap_alloc_strategy,
245			default_offchain_heap_alloc_strategy: self.default_offchain_heap_alloc_strategy,
246			ignore_onchain_heap_pages: self.ignore_onchain_heap_pages,
247			cache: self.cache.clone(),
248			cache_path: self.cache_path.clone(),
249			allow_missing_host_functions: self.allow_missing_host_functions,
250			phantom: self.phantom,
251		}
252	}
253}
254
255impl Default for WasmExecutor<sp_io::SubstrateHostFunctions> {
256	fn default() -> Self {
257		WasmExecutorBuilder::new().build()
258	}
259}
260
261impl<H> WasmExecutor<H> {
262	/// Create new instance.
263	///
264	/// # Parameters
265	///
266	/// `method` - Method used to execute Wasm code.
267	///
268	/// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. Internally this
269	/// will be mapped as [`HeapAllocStrategy::Static`] where `default_heap_pages` represent the
270	/// static number of heap pages to allocate. Defaults to `DEFAULT_HEAP_ALLOC_STRATEGY` if `None`
271	/// is provided.
272	///
273	/// `max_runtime_instances` - The number of runtime instances to keep in memory ready for reuse.
274	///
275	/// `cache_path` - A path to a directory where the executor can place its files for purposes of
276	///   caching. This may be important in cases when there are many different modules with the
277	///   compiled execution method is used.
278	///
279	/// `runtime_cache_size` - The capacity of runtime cache.
280	#[deprecated(note = "use `Self::builder` method instead of it")]
281	pub fn new(
282		method: WasmExecutionMethod,
283		default_heap_pages: Option<u64>,
284		max_runtime_instances: usize,
285		cache_path: Option<PathBuf>,
286		runtime_cache_size: u8,
287	) -> Self {
288		WasmExecutor {
289			method,
290			default_onchain_heap_alloc_strategy: unwrap_heap_pages(
291				default_heap_pages.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }),
292			),
293			default_offchain_heap_alloc_strategy: unwrap_heap_pages(
294				default_heap_pages.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ }),
295			),
296			ignore_onchain_heap_pages: false,
297			cache: Arc::new(RuntimeCache::new(
298				max_runtime_instances,
299				cache_path.clone(),
300				runtime_cache_size,
301			)),
302			cache_path,
303			allow_missing_host_functions: false,
304			phantom: PhantomData,
305		}
306	}
307
308	/// Instantiate a builder for creating an instance of `Self`.
309	pub fn builder() -> WasmExecutorBuilder<H> {
310		WasmExecutorBuilder::new()
311	}
312
313	/// Ignore missing function imports if set true.
314	#[deprecated(note = "use `Self::builder` method instead of it")]
315	pub fn allow_missing_host_functions(&mut self, allow_missing_host_functions: bool) {
316		self.allow_missing_host_functions = allow_missing_host_functions
317	}
318}
319
320impl<H> WasmExecutor<H>
321where
322	H: HostFunctions,
323{
324	/// Execute the given closure `f` with the latest runtime (based on `runtime_code`).
325	///
326	/// The closure `f` is expected to return `Err(_)` when there happened a `panic!` in native code
327	/// while executing the runtime in Wasm. If a `panic!` occurred, the runtime is invalidated to
328	/// prevent any poisoned state. Native runtime execution does not need to report back
329	/// any `panic!`.
330	///
331	/// # Safety
332	///
333	/// `runtime` and `ext` are given as `AssertUnwindSafe` to the closure. As described above, the
334	/// runtime is invalidated on any `panic!` to prevent a poisoned state. `ext` is already
335	/// implicitly handled as unwind safe, as we store it in a global variable while executing the
336	/// native runtime.
337	pub fn with_instance<R, F>(
338		&self,
339		runtime_code: &RuntimeCode,
340		ext: &mut dyn Externalities,
341		heap_alloc_strategy: HeapAllocStrategy,
342		f: F,
343	) -> Result<R>
344	where
345		F: FnOnce(
346			AssertUnwindSafe<&dyn WasmModule>,
347			AssertUnwindSafe<&mut dyn WasmInstance>,
348			Option<&RuntimeVersion>,
349			AssertUnwindSafe<&mut dyn Externalities>,
350		) -> Result<Result<R>>,
351	{
352		match self.cache.with_instance::<H, _, _>(
353			runtime_code,
354			ext,
355			self.method,
356			heap_alloc_strategy,
357			self.allow_missing_host_functions,
358			|module, instance, version, ext| {
359				let module = AssertUnwindSafe(module);
360				let instance = AssertUnwindSafe(instance);
361				let ext = AssertUnwindSafe(ext);
362				f(module, instance, version, ext)
363			},
364		)? {
365			Ok(r) => r,
366			Err(e) => Err(e),
367		}
368	}
369
370	/// Perform a call into the given runtime.
371	///
372	/// The runtime is passed as a [`RuntimeBlob`]. The runtime will be instantiated with the
373	/// parameters this `WasmExecutor` was initialized with.
374	///
375	/// In case of problems with during creation of the runtime or instantiation, a `Err` is
376	/// returned. that describes the message.
377	#[doc(hidden)] // We use this function for tests across multiple crates.
378	pub fn uncached_call(
379		&self,
380		runtime_blob: RuntimeBlob,
381		ext: &mut dyn Externalities,
382		allow_missing_host_functions: bool,
383		export_name: &str,
384		call_data: &[u8],
385	) -> std::result::Result<Vec<u8>, Error> {
386		self.uncached_call_impl(
387			runtime_blob,
388			ext,
389			allow_missing_host_functions,
390			export_name,
391			call_data,
392			&mut None,
393		)
394	}
395
396	/// Same as `uncached_call`, except it also returns allocation statistics.
397	#[doc(hidden)] // We use this function in tests.
398	pub fn uncached_call_with_allocation_stats(
399		&self,
400		runtime_blob: RuntimeBlob,
401		ext: &mut dyn Externalities,
402		allow_missing_host_functions: bool,
403		export_name: &str,
404		call_data: &[u8],
405	) -> (std::result::Result<Vec<u8>, Error>, Option<AllocationStats>) {
406		let mut allocation_stats = None;
407		let result = self.uncached_call_impl(
408			runtime_blob,
409			ext,
410			allow_missing_host_functions,
411			export_name,
412			call_data,
413			&mut allocation_stats,
414		);
415		(result, allocation_stats)
416	}
417
418	fn uncached_call_impl(
419		&self,
420		runtime_blob: RuntimeBlob,
421		ext: &mut dyn Externalities,
422		allow_missing_host_functions: bool,
423		export_name: &str,
424		call_data: &[u8],
425		allocation_stats_out: &mut Option<AllocationStats>,
426	) -> std::result::Result<Vec<u8>, Error> {
427		let module = crate::wasm_runtime::create_wasm_runtime_with_code::<H>(
428			self.method,
429			self.default_onchain_heap_alloc_strategy,
430			runtime_blob,
431			allow_missing_host_functions,
432			self.cache_path.as_deref(),
433		)
434		.map_err(|e| format!("Failed to create module: {}", e))?;
435
436		let instance = module
437			.new_instance(self.default_onchain_heap_alloc_strategy)
438			.map_err(|e| format!("Failed to create instance: {}", e))?;
439
440		let mut instance = AssertUnwindSafe(instance);
441		let mut ext = AssertUnwindSafe(ext);
442		let mut allocation_stats_out = AssertUnwindSafe(allocation_stats_out);
443
444		with_externalities_safe(&mut **ext, move || {
445			let (result, allocation_stats) =
446				instance.call_with_allocation_stats(export_name.into(), call_data);
447			**allocation_stats_out = allocation_stats;
448			result
449		})
450		.and_then(|r| r)
451	}
452}
453
454impl<H> sp_core::traits::ReadRuntimeVersion for WasmExecutor<H>
455where
456	H: HostFunctions,
457{
458	fn read_runtime_version(
459		&self,
460		wasm_code: &[u8],
461		ext: &mut dyn Externalities,
462	) -> std::result::Result<Vec<u8>, String> {
463		let runtime_blob = RuntimeBlob::uncompress_if_needed(wasm_code)
464			.map_err(|e| format!("Failed to create runtime blob: {:?}", e))?;
465
466		if let Some(version) = crate::wasm_runtime::read_embedded_version(&runtime_blob)
467			.map_err(|e| format!("Failed to read the static section: {:?}", e))
468			.map(|v| v.map(|v| v.encode()))?
469		{
470			return Ok(version);
471		}
472
473		// If the blob didn't have embedded runtime version section, we fallback to the legacy
474		// way of fetching the version: i.e. instantiating the given instance and calling
475		// `Core_version` on it.
476
477		self.uncached_call(
478			runtime_blob,
479			ext,
480			// If a runtime upgrade introduces new host functions that are not provided by
481			// the node, we should not fail at instantiation. Otherwise nodes that are
482			// updated could run this successfully and it could lead to a storage root
483			// mismatch when importing this block.
484			true,
485			"Core_version",
486			&[],
487		)
488		.map_err(|e| e.to_string())
489	}
490}
491
492impl<H> CodeExecutor for WasmExecutor<H>
493where
494	H: HostFunctions,
495{
496	type Error = Error;
497
498	fn call(
499		&self,
500		ext: &mut dyn Externalities,
501		runtime_code: &RuntimeCode,
502		method: &str,
503		data: &[u8],
504		context: CallContext,
505	) -> (Result<Vec<u8>>, bool) {
506		tracing::trace!(
507			target: "executor",
508			%method,
509			"Executing function",
510		);
511
512		let on_chain_heap_alloc_strategy = if self.ignore_onchain_heap_pages {
513			self.default_onchain_heap_alloc_strategy
514		} else {
515			runtime_code
516				.heap_pages
517				.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ })
518				.unwrap_or_else(|| self.default_onchain_heap_alloc_strategy)
519		};
520
521		let heap_alloc_strategy = match context {
522			CallContext::Offchain => self.default_offchain_heap_alloc_strategy,
523			CallContext::Onchain { import: false } => on_chain_heap_alloc_strategy,
524			CallContext::Onchain { import: true } => on_chain_heap_alloc_strategy.double(),
525		};
526
527		let result = self.with_instance(
528			runtime_code,
529			ext,
530			heap_alloc_strategy,
531			|_, mut instance, _on_chain_version, mut ext| {
532				with_externalities_safe(&mut **ext, move || instance.call_export(method, data))
533			},
534		);
535
536		(result, false)
537	}
538}
539
540impl<H> RuntimeVersionOf for WasmExecutor<H>
541where
542	H: HostFunctions,
543{
544	fn runtime_version(
545		&self,
546		ext: &mut dyn Externalities,
547		runtime_code: &RuntimeCode,
548	) -> Result<RuntimeVersion> {
549		let on_chain_heap_pages = if self.ignore_onchain_heap_pages {
550			self.default_onchain_heap_alloc_strategy
551		} else {
552			runtime_code
553				.heap_pages
554				.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ })
555				.unwrap_or_else(|| self.default_onchain_heap_alloc_strategy)
556		};
557
558		self.with_instance(
559			runtime_code,
560			ext,
561			on_chain_heap_pages,
562			|_module, _instance, version, _ext| {
563				Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into())))
564			},
565		)
566	}
567}
568
569/// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence
570/// and dispatch to native code when possible, falling back on `WasmExecutor` when not.
571#[deprecated(
572	note = "Native execution will be deprecated, please replace with `WasmExecutor`. Will be removed at end of 2024."
573)]
574pub struct NativeElseWasmExecutor<D: NativeExecutionDispatch> {
575	/// Native runtime version info.
576	native_version: NativeVersion,
577	/// Fallback wasm executor.
578	wasm:
579		WasmExecutor<ExtendedHostFunctions<sp_io::SubstrateHostFunctions, D::ExtendHostFunctions>>,
580
581	use_native: bool,
582}
583
584#[allow(deprecated)]
585impl<D: NativeExecutionDispatch> NativeElseWasmExecutor<D> {
586	/// Create new instance.
587	///
588	/// # Parameters
589	///
590	/// `fallback_method` - Method used to execute fallback Wasm code.
591	///
592	/// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. Internally this
593	/// will be mapped as [`HeapAllocStrategy::Static`] where `default_heap_pages` represent the
594	/// static number of heap pages to allocate. Defaults to `DEFAULT_HEAP_ALLOC_STRATEGY` if `None`
595	/// is provided.
596	///
597	/// `max_runtime_instances` - The number of runtime instances to keep in memory ready for reuse.
598	///
599	/// `runtime_cache_size` - The capacity of runtime cache.
600	#[deprecated(note = "use `Self::new_with_wasm_executor` method instead of it")]
601	pub fn new(
602		fallback_method: WasmExecutionMethod,
603		default_heap_pages: Option<u64>,
604		max_runtime_instances: usize,
605		runtime_cache_size: u8,
606	) -> Self {
607		let heap_pages = default_heap_pages.map_or(DEFAULT_HEAP_ALLOC_STRATEGY, |h| {
608			HeapAllocStrategy::Static { extra_pages: h as _ }
609		});
610		let wasm = WasmExecutor::builder()
611			.with_execution_method(fallback_method)
612			.with_onchain_heap_alloc_strategy(heap_pages)
613			.with_offchain_heap_alloc_strategy(heap_pages)
614			.with_max_runtime_instances(max_runtime_instances)
615			.with_runtime_cache_size(runtime_cache_size)
616			.build();
617
618		NativeElseWasmExecutor { native_version: D::native_version(), wasm, use_native: true }
619	}
620
621	/// Create a new instance using the given [`WasmExecutor`].
622	pub fn new_with_wasm_executor(
623		executor: WasmExecutor<
624			ExtendedHostFunctions<sp_io::SubstrateHostFunctions, D::ExtendHostFunctions>,
625		>,
626	) -> Self {
627		Self { native_version: D::native_version(), wasm: executor, use_native: true }
628	}
629
630	/// Disable to use native runtime when possible just behave like `WasmExecutor`.
631	///
632	/// Default to enabled.
633	pub fn disable_use_native(&mut self) {
634		self.use_native = false;
635	}
636
637	/// Ignore missing function imports if set true.
638	#[deprecated(note = "use `Self::new_with_wasm_executor` method instead of it")]
639	pub fn allow_missing_host_functions(&mut self, allow_missing_host_functions: bool) {
640		self.wasm.allow_missing_host_functions = allow_missing_host_functions
641	}
642}
643
644#[allow(deprecated)]
645impl<D: NativeExecutionDispatch> RuntimeVersionOf for NativeElseWasmExecutor<D> {
646	fn runtime_version(
647		&self,
648		ext: &mut dyn Externalities,
649		runtime_code: &RuntimeCode,
650	) -> Result<RuntimeVersion> {
651		self.wasm.runtime_version(ext, runtime_code)
652	}
653}
654
655#[allow(deprecated)]
656impl<D: NativeExecutionDispatch> GetNativeVersion for NativeElseWasmExecutor<D> {
657	fn native_version(&self) -> &NativeVersion {
658		&self.native_version
659	}
660}
661
662#[allow(deprecated)]
663impl<D: NativeExecutionDispatch + 'static> CodeExecutor for NativeElseWasmExecutor<D> {
664	type Error = Error;
665
666	fn call(
667		&self,
668		ext: &mut dyn Externalities,
669		runtime_code: &RuntimeCode,
670		method: &str,
671		data: &[u8],
672		context: CallContext,
673	) -> (Result<Vec<u8>>, bool) {
674		let use_native = self.use_native;
675
676		tracing::trace!(
677			target: "executor",
678			function = %method,
679			"Executing function",
680		);
681
682		let on_chain_heap_alloc_strategy = if self.wasm.ignore_onchain_heap_pages {
683			self.wasm.default_onchain_heap_alloc_strategy
684		} else {
685			runtime_code
686				.heap_pages
687				.map(|h| HeapAllocStrategy::Static { extra_pages: h as _ })
688				.unwrap_or_else(|| self.wasm.default_onchain_heap_alloc_strategy)
689		};
690
691		let heap_alloc_strategy = match context {
692			CallContext::Offchain => self.wasm.default_offchain_heap_alloc_strategy,
693			CallContext::Onchain { import: false } => on_chain_heap_alloc_strategy,
694			CallContext::Onchain { import: true } => on_chain_heap_alloc_strategy.double(),
695		};
696
697		let mut used_native = false;
698		let result = self.wasm.with_instance(
699			runtime_code,
700			ext,
701			heap_alloc_strategy,
702			|_, mut instance, on_chain_version, mut ext| {
703				let on_chain_version =
704					on_chain_version.ok_or_else(|| Error::ApiError("Unknown version".into()))?;
705
706				let can_call_with =
707					on_chain_version.can_call_with(&self.native_version.runtime_version);
708
709				if use_native && can_call_with {
710					tracing::trace!(
711						target: "executor",
712						native = %self.native_version.runtime_version,
713						chain = %on_chain_version,
714						"Request for native execution succeeded",
715					);
716
717					used_native = true;
718					Ok(with_externalities_safe(&mut **ext, move || D::dispatch(method, data))?
719						.ok_or_else(|| Error::MethodNotFound(method.to_owned())))
720				} else {
721					if !can_call_with {
722						tracing::trace!(
723							target: "executor",
724							native = %self.native_version.runtime_version,
725							chain = %on_chain_version,
726							"Request for native execution failed",
727						);
728					}
729
730					with_externalities_safe(&mut **ext, move || instance.call_export(method, data))
731				}
732			},
733		);
734		(result, used_native)
735	}
736}
737
738#[allow(deprecated)]
739impl<D: NativeExecutionDispatch> Clone for NativeElseWasmExecutor<D> {
740	fn clone(&self) -> Self {
741		NativeElseWasmExecutor {
742			native_version: D::native_version(),
743			wasm: self.wasm.clone(),
744			use_native: self.use_native,
745		}
746	}
747}
748
749#[allow(deprecated)]
750impl<D: NativeExecutionDispatch> sp_core::traits::ReadRuntimeVersion for NativeElseWasmExecutor<D> {
751	fn read_runtime_version(
752		&self,
753		wasm_code: &[u8],
754		ext: &mut dyn Externalities,
755	) -> std::result::Result<Vec<u8>, String> {
756		self.wasm.read_runtime_version(wasm_code, ext)
757	}
758}
759
760#[cfg(test)]
761mod tests {
762	use super::*;
763	use sp_runtime_interface::{pass_by::PassFatPointerAndRead, runtime_interface};
764
765	#[runtime_interface]
766	trait MyInterface {
767		fn say_hello_world(data: PassFatPointerAndRead<&str>) {
768			println!("Hello world from: {}", data);
769		}
770	}
771
772	pub struct MyExecutorDispatch;
773
774	impl NativeExecutionDispatch for MyExecutorDispatch {
775		type ExtendHostFunctions = (my_interface::HostFunctions, my_interface::HostFunctions);
776
777		fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
778			substrate_test_runtime::api::dispatch(method, data)
779		}
780
781		fn native_version() -> NativeVersion {
782			substrate_test_runtime::native_version()
783		}
784	}
785
786	#[test]
787	#[allow(deprecated)]
788	fn native_executor_registers_custom_interface() {
789		let executor = NativeElseWasmExecutor::<MyExecutorDispatch>::new_with_wasm_executor(
790			WasmExecutor::builder().build(),
791		);
792
793		fn extract_host_functions<H>(
794			_: &WasmExecutor<H>,
795		) -> Vec<&'static dyn sp_wasm_interface::Function>
796		where
797			H: HostFunctions,
798		{
799			H::host_functions()
800		}
801
802		my_interface::HostFunctions::host_functions().iter().for_each(|function| {
803			assert_eq!(
804				extract_host_functions(&executor.wasm).iter().filter(|f| f == &function).count(),
805				2
806			);
807		});
808
809		my_interface::say_hello_world("hey");
810	}
811}