mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 20:17:57 +00:00
Wasm execution optimizations (#466)
* WASM execution optimizations * Dropped min-heap-pages * Fixed tests * Fixed tests 2
This commit is contained in:
committed by
Gav Wood
parent
f46c770225
commit
098cfcd319
Generated
+6
-6
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")?;
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<()> {
|
||||
|
||||
Reference in New Issue
Block a user