Make runtime api calls native when possible (#1302)

* Add simple benchmark for the runtime api

* Make the executor support native calls

* Some documentation

* Hide behind `feature = "std"`

* Rework the native calls

* Make all tests compile again

* Make every parameter using the Block serialized/deserialized in the native call

* Forward `UnwindSafe` requirement

* Remove debug stuff

* Add some documentation

* Fixes warnings

* Fixes errors after master rebase

* Fixes compilation after master rebase

* Fixes compilation after rebase
This commit is contained in:
Bastian Köcher
2019-01-21 14:32:53 +01:00
committed by Gav Wood
parent f0dbcf5401
commit 010e63116f
37 changed files with 1152 additions and 363 deletions
+63 -15
View File
@@ -21,10 +21,10 @@ use state_machine::{CodeExecutor, Externalities};
use wasm_executor::WasmExecutor;
use wasmi::{Module as WasmModule, ModuleRef as WasmModuleInstanceRef};
use runtime_version::{NativeVersion, RuntimeVersion};
use std::collections::HashMap;
use codec::Decode;
use std::{collections::HashMap, panic::UnwindSafe};
use codec::{Decode, Encode};
use RuntimeInfo;
use primitives::Blake2Hasher;
use primitives::{Blake2Hasher, NativeOrEncoded};
use primitives::storage::well_known_keys;
/// Default num of pages for the heap
@@ -95,7 +95,7 @@ fn fetch_cached_runtime_version<'a, E: Externalities<Blake2Hasher>>(
}
fn safe_call<F, U>(f: F) -> Result<U>
where F: ::std::panic::UnwindSafe + FnOnce() -> U
where F: UnwindSafe + FnOnce() -> U
{
// Substrate uses custom panic hook that terminates process on panic. Disable it for the native call.
let hook = ::std::panic::take_hook();
@@ -108,7 +108,7 @@ fn safe_call<F, U>(f: F) -> Result<U>
///
/// If the inner closure panics, it will be caught and return an error.
pub fn with_native_environment<F, U>(ext: &mut Externalities<Blake2Hasher>, f: F) -> Result<U>
where F: ::std::panic::UnwindSafe + FnOnce() -> U
where F: UnwindSafe + FnOnce() -> U
{
::runtime_io::with_externalities(ext, move || safe_call(f))
}
@@ -181,30 +181,78 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
impl<D: NativeExecutionDispatch> CodeExecutor<Blake2Hasher> for NativeExecutor<D> {
type Error = Error;
fn call<E: Externalities<Blake2Hasher>>(
fn call
<
E: Externalities<Blake2Hasher>,
R:Decode + Encode + PartialEq,
NC: FnOnce() -> R + UnwindSafe
>(
&self,
ext: &mut E,
method: &str,
data: &[u8],
use_native: bool,
) -> (Result<Vec<u8>>, bool) {
native_call: Option<NC>,
) -> (Result<NativeOrEncoded<R>>, bool) {
RUNTIMES_CACHE.with(|c| {
let mut c = c.borrow_mut();
let (module, onchain_version) = match fetch_cached_runtime_version(&self.fallback, &mut c, ext) {
Ok((module, onchain_version)) => (module, onchain_version),
Err(e) => return (Err(e), false),
};
match (use_native, onchain_version.as_ref().map_or(false, |v| v.can_call_with(&self.native_version.runtime_version))) {
(_, false) => {
trace!(target: "executor", "Request for native execution failed (native: {}, chain: {})", self.native_version.runtime_version, onchain_version.as_ref().map_or_else(||"<None>".into(), |v| format!("{}", v)));
(self.fallback.call_in_wasm_module(ext, module, method, data), false)
match (
use_native,
onchain_version
.as_ref()
.map_or(false, |v| v.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
.as_ref()
.map_or_else(||"<None>".into(), |v| format!("{}", v))
);
(
self.fallback
.call_in_wasm_module(ext, module, method, data)
.map(NativeOrEncoded::Encoded),
false
)
}
(false, _) => {
(self.fallback.call_in_wasm_module(ext, module, method, data), false)
(false, _, _) => {
(
self.fallback
.call_in_wasm_module(ext, module, method, data)
.map(NativeOrEncoded::Encoded),
false
)
}
(true, true, Some(call)) => {
trace!(
target: "executor",
"Request for native execution with native call succeeded (native: {}, chain: {}).",
self.native_version.runtime_version,
onchain_version
.as_ref()
.map_or_else(||"<None>".into(), |v| format!("{}", v))
);
(
with_native_environment(ext, move || (call)()).map(NativeOrEncoded::Native),
true
)
}
_ => {
trace!(target: "executor", "Request for native execution succeeded (native: {}, chain: {})", self.native_version.runtime_version, onchain_version.as_ref().map_or_else(||"<None>".into(), |v| format!("{}", v)));
(D::dispatch(ext, method, data), true)
trace!(
target: "executor",
"Request for native execution succeeded (native: {}, chain: {})",
self.native_version.runtime_version,
onchain_version.as_ref().map_or_else(||"<None>".into(), |v| format!("{}", v))
);
(D::dispatch(ext, method, data).map(NativeOrEncoded::Encoded), true)
}
}
})