// Copyright 2017-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . use crate::{ RuntimeInfo, error::{Error, Result}, wasm_runtime::{RuntimesCache, WasmExecutionMethod}, }; use sp_version::{NativeVersion, RuntimeVersion}; use codec::{Decode, Encode}; use sp_core::{NativeOrEncoded, traits::{CodeExecutor, Externalities}}; use log::trace; use std::{result, cell::RefCell, panic::{UnwindSafe, AssertUnwindSafe}, sync::Arc}; use sp_wasm_interface::{HostFunctions, Function}; use sc_executor_common::wasm_runtime::WasmRuntime; thread_local! { static RUNTIMES_CACHE: RefCell = RefCell::new(RuntimesCache::new()); } /// Default num of pages for the heap const DEFAULT_HEAP_PAGES: u64 = 1024; /// Set up the externalities and safe calling environment to execute runtime calls. /// /// If the inner closure panics, it will be caught and return an error. pub fn with_externalities_safe(ext: &mut dyn Externalities, f: F) -> Result where F: UnwindSafe + FnOnce() -> U { sp_externalities::set_and_run_with_externalities( ext, move || { // Substrate uses custom panic hook that terminates process on panic. Disable // termination for the native call. let _guard = sp_panic_handler::AbortGuard::force_unwind(); std::panic::catch_unwind(f).map_err(|e| { if let Some(err) = e.downcast_ref::() { Error::RuntimePanicked(err.clone()) } else if let Some(err) = e.downcast_ref::<&'static str>() { Error::RuntimePanicked(err.to_string()) } else { Error::RuntimePanicked("Unknown panic".into()) } }) }, ) } /// Delegate for dispatching a CodeExecutor call. /// /// By dispatching we mean that we execute a runtime function specified by it's name. pub trait NativeExecutionDispatch: Send + Sync { /// Host functions for custom runtime interfaces that should be callable from within the runtime /// besides the default Substrate runtime interfaces. type ExtendHostFunctions: HostFunctions; /// Dispatch a method in the runtime. /// /// If the method with the specified name doesn't exist then `Err` is returned. fn dispatch(ext: &mut dyn Externalities, method: &str, data: &[u8]) -> Result>; /// Provide native runtime version. fn native_version() -> NativeVersion; } /// A generic `CodeExecutor` implementation that uses a delegate to determine wasm code equivalence /// and dispatch to native code when possible, falling back on `WasmExecutor` when not. pub struct NativeExecutor { /// Dummy field to avoid the compiler complaining about us not using `D`. _dummy: std::marker::PhantomData, /// Method used to execute fallback Wasm code. fallback_method: WasmExecutionMethod, /// Native runtime version info. native_version: NativeVersion, /// The number of 64KB pages to allocate for Wasm execution. default_heap_pages: u64, /// The host functions registered with this instance. host_functions: Arc>, } impl NativeExecutor { /// Create new instance. /// /// # Parameters /// /// `fallback_method` - Method used to execute fallback Wasm code. /// /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. /// Defaults to `DEFAULT_HEAP_PAGES` if `None` is provided. pub fn new(fallback_method: WasmExecutionMethod, default_heap_pages: Option) -> Self { let mut host_functions = sp_io::SubstrateHostFunctions::host_functions(); // Add the old and deprecated host functions as well, so that we support old wasm runtimes. host_functions.extend( crate::deprecated_host_interface::SubstrateExternals::host_functions(), ); // Add the custom host functions provided by the user. host_functions.extend(D::ExtendHostFunctions::host_functions()); NativeExecutor { _dummy: Default::default(), fallback_method, native_version: D::native_version(), default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES), host_functions: Arc::new(host_functions), } } /// Execute the given closure `f` with the latest runtime (based on the `CODE` key in `ext`). /// /// The closure `f` is expected to return `Err(_)` when there happened a `panic!` in native code /// while executing the runtime in Wasm. If a `panic!` occurred, the runtime is invalidated to /// prevent any poisoned state. Native runtime execution does not need to report back /// any `panic!`. /// /// # Safety /// /// `runtime` and `ext` are given as `AssertUnwindSafe` to the closure. As described above, the /// runtime is invalidated on any `panic!` to prevent a poisoned state. `ext` is already /// implicitly handled as unwind safe, as we store it in a global variable while executing the /// native runtime. fn with_runtime( &self, ext: &mut E, f: impl for<'a> FnOnce( AssertUnwindSafe<&'a mut (dyn WasmRuntime + 'static)>, &'a RuntimeVersion, AssertUnwindSafe<&'a mut E>, ) -> Result>, ) -> Result where E: Externalities { RUNTIMES_CACHE.with(|cache| { let mut cache = cache.borrow_mut(); let (runtime, version, code_hash) = cache.fetch_runtime( ext, self.fallback_method, self.default_heap_pages, &*self.host_functions, )?; let runtime = AssertUnwindSafe(runtime); let ext = AssertUnwindSafe(ext); match f(runtime, version, ext) { Ok(res) => res, Err(e) => { cache.invalidate_runtime(self.fallback_method, code_hash); Err(e) } } }) } } impl Clone for NativeExecutor { fn clone(&self) -> Self { NativeExecutor { _dummy: Default::default(), fallback_method: self.fallback_method, native_version: D::native_version(), default_heap_pages: self.default_heap_pages, host_functions: self.host_functions.clone(), } } } impl RuntimeInfo for NativeExecutor { fn native_version(&self) -> &NativeVersion { &self.native_version } fn runtime_version( &self, ext: &mut E, ) -> Result { self.with_runtime(ext, |_runtime, version, _ext| Ok(Ok(version.clone()))) } } impl CodeExecutor for NativeExecutor { type Error = Error; fn call < E: Externalities, R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, >( &self, ext: &mut E, method: &str, data: &[u8], use_native: bool, native_call: Option, ) -> (Result>, bool){ let mut used_native = false; let result = self.with_runtime(ext, |mut runtime, onchain_version, mut ext| { match ( use_native, onchain_version.can_call_with(&self.native_version.runtime_version), native_call, ) { (_, false, _) => { trace!( target: "executor", "Request for native execution failed (native: {}, chain: {})", self.native_version.runtime_version, onchain_version, ); with_externalities_safe( &mut **ext, move || runtime.call(method, data).map(NativeOrEncoded::Encoded) ) } (false, _, _) => { with_externalities_safe( &mut **ext, move || runtime.call(method, data).map(NativeOrEncoded::Encoded) ) }, (true, true, Some(call)) => { trace!( target: "executor", "Request for native execution with native call succeeded (native: {}, chain: {}).", self.native_version.runtime_version, onchain_version, ); used_native = true; let res = with_externalities_safe(&mut **ext, move || (call)()) .and_then(|r| r .map(NativeOrEncoded::Native) .map_err(|s| Error::ApiError(s.to_string())) ); Ok(res) } _ => { trace!( target: "executor", "Request for native execution succeeded (native: {}, chain: {})", self.native_version.runtime_version, onchain_version ); used_native = true; Ok(D::dispatch(&mut **ext, method, data).map(NativeOrEncoded::Encoded)) } } }); (result, used_native) } } impl sp_core::traits::CallInWasm for NativeExecutor { fn call_in_wasm( &self, wasm_blob: &[u8], method: &str, call_data: &[u8], ext: &mut dyn Externalities, ) -> std::result::Result, String> { crate::call_in_wasm_with_host_functions( method, call_data, self.fallback_method, ext, wasm_blob, self.default_heap_pages, (*self.host_functions).clone(), false, ).map_err(|e| e.to_string()) } } /// Implements a `NativeExecutionDispatch` for provided parameters. /// /// # Example /// /// ``` /// sc_executor::native_executor_instance!( /// pub MyExecutor, /// substrate_test_runtime::api::dispatch, /// substrate_test_runtime::native_version, /// ); /// ``` /// /// # With custom host functions /// /// When you want to use custom runtime interfaces from within your runtime, you need to make the /// executor aware of the host functions for these interfaces. /// /// ``` /// # use sp_runtime_interface::runtime_interface; /// /// #[runtime_interface] /// trait MyInterface { /// fn say_hello_world(data: &str) { /// println!("Hello world from: {}", data); /// } /// } /// /// sc_executor::native_executor_instance!( /// pub MyExecutor, /// substrate_test_runtime::api::dispatch, /// substrate_test_runtime::native_version, /// my_interface::HostFunctions, /// ); /// ``` /// /// When you have multiple interfaces, you can give the host functions as a tuple e.g.: /// `(my_interface::HostFunctions, my_interface2::HostFunctions)` /// #[macro_export] macro_rules! native_executor_instance { ( $pub:vis $name:ident, $dispatcher:path, $version:path $(,)?) => { /// A unit struct which implements `NativeExecutionDispatch` feeding in the /// hard-coded runtime. $pub struct $name; $crate::native_executor_instance!(IMPL $name, $dispatcher, $version, ()); }; ( $pub:vis $name:ident, $dispatcher:path, $version:path, $custom_host_functions:ty $(,)?) => { /// A unit struct which implements `NativeExecutionDispatch` feeding in the /// hard-coded runtime. $pub struct $name; $crate::native_executor_instance!( IMPL $name, $dispatcher, $version, $custom_host_functions ); }; (IMPL $name:ident, $dispatcher:path, $version:path, $custom_host_functions:ty) => { impl $crate::NativeExecutionDispatch for $name { type ExtendHostFunctions = $custom_host_functions; fn dispatch( ext: &mut dyn $crate::Externalities, method: &str, data: &[u8] ) -> $crate::error::Result> { $crate::with_externalities_safe(ext, move || $dispatcher(method, data))? .ok_or_else(|| $crate::error::Error::MethodNotFound(method.to_owned())) } fn native_version() -> $crate::NativeVersion { $version() } } } } #[cfg(test)] mod tests { use super::*; use sp_runtime_interface::runtime_interface; #[runtime_interface] trait MyInterface { fn say_hello_world(data: &str) { println!("Hello world from: {}", data); } } native_executor_instance!( pub MyExecutor, substrate_test_runtime::api::dispatch, substrate_test_runtime::native_version, (my_interface::HostFunctions, my_interface::HostFunctions), ); #[test] fn native_executor_registers_custom_interface() { let executor = NativeExecutor::::new(WasmExecutionMethod::Interpreted, None); my_interface::HostFunctions::host_functions().iter().for_each(|function| { assert_eq!( executor.host_functions.iter().filter(|f| f == &function).count(), 2, ); }); } }