mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 03:41:06 +00:00
Improve Runtime execution by caching runtime lookup (#276)
* Improve Runtime execution by caching runtime lookup Cache whether the native or wasm runtime should be used for a given code and if the latter, keep around the parsed wasmi::Module for faster execution. * Additional comment and code style fixes * Fallback to WASM runtime if we can't call the version function * The previous assumption that the wasm-code was compiled with rustc might be wrong now, that the code comes from the blockchain. Added Notes about that.
This commit is contained in:
committed by
Gav Wood
parent
181e7313d2
commit
c8b4b61412
Generated
+3
@@ -2165,7 +2165,9 @@ dependencies = [
|
||||
"ed25519 0.1.0",
|
||||
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.64 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -2176,6 +2178,7 @@ dependencies = [
|
||||
"substrate-serializer 0.1.0",
|
||||
"substrate-state-machine 0.1.0",
|
||||
"triehash 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wabt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@@ -19,6 +19,9 @@ byteorder = "1.1"
|
||||
rustc-hex = "1.0.0"
|
||||
triehash = "0.1.0"
|
||||
hex-literal = "0.1.0"
|
||||
twox-hash = "1.1.0"
|
||||
lazy_static = "1.0"
|
||||
parking_lot = "*"
|
||||
log = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -41,8 +41,13 @@ extern crate wasmi;
|
||||
extern crate byteorder;
|
||||
extern crate rustc_hex;
|
||||
extern crate triehash;
|
||||
extern crate parking_lot;
|
||||
extern crate twox_hash;
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
#[macro_use]
|
||||
extern crate error_chain;
|
||||
|
||||
|
||||
@@ -17,10 +17,35 @@
|
||||
use error::{Error, ErrorKind, Result};
|
||||
use state_machine::{CodeExecutor, Externalities};
|
||||
use wasm_executor::WasmExecutor;
|
||||
use wasmi::Module as WasmModule;
|
||||
use runtime_version::RuntimeVersion;
|
||||
use std::collections::HashMap;
|
||||
use codec::Slicable;
|
||||
use twox_hash::XxHash;
|
||||
use std::hash::Hasher;
|
||||
use parking_lot::Mutex;
|
||||
use RuntimeInfo;
|
||||
|
||||
// For the internal Runtime Cache
|
||||
enum RunWith {
|
||||
NativeRuntime,
|
||||
WasmRuntime(WasmModule)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref RUNTIMES_CACHE: Mutex<HashMap<u64, RunWith>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
// helper function to generate low-over-head caching_keys
|
||||
// it is asserted that part of the audit process that any potential on-chain code change
|
||||
// will have done is to ensure that the two-x hash is different to that of any other
|
||||
// :code value from the same chain
|
||||
fn gen_cache_key(code: &[u8]) -> u64 {
|
||||
let mut h = XxHash::with_seed(0);
|
||||
h.write(code);
|
||||
h.finish()
|
||||
}
|
||||
|
||||
fn safe_call<F, U>(f: F) -> Result<U>
|
||||
where F: ::std::panic::UnwindSafe + FnOnce() -> U
|
||||
{
|
||||
@@ -60,6 +85,11 @@ pub struct NativeExecutor<D: NativeExecutionDispatch + Sync + Send> {
|
||||
impl<D: NativeExecutionDispatch + Sync + Send> NativeExecutor<D> {
|
||||
/// Create new instance.
|
||||
pub fn new() -> Self {
|
||||
// FIXME: set this entry at compile time
|
||||
RUNTIMES_CACHE.lock().insert(
|
||||
gen_cache_key(D::native_equivalent()),
|
||||
RunWith::NativeRuntime);
|
||||
|
||||
NativeExecutor {
|
||||
_dummy: Default::default(),
|
||||
}
|
||||
@@ -88,17 +118,24 @@ impl<D: NativeExecutionDispatch + Sync + Send> CodeExecutor for NativeExecutor<D
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
if code == D::native_equivalent() {
|
||||
// call native
|
||||
D::dispatch(ext, method, data)
|
||||
} else {
|
||||
let version = WasmExecutor.call(ext, code, "version", &[])?;
|
||||
let version = RuntimeVersion::decode(&mut version.as_slice());
|
||||
if version.map_or(false, |v| D::VERSION.can_call_with(&v)) {
|
||||
return D::dispatch(ext, method, data)
|
||||
}
|
||||
// call into wasm.
|
||||
WasmExecutor.call(ext, code, method, data)
|
||||
let mut cache = RUNTIMES_CACHE.lock();
|
||||
let r = cache.entry(gen_cache_key(code))
|
||||
.or_insert_with(|| {
|
||||
let wasm_module = WasmModule::from_buffer(code)
|
||||
.expect("all modules compiled with rustc are valid wasm code; qed");
|
||||
|
||||
if WasmExecutor.call_in_wasm_module(ext, &wasm_module, "version", &[]).ok()
|
||||
.and_then(|version| RuntimeVersion::decode(&mut version.as_slice()))
|
||||
.map_or(false, |v| D::VERSION.can_call_with(&v))
|
||||
{
|
||||
RunWith::NativeRuntime
|
||||
} else {
|
||||
RunWith::WasmRuntime(wasm_module)
|
||||
}
|
||||
});
|
||||
match r {
|
||||
RunWith::NativeRuntime => D::dispatch(ext, method, data),
|
||||
RunWith::WasmRuntime(module) => WasmExecutor.call_in_wasm_module(ext, &module, method, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,21 +468,19 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct WasmExecutor;
|
||||
|
||||
impl CodeExecutor for WasmExecutor {
|
||||
type Error = Error;
|
||||
impl WasmExecutor {
|
||||
|
||||
fn call<E: Externalities>(
|
||||
/// Call a given method in the given wasm-module runtime.
|
||||
pub fn call_in_wasm_module<E: Externalities>(
|
||||
&self,
|
||||
ext: &mut E,
|
||||
code: &[u8],
|
||||
module: &Module,
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
let module = Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
|
||||
|
||||
// start module instantiation. Don't run 'start' function yet.
|
||||
let intermediate_instance = ModuleInstance::new(
|
||||
&module,
|
||||
module,
|
||||
&ImportsBuilder::new()
|
||||
.with_resolver("env", FunctionExecutor::<E>::resolver())
|
||||
)?;
|
||||
@@ -492,6 +490,8 @@ impl CodeExecutor for WasmExecutor {
|
||||
let memory = intermediate_instance
|
||||
.not_started_instance()
|
||||
.export_by_name("memory")
|
||||
// TODO: with code coming from the blockchain it isn't strictly been compiled with rustc anymore.
|
||||
// these assumptions are probably not true anymore
|
||||
.expect("all modules compiled with rustc should have an export named 'memory'; qed")
|
||||
.as_memory()
|
||||
.expect("in module generated by rustc export named 'memory' should be a memory; qed")
|
||||
@@ -529,6 +529,21 @@ impl CodeExecutor for WasmExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeExecutor for WasmExecutor {
|
||||
type Error = Error;
|
||||
|
||||
fn call<E: Externalities>(
|
||||
&self,
|
||||
ext: &mut E,
|
||||
code: &[u8],
|
||||
method: &str,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>> {
|
||||
let module = Module::from_buffer(code)?;
|
||||
self.call_in_wasm_module(ext, &module, method, data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user