Wasm execution optimizations (#466)

* WASM execution optimizations

* Dropped min-heap-pages

* Fixed tests

* Fixed tests 2
This commit is contained in:
Arkadiy Paronyan
2018-08-01 13:29:45 +02:00
committed by Gav Wood
parent f46c770225
commit 098cfcd319
20 changed files with 97 additions and 180 deletions
+6 -6
View File
@@ -1926,7 +1926,7 @@ dependencies = [
"error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"substrate-codec 0.1.0",
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2720,7 +2720,7 @@ dependencies = [
"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)",
"wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2839,7 +2839,7 @@ dependencies = [
"substrate-serializer 0.1.0",
"twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)",
"wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -3022,7 +3022,7 @@ dependencies = [
"substrate-runtime-io 0.1.0",
"substrate-runtime-std 0.1.0",
"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)",
"wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -3780,7 +3780,7 @@ dependencies = [
[[package]]
name = "wasmi"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -4196,7 +4196,7 @@ dependencies = [
"checksum wabt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "182ae543249ccf2705f324d233891c1176fca142e137b55ba43d9dbfe93f18a2"
"checksum wabt-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca77c6b934a2b32618941b2f565aac43b8cb7141378c3b4fba4d8fcdcd57da3"
"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1"
"checksum wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3"
"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8"
"checksum websocket 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eb277e7f4c23dc49176f74ae200e77651764efb2c25f56ad2d22623b63826369"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
+1 -1
View File
@@ -114,7 +114,7 @@ pub fn run<I, T>(args: I) -> error::Result<()> where
init_logger(log_pattern);
// Create client
let executor = NativeExecutor::with_heap_pages(8, 8);
let executor = NativeExecutor::with_heap_pages(8);
let god_key = hex!["3d866ec8a9190c8343c2fc593d21d8a6d0c5c4763aaab2349de3a6111d64d124"];
let genesis_config = GenesisConfig {
+10 -10
View File
@@ -95,7 +95,7 @@ mod tests {
}
fn executor() -> ::substrate_executor::NativeExecutor<Executor> {
::substrate_executor::NativeExecutor::with_heap_pages(8, 8)
::substrate_executor::NativeExecutor::with_heap_pages(8)
}
#[test]
@@ -312,14 +312,14 @@ mod tests {
fn full_wasm_block_import_works() {
let mut t = new_test_ext();
WasmExecutor::new(8, 8).call(&mut t, COMPACT_CODE, "execute_block", &block1().0, true).0.unwrap();
WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block1().0).unwrap();
runtime_io::with_externalities(&mut t, || {
assert_eq!(Staking::voting_balance(&alice()), 41);
assert_eq!(Staking::voting_balance(&bob()), 69);
});
WasmExecutor::new(8, 8).call(&mut t, COMPACT_CODE, "execute_block", &block2().0, true).0.unwrap();
WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block2().0).unwrap();
runtime_io::with_externalities(&mut t, || {
assert_eq!(Staking::voting_balance(&alice()), 30);
@@ -331,7 +331,7 @@ mod tests {
fn wasm_big_block_import_fails() {
let mut t = new_test_ext();
let r = WasmExecutor::new(8, 8).call(&mut t, COMPACT_CODE, "execute_block", &block1big().0, true).0;
let r = WasmExecutor::new(8).call(&mut t, COMPACT_CODE, "execute_block", &block1big().0);
assert!(!r.is_ok());
}
@@ -339,7 +339,7 @@ mod tests {
fn native_big_block_import_succeeds() {
let mut t = new_test_ext();
let r = Executor::with_heap_pages(8, 8).call(&mut t, COMPACT_CODE, "execute_block", &block1big().0, true).0;
let r = Executor::with_heap_pages(8).call(&mut t, COMPACT_CODE, "execute_block", &block1big().0, true).0;
assert!(r.is_ok());
}
@@ -347,7 +347,7 @@ mod tests {
fn native_big_block_import_fails_on_fallback() {
let mut t = new_test_ext();
let r = Executor::with_heap_pages(8, 8).call(&mut t, COMPACT_CODE, "execute_block", &block1big().0, false).0;
let r = Executor::with_heap_pages(8).call(&mut t, COMPACT_CODE, "execute_block", &block1big().0, false).0;
assert!(!r.is_ok());
}
@@ -364,9 +364,9 @@ mod tests {
];
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm");
let r = WasmExecutor::new(8, 8).call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64)), true).0;
let r = WasmExecutor::new(8).call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64)));
assert!(r.is_ok());
let r = WasmExecutor::new(8, 8).call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt()), true).0.unwrap();
let r = WasmExecutor::new(8).call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap();
let r = ApplyResult::decode(&mut &r[..]).unwrap();
assert_eq!(r, Err(ApplyError::CantPay));
}
@@ -384,9 +384,9 @@ mod tests {
];
let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm");
let r = WasmExecutor::new(8, 8).call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64)), true).0;
let r = WasmExecutor::new(8).call(&mut t, &foreign_code[..], "initialise_block", &vec![].and(&from_block_number(1u64)));
assert!(r.is_ok());
let r = WasmExecutor::new(8, 8).call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt()), true).0.unwrap();
let r = WasmExecutor::new(8).call(&mut t, &foreign_code[..], "apply_extrinsic", &vec![].and(&xt())).unwrap();
let r = ApplyResult::decode(&mut &r[..]).unwrap();
assert_eq!(r, Ok(ApplyOutcome::Success));
+1 -1
View File
@@ -200,7 +200,7 @@ mod tests {
timestamp: Some(Default::default()),
};
::client::new_in_mem(LocalDispatch::with_heap_pages(8, 8), genesis_config).unwrap()
::client::new_in_mem(LocalDispatch::with_heap_pages(8), genesis_config).unwrap()
}
#[test]
+1 -1
View File
@@ -6,7 +6,7 @@ description = "Types and utilities for creating and working with parachains"
[dependencies]
substrate-codec = { path = "../../substrate/codec", default-features = false }
wasmi = { version = "0.3", optional = true }
wasmi = { version = "0.4", optional = true }
error-chain = { version = "0.12", optional = true }
[dev-dependencies]
-8
View File
@@ -95,10 +95,6 @@ args:
long: execution
value_name: STRATEGY
help: The means of execution used when calling into the runtime. Can be either wasm, native or both.
- min-heap-pages:
long: min-heap-pages
value_name: COUNT
help: The number of 64KB pages to allocate for Wasm execution initially.
- max-heap-pages:
long: max-heap-pages
value_name: COUNT
@@ -170,10 +166,6 @@ subcommands:
long: execution
value_name: STRATEGY
help: The means of execution used when calling into the runtime. Can be either wasm, native or both.
- min-heap-pages:
long: min-heap-pages
value_name: COUNT
help: The number of 64KB pages to allocate for Wasm execution initially.
- max-heap-pages:
long: max-heap-pages
value_name: COUNT
-6
View File
@@ -252,9 +252,6 @@ where
service::Roles::FULL
};
if let Some(v) = matches.value_of("min-heap-pages") {
config.min_heap_pages = v.parse().map_err(|_| "Invalid --min-heap-pages argument")?;
}
if let Some(v) = matches.value_of("max-heap-pages") {
config.max_heap_pages = v.parse().map_err(|_| "Invalid --max-heap-pages argument")?;
}
@@ -352,9 +349,6 @@ fn import_blocks<F, E>(matches: &clap::ArgMatches, spec: ChainSpec<FactoryGenesi
let mut config = service::Configuration::default_with_spec(spec);
config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into();
if let Some(v) = matches.value_of("min-heap-pages") {
config.min_heap_pages = v.parse().map_err(|_| "Invalid --min-heap-pages argument")?;
}
if let Some(v) = matches.value_of("max-heap-pages") {
config.max_heap_pages = v.parse().map_err(|_| "Invalid --max-heap-pages argument")?;
}
+5 -5
View File
@@ -44,7 +44,7 @@ mod tests {
use super::*;
use codec::{Encode, Decode, Joiner};
use keyring::Keyring;
use executor::{WasmExecutor, NativeExecutionDispatch};
use executor::NativeExecutionDispatch;
use state_machine::{execute, OverlayedChanges, ExecutionStrategy};
use state_machine::backend::InMemory;
use test_client;
@@ -55,7 +55,7 @@ mod tests {
native_executor_instance!(Executor, test_client::runtime::api::dispatch, test_client::runtime::VERSION, include_bytes!("../../test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm"));
fn executor() -> ::executor::NativeExecutor<Executor> {
NativeExecutionDispatch::with_heap_pages(8, 8)
NativeExecutionDispatch::with_heap_pages(8)
}
fn construct_block(backend: &InMemory, number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec<Transfer>) -> (Vec<u8>, Hash) {
@@ -169,10 +169,10 @@ mod tests {
let _ = execute(
&backend,
&mut overlay,
&WasmExecutor::new(8, 8),
&executor(),
"execute_block",
&b1data,
ExecutionStrategy::NativeWhenPossible,
ExecutionStrategy::AlwaysWasm,
).unwrap();
}
@@ -193,7 +193,7 @@ mod tests {
let _ = execute(
&backend,
&mut overlay,
&Executor::with_heap_pages(8, 8),
&Executor::with_heap_pages(8),
"execute_block",
&b1data,
ExecutionStrategy::NativeWhenPossible,
@@ -155,7 +155,7 @@ mod tests {
let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "authorities", &[]).unwrap().1;
// check remote execution proof locally
let local_executor = test_client::LocalExecutor::with_heap_pages(8, 8);
let local_executor = test_client::LocalExecutor::with_heap_pages(8);
do_check_execution_proof(remote_block_storage_root.into(), &local_executor, &RemoteCallRequest {
block: test_client::runtime::Hash::default(),
method: "authorities".into(),
+2 -2
View File
@@ -14,7 +14,7 @@ substrate-runtime-version = { path = "../runtime/version" }
ed25519 = { path = "../ed25519" }
serde = "1.0"
serde_derive = "1.0"
wasmi = "0.3"
wasmi = "0.4"
byteorder = "1.1"
rustc-hex = "1.0.0"
triehash = "0.1.0"
@@ -30,4 +30,4 @@ wabt = "0.4"
[features]
default = []
wasm-extern-trace = []
wasm-extern-trace = []
+1 -15
View File
@@ -75,21 +75,7 @@ error_chain! {
description("invalid memory reference"),
display("Invalid memory reference"),
}
/// Retry, please.
PleaseRetry {
description("retry needed"),
display("Retry needed"),
}
}
}
impl state_machine::Error for Error {
fn needs_retry(&self) -> bool {
if let ErrorKind::PleaseRetry = self.0 {
true
} else {
false
}
}
}
impl state_machine::Error for Error {}
@@ -28,12 +28,15 @@ use RuntimeInfo;
// For the internal Runtime Cache:
// Is it compatible enough to run this natively or do we need to fall back on the WasmModule
enum Compatibility {
InvalidVersion(WasmModule),
IsCompatible(RuntimeVersion),
IsCompatible(RuntimeVersion, WasmModule),
NotCompatible(RuntimeVersion, WasmModule)
}
unsafe impl Send for Compatibility {}
type CacheType = HashMap<u64, Compatibility>;
lazy_static! {
@@ -54,6 +57,7 @@ fn gen_cache_key(code: &[u8]) -> u64 {
/// the runtime version entry for `code`, determines whether `Compatibility::IsCompatible`
/// can be used by by comparing returned RuntimeVersion to `ref_version`
fn fetch_cached_runtime_version<'a, E: Externalities>(
wasm_executor: &WasmExecutor,
cache: &'a mut MutexGuard<CacheType>,
ext: &mut E,
code: &[u8],
@@ -62,12 +66,12 @@ fn fetch_cached_runtime_version<'a, E: Externalities>(
cache.entry(gen_cache_key(code))
.or_insert_with(|| {
let module = WasmModule::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
let version = WasmExecutor::new(8, 8).call_in_wasm_module(ext, &module, "version", &[]).ok()
let version = wasm_executor.call_in_wasm_module(ext, &module, "version", &[]).ok()
.and_then(|v| RuntimeVersion::decode(&mut v.as_slice()));
if let Some(v) = version {
if ref_version.can_call_with(&v) {
Compatibility::IsCompatible(v)
Compatibility::IsCompatible(v, module)
} else {
Compatibility::NotCompatible(v, module)
}
@@ -109,8 +113,8 @@ pub trait NativeExecutionDispatch: Send + Sync {
const VERSION: RuntimeVersion;
/// Construct corresponding `NativeExecutor` with given `heap_pages`.
fn with_heap_pages(min_heap_pages: usize, max_heap_pages: usize) -> NativeExecutor<Self> where Self: Sized {
NativeExecutor::with_heap_pages(min_heap_pages, max_heap_pages)
fn with_heap_pages(max_heap_pages: usize) -> NativeExecutor<Self> where Self: Sized {
NativeExecutor::with_heap_pages(max_heap_pages)
}
}
@@ -126,15 +130,10 @@ pub struct NativeExecutor<D: NativeExecutionDispatch> {
impl<D: NativeExecutionDispatch> NativeExecutor<D> {
/// Create new instance with specific number of pages for wasm fallback's heap.
pub fn with_heap_pages(min_heap_pages: usize, max_heap_pages: usize) -> Self {
// FIXME: set this entry at compile time
RUNTIMES_CACHE.lock().insert(
gen_cache_key(D::native_equivalent()),
Compatibility::IsCompatible(D::VERSION));
pub fn with_heap_pages(max_heap_pages: usize) -> Self {
NativeExecutor {
_dummy: Default::default(),
fallback: WasmExecutor::new(min_heap_pages, max_heap_pages),
fallback: WasmExecutor::new(max_heap_pages),
}
}
}
@@ -157,8 +156,8 @@ impl<D: NativeExecutionDispatch> RuntimeInfo for NativeExecutor<D> {
code: &[u8],
) -> Option<RuntimeVersion> {
let mut c = RUNTIMES_CACHE.lock();
match fetch_cached_runtime_version(&mut c, ext, code, D::VERSION) {
Compatibility::IsCompatible(v) | Compatibility::NotCompatible(v, _) => Some(v.clone()),
match fetch_cached_runtime_version(&self.fallback, &mut c, ext, code, D::VERSION) {
Compatibility::IsCompatible(v, _) | Compatibility::NotCompatible(v, _) => Some(v.clone()),
Compatibility::InvalidVersion(_m) => None
}
}
@@ -176,11 +175,9 @@ impl<D: NativeExecutionDispatch> CodeExecutor for NativeExecutor<D> {
use_native: bool,
) -> (Result<Vec<u8>>, bool) {
let mut c = RUNTIMES_CACHE.lock();
match (use_native, fetch_cached_runtime_version(&mut c, ext, code, D::VERSION)) {
(_, Compatibility::NotCompatible(_, m)) | (_, Compatibility::InvalidVersion(m)) =>
match (use_native, fetch_cached_runtime_version(&self.fallback, &mut c, ext, code, D::VERSION)) {
(_, Compatibility::NotCompatible(_, m)) | (_, Compatibility::InvalidVersion(m)) | (false, Compatibility::IsCompatible(_, m)) =>
(self.fallback.call_in_wasm_module(ext, m, method, data), false),
(false, _) =>
(self.fallback.call(ext, code, method, data, false).0, false),
_ => (D::dispatch(ext, method, data), true),
}
}
@@ -211,8 +208,8 @@ macro_rules! native_executor_instance {
.ok_or_else(|| $crate::error::ErrorKind::MethodNotFound(method.to_owned()).into())
}
fn with_heap_pages(min_heap_pages: usize, max_heap_pages: usize) -> $crate::NativeExecutor<$name> {
$crate::NativeExecutor::with_heap_pages(min_heap_pages, max_heap_pages)
fn with_heap_pages(max_heap_pages: usize) -> $crate::NativeExecutor<$name> {
$crate::NativeExecutor::with_heap_pages(max_heap_pages)
}
}
}
+6 -6
View File
@@ -524,7 +524,7 @@ impl Store {
#[cfg(test)]
mod tests {
use wasm_executor::WasmExecutor;
use state_machine::{TestExternalities, CodeExecutor};
use state_machine::TestExternalities;
use wabt;
#[test]
@@ -554,7 +554,7 @@ mod tests {
"#).unwrap();
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_sandbox", &code, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_sandbox", &code).unwrap(),
vec![1],
);
}
@@ -575,7 +575,7 @@ mod tests {
"#).unwrap();
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_sandbox", &code, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_sandbox", &code).unwrap(),
vec![0],
);
}
@@ -613,7 +613,7 @@ mod tests {
"#).unwrap();
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_sandbox", &code, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_sandbox", &code).unwrap(),
vec![1],
);
}
@@ -647,7 +647,7 @@ mod tests {
"#).unwrap();
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_sandbox_args", &code, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_sandbox_args", &code).unwrap(),
vec![1],
);
}
@@ -669,7 +669,7 @@ mod tests {
"#).unwrap();
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_sandbox_return_val", &code, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_sandbox_return_val", &code).unwrap(),
vec![1],
);
}
@@ -17,14 +17,13 @@
//! Rust implementation of Substrate contracts.
use std::cmp::Ordering;
use parking_lot::Mutex;
use std::collections::HashMap;
use wasmi::{
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder
Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder
};
use wasmi::RuntimeValue::{I32, I64};
use wasmi::memory_units::{Pages, Bytes};
use state_machine::{Externalities, CodeExecutor};
use state_machine::Externalities;
use error::{Error, ErrorKind, Result};
use wasm_utils::UserError;
use primitives::{blake2_256, twox_128, twox_256};
@@ -46,9 +45,8 @@ impl Heap {
/// This could mean that wasm binary specifies memory
/// limit and we are trying to allocate beyond that limit.
fn new(memory: &MemoryRef, pages: usize) -> Result<Self> {
let prev_page_count = memory
.grow(Pages(pages))
.map_err(|_| Error::from(ErrorKind::Runtime))?;
let prev_page_count = memory.initial();
memory.grow(Pages(pages)).map_err(|_| Error::from(ErrorKind::Runtime))?;
Ok(Heap {
end: Bytes::from(prev_page_count).0 as u32,
})
@@ -486,38 +484,41 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
/// Executes the provided code in a sandboxed wasm runtime.
#[derive(Debug)]
pub struct WasmExecutor {
/// The min number of pages to allocate for the heap.
pub min_heap_pages: usize,
/// The max number of pages to allocate for the heap.
pub max_heap_pages: usize,
try_heap_pages: Mutex<(usize, usize)>,
}
impl Clone for WasmExecutor {
fn clone(&self) -> Self {
WasmExecutor {
min_heap_pages: self.min_heap_pages,
max_heap_pages: self.max_heap_pages,
try_heap_pages: Mutex::new((self.min_heap_pages, 0)),
}
}
}
// Number of executions to continue with the old heap_pages before reducing to the next lowest POT.
const DECAY_TIMEOUT: usize = 16;
impl WasmExecutor {
/// Create a new instance.
pub fn new(min_heap_pages: usize, max_heap_pages: usize) -> Self {
pub fn new(max_heap_pages: usize) -> Self {
WasmExecutor {
min_heap_pages,
max_heap_pages,
try_heap_pages: Mutex::new((min_heap_pages, 0)),
}
}
/// Call a given method in the given code.
/// This should be used for tests only.
pub fn call<E: Externalities>(
&self,
ext: &mut E,
code: &[u8],
method: &str,
data: &[u8],
) -> Result<Vec<u8>> {
let module = ::wasmi::Module::from_buffer(code).expect("all modules compiled with rustc are valid wasm code; qed");
self.call_in_wasm_module(ext, &module, method, data)
}
/// Call a given method in the given wasm-module runtime.
pub fn call_in_wasm_module<E: Externalities>(
&self,
@@ -549,20 +550,14 @@ impl WasmExecutor {
.export_by_name("__indirect_function_table")
.and_then(|e| e.as_table().cloned());
let mut try_heap_pages = self.try_heap_pages.lock();
let mut fec = FunctionExecutor::new(memory.clone(), try_heap_pages.0, table, ext)?;
let mut fec = FunctionExecutor::new(memory.clone(), self.max_heap_pages, table, ext)?;
// finish instantiation by running 'start' function (if any).
let instance = intermediate_instance.run_start(&mut fec)?;
let size = data.len() as u32;
let offset = fec.heap.allocate(size);
if let Err(_) = memory.set(offset, &data) {
let old = try_heap_pages.0;
*try_heap_pages = ((old * 2).min(self.max_heap_pages), DECAY_TIMEOUT);
trace!(target: "wasm-executor", "Shrunk heap size too small at {} pages. Retrying with {}", old, try_heap_pages.0);
return Err(ErrorKind::PleaseRetry.into())
}
memory.set(offset, &data)?;
let result = instance.invoke_export(
method,
@@ -575,27 +570,12 @@ impl WasmExecutor {
let returned = match result {
Ok(x) => x,
Err(_) if try_heap_pages.0 < self.max_heap_pages => {
let old = try_heap_pages.0;
*try_heap_pages = ((old * 2).min(self.max_heap_pages), DECAY_TIMEOUT);
trace!(target: "wasm-executor", "Shrunk heap size too small at {} pages. Retrying with {}", old, try_heap_pages.0);
return Err(ErrorKind::PleaseRetry.into())
}
Err(e) => {
trace!(target: "wasm-executor", "Failed to execute code with {} pages", try_heap_pages.0);
trace!(target: "wasm-executor", "Failed to execute code with {} pages", self.max_heap_pages);
return Err(e.into())
},
};
let decay_timeout = try_heap_pages.1;
if decay_timeout == 0 {
if try_heap_pages.0 > self.min_heap_pages {
*try_heap_pages = (self.min_heap_pages.max(try_heap_pages.0 - 1), DECAY_TIMEOUT);
}
} else {
try_heap_pages.1 -= 1;
}
if let Some(I64(r)) = returned {
let offset = r as u32;
let length = (r >> 32) as u32 as usize;
@@ -607,22 +587,6 @@ impl WasmExecutor {
}
}
impl CodeExecutor for WasmExecutor {
type Error = Error;
fn call<E: Externalities>(
&self,
ext: &mut E,
code: &[u8],
method: &str,
data: &[u8],
_use_native: bool
) -> (Result<Vec<u8>>, bool) {
(Module::from_buffer(code).map_err(Into::into).and_then(|module|
self.call_in_wasm_module(ext, &module, method, data)
), false)
}
}
#[cfg(test)]
mod tests {
@@ -643,7 +607,7 @@ mod tests {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
let output = WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_empty_return", &[], true).0.unwrap();
let output = WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_empty_return", &[]).unwrap();
assert_eq!(output, vec![0u8; 0]);
}
@@ -652,10 +616,10 @@ mod tests {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
let output = WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_panic", &[], true).0;
let output = WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_panic", &[]);
assert!(output.is_err());
let output = WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_conditional_panic", &[2], true).0;
let output = WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_conditional_panic", &[2]);
assert!(output.is_err());
}
@@ -665,7 +629,7 @@ mod tests {
ext.set_storage(b"foo".to_vec(), b"bar".to_vec());
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
let output = WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_data_in", b"Hello world", true).0.unwrap();
let output = WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_data_in", b"Hello world").unwrap();
assert_eq!(output, b"all ok!".to_vec());
@@ -688,7 +652,7 @@ mod tests {
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
// This will clear all entries which prefix is "ab".
let output = WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_clear_prefix", b"ab", true).0.unwrap();
let output = WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_clear_prefix", b"ab").unwrap();
assert_eq!(output, b"all ok!".to_vec());
@@ -705,11 +669,11 @@ mod tests {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_blake2_256", &[], true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_blake2_256", &[]).unwrap(),
blake2_256(&b""[..]).encode()
);
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_blake2_256", b"Hello world!", true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_blake2_256", b"Hello world!").unwrap(),
blake2_256(&b"Hello world!"[..]).encode()
);
}
@@ -719,11 +683,11 @@ mod tests {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_twox_256", &[], true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_twox_256", &[]).unwrap(),
FromHex::from_hex("99e9d85137db46ef4bbea33613baafd56f963c64b1f3685a4eb4abd67ff6203a").unwrap()
);
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_twox_256", b"Hello world!", true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_twox_256", b"Hello world!").unwrap(),
FromHex::from_hex("b27dfd7f223f177f2a13647b533599af0c07f68bda23d96d059da2b451a35a74").unwrap()
);
}
@@ -733,11 +697,11 @@ mod tests {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_twox_128", &[], true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_twox_128", &[]).unwrap(),
FromHex::from_hex("99e9d85137db46ef4bbea33613baafd5").unwrap()
);
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_twox_128", b"Hello world!", true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_twox_128", b"Hello world!").unwrap(),
FromHex::from_hex("b27dfd7f223f177f2a13647b533599af").unwrap()
);
}
@@ -753,7 +717,7 @@ mod tests {
calldata.extend_from_slice(sig.as_ref());
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_ed25519_verify", &calldata, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_ed25519_verify", &calldata).unwrap(),
vec![1]
);
@@ -763,7 +727,7 @@ mod tests {
calldata.extend_from_slice(other_sig.as_ref());
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_ed25519_verify", &calldata, true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_ed25519_verify", &calldata).unwrap(),
vec![0]
);
}
@@ -773,7 +737,7 @@ mod tests {
let mut ext = TestExternalities::default();
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new(8, 8).call(&mut ext, &test_code[..], "test_enumerated_trie_root", &[], true).0.unwrap(),
WasmExecutor::new(8).call(&mut ext, &test_code[..], "test_enumerated_trie_root", &[]).unwrap(),
ordered_trie_root(vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()]).0.encode()
);
}
+1 -1
View File
@@ -15,7 +15,7 @@ uint = { git = "https://github.com/rphmeier/primitives.git", branch = "compile-f
twox-hash = { version = "1.1.0", optional = true }
byteorder = { version = "1.1", default_features = false }
blake2-rfc = { version = "0.2.18", optional = true }
wasmi = { version = "0.3", optional = true }
wasmi = { version = "0.4", optional = true }
[dev-dependencies]
substrate-serializer = { path = "../serializer" }
@@ -8,7 +8,7 @@ build = "build.rs"
rustc_version = "0.2"
[dependencies]
wasmi = { version = "0.3", optional = true }
wasmi = { version = "0.4", optional = true }
substrate-primitives = { path = "../primitives", default_features = false }
substrate-runtime-std = { path = "../runtime-std", default_features = false }
substrate-runtime-io = { path = "../runtime-io", default_features = false }
@@ -59,8 +59,6 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
pub name: String,
/// Execution strategy.
pub execution_strategy: ExecutionStrategy,
/// Minimum number of heap pages to allocate for Wasm execution.
pub min_heap_pages: usize,
/// Maximum number of heap pages to allocate for Wasm execution.
pub max_heap_pages: usize,
/// RPC over HTTP binding address. `None` if disabled.
@@ -90,7 +88,6 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
telemetry: Default::default(),
pruning: PruningMode::default(),
execution_strategy: ExecutionStrategy::Both,
min_heap_pages: 8,
max_heap_pages: 1024,
rpc_http: None,
rpc_ws: None,
+2 -2
View File
@@ -96,7 +96,7 @@ pub struct Service<Components: components::Components> {
pub fn new_client<Factory: components::ServiceFactory>(config: FactoryFullConfiguration<Factory>)
-> Result<Arc<ComponentClient<components::FullComponents<Factory>>>, error::Error>
{
let executor = NativeExecutor::with_heap_pages(config.min_heap_pages, config.max_heap_pages);
let executor = NativeExecutor::with_heap_pages(config.max_heap_pages);
let (client, _) = components::FullComponents::<Factory>::build_client(
&config,
executor,
@@ -118,7 +118,7 @@ impl<Components> Service<Components>
let (signal, exit) = ::exit_future::signal();
// Create client
let executor = NativeExecutor::with_heap_pages(config.min_heap_pages, config.max_heap_pages);
let executor = NativeExecutor::with_heap_pages(config.max_heap_pages);
let mut keystore = Keystore::open(config.keystore_path.as_str().into())?;
for seed in &config.keys {
+5 -18
View File
@@ -122,10 +122,7 @@ impl OverlayedChanges {
/// State Machine Error bound.
///
/// This should reflect WASM error type bound for future compatibility.
pub trait Error: 'static + fmt::Debug + fmt::Display + Send {
/// Error implies execution should be retried.
fn needs_retry(&self) -> bool { false }
}
pub trait Error: 'static + fmt::Debug + fmt::Display + Send {}
impl Error for ExecutionError {}
@@ -302,7 +299,7 @@ pub fn execute_using_consensus_failure_handler<
let result = {
let mut orig_prospective = overlay.prospective.clone();
let (result, was_native, delta) = loop {
let (result, was_native, delta) = {
let ((result, was_native), delta) = {
let mut externalities = ext::Ext::new(overlay, backend);
(
@@ -317,12 +314,7 @@ pub fn execute_using_consensus_failure_handler<
externalities.transaction()
)
};
if result.as_ref().err().map_or(false, |e| e.needs_retry()) {
overlay.prospective = orig_prospective.clone();
} else {
break (result, was_native, delta)
}
(result, was_native, delta)
};
// run wasm separately if we did run native the first time and we're meant to run both
@@ -331,7 +323,7 @@ pub fn execute_using_consensus_failure_handler<
{
overlay.prospective = orig_prospective.clone();
let (wasm_result, wasm_delta) = loop {
let (wasm_result, wasm_delta) = {
let ((result, _), delta) = {
let mut externalities = ext::Ext::new(overlay, backend);
(
@@ -345,12 +337,7 @@ pub fn execute_using_consensus_failure_handler<
externalities.transaction()
)
};
if result.as_ref().err().map_or(false, |e| e.needs_retry()) {
overlay.prospective = orig_prospective.clone();
} else {
break (result, delta)
}
(result, delta)
};
if (result.is_ok() && wasm_result.is_ok() && result.as_ref().unwrap() == wasm_result.as_ref().unwrap()/* && delta == wasm_delta*/)
@@ -39,7 +39,7 @@ pub trait TestClient {
impl TestClient for Client<Backend, Executor, runtime::Block> {
fn new_for_tests() -> Self {
client::new_in_mem(NativeExecutor::with_heap_pages(8, 8), genesis_storage()).unwrap()
client::new_in_mem(NativeExecutor::with_heap_pages(8), genesis_storage()).unwrap()
}
fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()> {