diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 07ee88e170..4f42fa17cd 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -713,25 +713,23 @@ checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "cranelift-bforest" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0f53d59dc9ab1c8ab68c991d8406b52b7a0aab0b15b05a3a6895579c4e5dd9" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0381a794836fb994c47006465d46d46be072483b667f36013d993b9895117fee" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "byteorder 1.3.4", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", - "gimli 0.20.0", + "gimli", "log 0.4.8", "serde", "smallvec 1.2.0", @@ -741,9 +739,8 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208c3c8d82bfef32a534c5020c6cfc3bc92f41388f1246b7bb98cf543331abaa" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -751,24 +748,21 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea048c456a517e56fd6df8f0e3947922897e6e6f61fbc5eb557a36c7b8ff6394" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" [[package]] name = "cranelift-entity" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8c7ed50812194c9e9de1fa39c77b39fc9ab48173d5e7ee88b25b6a8953e9b8" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ceb931d9f919731df1b1ecdc716b5c66384b413a7f95909d1f45441ab9bef5" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "cranelift-codegen", "log 0.4.8", @@ -778,9 +772,8 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "564ee82268bc25b914fcf331edfc2452f2d9ca34f976b187b4ca668beba250c8" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "cranelift-codegen", "raw-cpuid", @@ -789,9 +782,8 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de63e2271b374be5b07f359184e2126a08fb24d24a740cbc178b7e0107ddafa5" +version = "0.59.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -799,7 +791,7 @@ dependencies = [ "log 0.4.8", "serde", "thiserror", - "wasmparser 0.48.2", + "wasmparser", ] [[package]] @@ -1886,27 +1878,18 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162d18ae5f2e3b90a993d202f1ba17a5633c2484426f8bcae201f86194bacd00" -dependencies = [ - "arrayvec 0.4.12", - "byteorder 1.3.4", - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" dependencies = [ + "arrayvec 0.5.1", "byteorder 1.3.4", + "fallible-iterator", "indexmap", + "smallvec 1.2.0", + "stable_deref_trait", ] [[package]] @@ -3773,6 +3756,20 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea44a4fd660ab0f38434934ca0212e90fbeaaee54126ef20a3451c30c95bafae" +dependencies = [ + "flate2", + "goblin", + "parity-wasm 0.41.0", + "scroll", + "target-lexicon", + "uuid", +] + [[package]] name = "ole32-sys" version = "0.2.0" @@ -6096,11 +6093,11 @@ dependencies = [ "parity-scale-codec", "parity-wasm 0.41.0", "sc-executor-common", + "scoped-tls", "sp-allocator", "sp-core", "sp-runtime-interface", "sp-wasm-interface", - "wasmi", "wasmtime", ] @@ -6601,6 +6598,12 @@ dependencies = [ "zeroize 0.9.3", ] +[[package]] +name = "scoped-tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" + [[package]] name = "scopeguard" version = "1.1.0" @@ -8596,6 +8599,12 @@ dependencies = [ "percent-encoding 2.1.0", ] +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" + [[package]] name = "vcpkg" version = "0.2.8" @@ -8831,12 +8840,6 @@ dependencies = [ "parity-wasm 0.41.0", ] -[[package]] -name = "wasmparser" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "073da89bf1c84db000dd68ce660c1b4a08e3a2d28fd1e3394ab9e7abdde4a0f8" - [[package]] name = "wasmparser" version = "0.51.4" @@ -8845,9 +8848,8 @@ checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" [[package]] name = "wasmtime" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5614d964c3e7d07a13b59aca66103c52656bd80430f0d86dc7eeb3af4f03d4a2" +version = "0.12.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "anyhow", "backtrace", @@ -8857,9 +8859,10 @@ dependencies = [ "region", "rustc-demangle", "target-lexicon", - "wasmparser 0.51.4", + "wasmparser", "wasmtime-environ", "wasmtime-jit", + "wasmtime-profiling", "wasmtime-runtime", "wat", "winapi 0.3.8", @@ -8867,25 +8870,23 @@ dependencies = [ [[package]] name = "wasmtime-debug" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb5900275b4ef0b621ce725b9d5660b12825d7f7d79b392b97baf089ffab8c0" +version = "0.12.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "anyhow", "faerie", - "gimli 0.19.0", + "gimli", "more-asserts", "target-lexicon", "thiserror", - "wasmparser 0.51.4", + "wasmparser", "wasmtime-environ", ] [[package]] name = "wasmtime-environ" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04661851e133fb11691c4a0f92a705766b4bbf7afc06811f949e295cc8414fc" +version = "0.12.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "anyhow", "base64 0.11.0", @@ -8905,16 +8906,15 @@ dependencies = [ "sha2", "thiserror", "toml", - "wasmparser 0.51.4", + "wasmparser", "winapi 0.3.8", "zstd", ] [[package]] name = "wasmtime-jit" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d451353764ce55c9bb6a8b260063cfc209b7adadd277a9a872ab4563a69e357c" +version = "0.12.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "anyhow", "cfg-if", @@ -8927,29 +8927,46 @@ dependencies = [ "region", "target-lexicon", "thiserror", - "wasmparser 0.51.4", + "wasmparser", "wasmtime-debug", "wasmtime-environ", + "wasmtime-profiling", "wasmtime-runtime", "winapi 0.3.8", ] +[[package]] +name = "wasmtime-profiling" +version = "0.12.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" +dependencies = [ + "gimli", + "goblin", + "lazy_static", + "libc", + "object", + "scroll", + "serde", + "target-lexicon", +] + [[package]] name = "wasmtime-runtime" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dbd4fc114b828cae3e405fed413df4b3814d87a92ea029640cec9ba41f0c162" +version = "0.12.0" +source = "git+https://github.com/paritytech/wasmtime?branch=a-thread-safe-api#851887d84d03543f931f6312448d0dd5d8a9324e" dependencies = [ "backtrace", "cc", "cfg-if", "indexmap", + "lazy_static", "libc", "memoffset", "more-asserts", "region", "thiserror", "wasmtime-environ", + "wasmtime-profiling", "winapi 0.3.8", ] diff --git a/substrate/bin/node/executor/benches/bench.rs b/substrate/bin/node/executor/benches/bench.rs index 98aa75edfc..3209bb8729 100644 --- a/substrate/bin/node/executor/benches/bench.rs +++ b/substrate/bin/node/executor/benches/bench.rs @@ -91,7 +91,7 @@ fn construct_block( }; // execute the block to get the real header. - executor.call::<_, NeverNativeValue, fn() -> _>( + executor.call:: _>( ext, "Core_initialize_block", &header.encode(), @@ -100,7 +100,7 @@ fn construct_block( ).0.unwrap(); for i in extrinsics.iter() { - executor.call::<_, NeverNativeValue, fn() -> _>( + executor.call:: _>( ext, "BlockBuilder_apply_extrinsic", &i.encode(), @@ -109,7 +109,7 @@ fn construct_block( ).0.unwrap(); } - let header = match executor.call::<_, NeverNativeValue, fn() -> _>( + let header = match executor.call:: _>( ext, "BlockBuilder_finalize_block", &[0u8;0], @@ -175,7 +175,7 @@ fn bench_execute_block(c: &mut Criterion) { || new_test_ext(&genesis_config), |test_ext| { for block in blocks.iter() { - executor.call::<_, NeverNativeValue, fn() -> _>( + executor.call:: _>( &mut test_ext.ext(), "Core_execute_block", &block.0, diff --git a/substrate/bin/node/executor/tests/common.rs b/substrate/bin/node/executor/tests/common.rs index a39acbb42b..34f3034208 100644 --- a/substrate/bin/node/executor/tests/common.rs +++ b/substrate/bin/node/executor/tests/common.rs @@ -71,7 +71,7 @@ pub fn executor_call< native_call: Option, ) -> (Result>, bool) { let mut t = t.ext(); - executor().call::<_, R, NC>( + executor().call::( &mut t, method, data, diff --git a/substrate/client/executor/common/src/wasm_runtime.rs b/substrate/client/executor/common/src/wasm_runtime.rs index 7af6c2bd53..b59ca8ba93 100644 --- a/substrate/client/executor/common/src/wasm_runtime.rs +++ b/substrate/client/executor/common/src/wasm_runtime.rs @@ -17,18 +17,25 @@ //! Definitions for a wasm runtime. use crate::error::Error; -use sp_wasm_interface::{Function, Value}; +use sp_wasm_interface::Value; -/// A trait that defines an abstract wasm runtime. +/// A trait that defines an abstract WASM runtime module. /// /// This can be implemented by an execution engine. -pub trait WasmRuntime { - /// Return the host functions that are registered for this Wasm runtime. - fn host_functions(&self) -> &[&'static dyn Function]; +pub trait WasmModule: Sync + Send { + /// Create a new instance. + fn new_instance(&self) -> Result, Error>; +} - /// Call a method in the Substrate runtime by name. Returns the encoded result on success. - fn call(&mut self, method: &str, data: &[u8]) -> Result, Error>; +/// A trait that defines an abstract wasm module instance. +/// +/// This can be implemented by an execution engine. +pub trait WasmInstance: Send { + /// Call a method on this WASM instance and reset it afterwards. + /// Returns the encoded result on success. + fn call(&self, method: &str, data: &[u8]) -> Result, Error>; /// Get the value from a global with the given `name`. - fn get_global_val(&self, name: &str) -> Result, Error>; + /// This method is only suitable for getting immutable globals. + fn get_global_const(&self, name: &str) -> Result, Error>; } diff --git a/substrate/client/executor/src/integration_tests/mod.rs b/substrate/client/executor/src/integration_tests/mod.rs index 87ca33c4da..e787b229ec 100644 --- a/substrate/client/executor/src/integration_tests/mod.rs +++ b/substrate/client/executor/src/integration_tests/mod.rs @@ -21,7 +21,7 @@ use hex_literal::hex; use sp_core::{ blake2_128, blake2_256, ed25519, sr25519, map, Pair, offchain::{OffchainExt, testing}, - traits::Externalities, + traits::{Externalities, CallInWasm}, }; use sc_runtime_test::WASM_BINARY; use sp_state_machine::TestExternalities as CoreTestExternalities; @@ -40,15 +40,18 @@ fn call_in_wasm( call_data: &[u8], execution_method: WasmExecutionMethod, ext: &mut E, -) -> crate::error::Result> { - crate::call_in_wasm::( +) -> Result, String> { + let executor = crate::WasmExecutor::new( + execution_method, + Some(1024), + HostFunctions::host_functions(), + true, + ); + executor.call_in_wasm( + &WASM_BINARY[..], function, call_data, - execution_method, ext, - &WASM_BINARY[..], - 1024, - true, ) } @@ -84,12 +87,12 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) { match wasm_method { WasmExecutionMethod::Interpreted => assert_eq!( &format!("{:?}", e), - "Wasmi(Trap(Trap { kind: Host(Other(\"Function `missing_external` is only a stub. Calling a stub is not allowed.\")) }))" + "\"Trap: Trap { kind: Host(Other(\\\"Function `missing_external` is only a stub. Calling a stub is not allowed.\\\")) }\"" ), #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => assert_eq!( &format!("{:?}", e), - "Other(\"Wasm execution trapped: call to a missing function env:missing_external\")" + "\"Wasm execution trapped: call to a missing function env:missing_external\"" ), } } @@ -113,12 +116,12 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) { match wasm_method { WasmExecutionMethod::Interpreted => assert_eq!( &format!("{:?}", e), - "Wasmi(Trap(Trap { kind: Host(Other(\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\")) }))" + "\"Trap: Trap { kind: Host(Other(\\\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\\\")) }\"" ), #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => assert_eq!( &format!("{:?}", e), - "Other(\"Wasm execution trapped: call to a missing function env:yet_another_missing_external\")" + "\"Wasm execution trapped: call to a missing function env:yet_another_missing_external\"" ), } } @@ -502,29 +505,32 @@ fn offchain_http_should_work(wasm_method: WasmExecutionMethod) { fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); - crate::call_in_wasm::( + let executor = crate::WasmExecutor::new( + wasm_method, + Some(17), // `17` is the initial number of pages compiled into the binary. + HostFunctions::host_functions(), + true, + ); + executor.call_in_wasm( + &WASM_BINARY[..], "test_exhaust_heap", &[0], - wasm_method, &mut ext.ext(), - &WASM_BINARY[..], - // `17` is the initial number of pages compiled into the binary. - 17, - true, ).unwrap(); } #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] fn returns_mutable_static(wasm_method: WasmExecutionMethod) { - let mut instance = crate::wasm_runtime::create_wasm_runtime_with_code( + let runtime = crate::wasm_runtime::create_wasm_runtime_with_code( wasm_method, 1024, &WASM_BINARY[..], HostFunctions::host_functions(), true, - ).expect("Creates instance"); + ).expect("Creates runtime"); + let instance = runtime.new_instance().unwrap(); let res = instance.call("returns_mutable_static", &[0]).unwrap(); assert_eq!(33, u64::decode(&mut &res[..]).unwrap()); @@ -550,13 +556,14 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) { // to our allocator algorithm there are inefficiencies. const REQUIRED_MEMORY_PAGES: u64 = 32; - let mut instance = crate::wasm_runtime::create_wasm_runtime_with_code( + let runtime = crate::wasm_runtime::create_wasm_runtime_with_code( wasm_method, REQUIRED_MEMORY_PAGES, &WASM_BINARY[..], HostFunctions::host_functions(), true, - ).expect("Creates instance"); + ).expect("Creates runtime"); + let instance = runtime.new_instance().unwrap(); // On the first invocation we allocate approx. 768KB (75%) of stack and then trap. let res = instance.call("allocates_huge_stack_array", &true.encode()); @@ -569,15 +576,16 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) { #[test_case(WasmExecutionMethod::Interpreted)] fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) { - let mut instance = crate::wasm_runtime::create_wasm_runtime_with_code( + let runtime = crate::wasm_runtime::create_wasm_runtime_with_code( wasm_method, 1024, &WASM_BINARY[..], HostFunctions::host_functions(), true, - ).expect("Creates instance"); + ).expect("Creates runtime"); + let instance = runtime.new_instance().unwrap(); - let heap_base = instance.get_global_val("__heap_base") + let heap_base = instance.get_global_const("__heap_base") .expect("`__heap_base` is valid") .expect("`__heap_base` exists") .as_i32() diff --git a/substrate/client/executor/src/lib.rs b/substrate/client/executor/src/lib.rs index af53ed9183..1c21026fd8 100644 --- a/substrate/client/executor/src/lib.rs +++ b/substrate/client/executor/src/lib.rs @@ -36,82 +36,24 @@ mod wasm_runtime; mod integration_tests; pub use wasmi; -pub use native_executor::{with_externalities_safe, NativeExecutor, NativeExecutionDispatch}; +pub use native_executor::{with_externalities_safe, NativeExecutor, WasmExecutor, NativeExecutionDispatch}; pub use sp_version::{RuntimeVersion, NativeVersion}; pub use codec::Codec; #[doc(hidden)] -pub use sp_core::traits::Externalities; +pub use sp_core::traits::{Externalities, CallInWasm}; #[doc(hidden)] pub use sp_wasm_interface; pub use wasm_runtime::WasmExecutionMethod; pub use sc_executor_common::{error, sandbox}; -/// Call the given `function` in the given wasm `code`. -/// -/// The signature of `function` needs to follow the default Substrate function signature. -/// -/// - `call_data`: Will be given as input parameters to `function` -/// - `execution_method`: The execution method to use. -/// - `ext`: The externalities that should be set while executing the wasm function. -/// If `None` is given, no externalities will be set. -/// - `heap_pages`: The number of heap pages to allocate. -/// -/// Returns the `Vec` that contains the return value of the function. -pub fn call_in_wasm( - function: &str, - call_data: &[u8], - execution_method: WasmExecutionMethod, - ext: &mut dyn Externalities, - code: &[u8], - heap_pages: u64, - allow_missing_func_imports: bool, -) -> error::Result> { - call_in_wasm_with_host_functions( - function, - call_data, - execution_method, - ext, - code, - heap_pages, - HF::host_functions(), - allow_missing_func_imports, - ) -} - -/// Non-generic version of [`call_in_wasm`] that takes the `host_functions` as parameter. -/// For more information please see [`call_in_wasm`]. -pub fn call_in_wasm_with_host_functions( - function: &str, - call_data: &[u8], - execution_method: WasmExecutionMethod, - ext: &mut dyn Externalities, - code: &[u8], - heap_pages: u64, - host_functions: Vec<&'static dyn sp_wasm_interface::Function>, - allow_missing_func_imports: bool, -) -> error::Result> { - let instance = wasm_runtime::create_wasm_runtime_with_code( - execution_method, - heap_pages, - code, - host_functions, - allow_missing_func_imports, - )?; - - // It is safe, as we delete the instance afterwards. - let mut instance = std::panic::AssertUnwindSafe(instance); - - with_externalities_safe(ext, move || instance.call(function, call_data)).and_then(|r| r) -} - /// Provides runtime information. pub trait RuntimeInfo { /// Native runtime information. fn native_version(&self) -> &NativeVersion; /// Extract RuntimeVersion of given :code block - fn runtime_version (&self, ext: &mut E) -> error::Result; + fn runtime_version(&self, ext: &mut dyn Externalities) -> error::Result; } #[cfg(test)] @@ -119,19 +61,25 @@ mod tests { use super::*; use sc_runtime_test::WASM_BINARY; use sp_io::TestExternalities; + use sp_wasm_interface::HostFunctions; + use sp_core::traits::CallInWasm; #[test] fn call_in_interpreted_wasm_works() { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let res = call_in_wasm::( + + let executor = WasmExecutor::new( + WasmExecutionMethod::Interpreted, + Some(8), + sp_io::SubstrateHostFunctions::host_functions(), + true, + ); + let res = executor.call_in_wasm( + &WASM_BINARY[..], "test_empty_return", &[], - WasmExecutionMethod::Interpreted, &mut ext, - &WASM_BINARY, - 8, - true, ).unwrap(); assert_eq!(res, vec![0u8; 0]); } diff --git a/substrate/client/executor/src/native_executor.rs b/substrate/client/executor/src/native_executor.rs index 1364b753db..dfc88d2ede 100644 --- a/substrate/client/executor/src/native_executor.rs +++ b/substrate/client/executor/src/native_executor.rs @@ -16,19 +16,15 @@ use crate::{ RuntimeInfo, error::{Error, Result}, - wasm_runtime::{RuntimesCache, WasmExecutionMethod}, + wasm_runtime::{RuntimeCache, WasmExecutionMethod, CodeSource}, }; 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 std::{result, 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()); -} +use sc_executor_common::wasm_runtime::WasmInstance; /// Default num of pages for the heap const DEFAULT_HEAP_PAGES: u64 = 1024; @@ -75,42 +71,43 @@ pub trait NativeExecutionDispatch: Send + Sync { 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, +/// An abstraction over Wasm code executor. Supports selecting execution backend and +/// manages runtime cache. +#[derive(Clone)] +pub struct WasmExecutor { /// Method used to execute fallback Wasm code. - fallback_method: WasmExecutionMethod, - /// Native runtime version info. - native_version: NativeVersion, + method: WasmExecutionMethod, /// The number of 64KB pages to allocate for Wasm execution. default_heap_pages: u64, /// The host functions registered with this instance. host_functions: Arc>, + /// WASM runtime cache. + cache: Arc, + /// Allow missing function imports. + allow_missing_func_imports: bool, } -impl NativeExecutor { +impl WasmExecutor { /// Create new instance. /// /// # Parameters /// - /// `fallback_method` - Method used to execute fallback Wasm code. + /// `method` - Method used to execute 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 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(), + pub fn new( + method: WasmExecutionMethod, + default_heap_pages: Option, + host_functions: Vec<&'static dyn Function>, + allow_missing_func_imports: bool, + ) -> Self { + WasmExecutor { + method, default_heap_pages: default_heap_pages.unwrap_or(DEFAULT_HEAP_PAGES), host_functions: Arc::new(host_functions), + cache: Arc::new(RuntimeCache::new()), + allow_missing_func_imports, } } @@ -127,46 +124,90 @@ impl NativeExecutor { /// 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( + fn with_instance<'c, R, F>( &self, - ext: &mut E, - f: impl for<'a> FnOnce( - AssertUnwindSafe<&'a mut (dyn WasmRuntime + 'static)>, - &'a RuntimeVersion, - AssertUnwindSafe<&'a mut E>, + code: CodeSource<'c>, + ext: &mut dyn Externalities, + f: F, + ) -> Result + where F: FnOnce( + AssertUnwindSafe<&dyn WasmInstance>, + Option<&RuntimeVersion>, + AssertUnwindSafe<&mut dyn Externalities>, ) -> 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) - } + { + match self.cache.with_instance( + code, + ext, + self.method, + self.default_heap_pages, + &*self.host_functions, + self.allow_missing_func_imports, + |instance, version, ext| { + let instance = AssertUnwindSafe(instance); + let ext = AssertUnwindSafe(ext); + f(instance, version, ext) } - }) + )? { + Ok(r) => r, + Err(e) => Err(e), + } } } -impl Clone for NativeExecutor { - fn clone(&self) -> Self { +impl sp_core::traits::CallInWasm for WasmExecutor { + fn call_in_wasm( + &self, + wasm_blob: &[u8], + method: &str, + call_data: &[u8], + ext: &mut dyn Externalities, + ) -> std::result::Result, String> { + self.with_instance(CodeSource::Custom(wasm_blob), ext, |instance, _, mut ext| { + with_externalities_safe( + &mut **ext, + move || instance.call(method, call_data), + ) + }).map_err(|e| e.to_string()) + } +} + +/// 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, + /// Native runtime version info. + native_version: NativeVersion, + /// Fallback wasm executor. + wasm: WasmExecutor, +} + +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 custom host functions provided by the user. + host_functions.extend(D::ExtendHostFunctions::host_functions()); + let wasm_executor = WasmExecutor::new( + fallback_method, + default_heap_pages, + host_functions, + false, + ); + 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(), + wasm: wasm_executor, } } } @@ -176,90 +217,109 @@ impl RuntimeInfo for NativeExecutor { &self.native_version } - fn runtime_version( + fn runtime_version( &self, - ext: &mut E, + ext: &mut dyn Externalities, ) -> Result { - self.with_runtime(ext, |_runtime, version, _ext| Ok(Ok(version.clone()))) + self.wasm.with_instance(CodeSource::Externalities, ext, + |_instance, version, _ext| + Ok(version.cloned().ok_or_else(|| Error::ApiError("Unknown version".into()))) + ) } } impl CodeExecutor for NativeExecutor { type Error = Error; - fn call - < - E: Externalities, + fn call< R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, >( &self, - ext: &mut E, + ext: &mut dyn Externalities, method: &str, data: &[u8], use_native: bool, native_call: Option, - ) -> (Result>, bool){ + ) -> (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())) + let result = self.wasm.with_instance( + CodeSource::Externalities, + ext, + |instance, onchain_version, mut ext| { + let onchain_version = onchain_version.ok_or_else( + || Error::ApiError("Unknown version".into()) + )?; + 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, ); - Ok(res) - } - _ => { - trace!( - target: "executor", - "Request for native execution succeeded (native: {}, chain: {})", - self.native_version.runtime_version, - onchain_version - ); + with_externalities_safe( + &mut **ext, + move || instance.call(method, data).map(NativeOrEncoded::Encoded) + ) + } + (false, _, _) => { + with_externalities_safe( + &mut **ext, + move || instance.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; - Ok(D::dispatch(&mut **ext, method, data).map(NativeOrEncoded::Encoded)) + 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 Clone for NativeExecutor { + fn clone(&self) -> Self { + NativeExecutor { + _dummy: Default::default(), + native_version: D::native_version(), + wasm: self.wasm.clone(), + } + } +} + impl sp_core::traits::CallInWasm for NativeExecutor { fn call_in_wasm( &self, @@ -268,16 +328,7 @@ impl sp_core::traits::CallInWasm for NativeExecutor< 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()) + sp_core::traits::CallInWasm::call_in_wasm(&self.wasm, wasm_blob, method, call_data, ext) } } @@ -379,7 +430,7 @@ mod tests { 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(), + executor.wasm.host_functions.iter().filter(|f| f == &function).count(), 2, ); }); diff --git a/substrate/client/executor/src/wasm_runtime.rs b/substrate/client/executor/src/wasm_runtime.rs index 9d54246ee0..180baf0a2f 100644 --- a/substrate/client/executor/src/wasm_runtime.rs +++ b/substrate/client/executor/src/wasm_runtime.rs @@ -19,13 +19,15 @@ //! The primary means of accessing the runtimes is through a cache which saves the reusable //! components of the runtime that are expensive to initialize. +use std::sync::Arc; +use std::borrow::Cow; use crate::error::{Error, WasmError}; -use log::{trace, warn}; +use parking_lot::{Mutex, RwLock}; use codec::Decode; use sp_core::{storage::well_known_keys, traits::Externalities}; use sp_version::RuntimeVersion; -use std::{collections::hash_map::{Entry, HashMap}, panic::AssertUnwindSafe}; -use sc_executor_common::wasm_runtime::WasmRuntime; +use std::panic::AssertUnwindSafe; +use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance}; use sp_wasm_interface::Function; @@ -39,15 +41,33 @@ pub enum WasmExecutionMethod { Compiled, } +/// Executoed code origin. +pub enum CodeSource<'a> { + /// Take code from storage, + Externalities, + /// Use provided code, + Custom(&'a [u8]), +} + /// A Wasm runtime object along with its cached runtime version. struct VersionedRuntime { - runtime: Box, + /// Runtime code hash. + code_hash: Vec, + /// Wasm runtime type. + wasm_method: WasmExecutionMethod, + /// Shared runtime that can spawn instances. + module: Box, /// The number of WebAssembly heap pages this instance was created with. heap_pages: u64, - /// Runtime version according to `Core_version`. - version: RuntimeVersion, + /// Runtime version according to `Core_version` if any. + version: Option, + /// Cached instance pool. + instances: RwLock<[Option>>>; MAX_INSTANCES]>, } +const MAX_RUNTIMES: usize = 2; +const MAX_INSTANCES: usize = 8; + /// Cache for the runtimes. /// /// When an instance is requested for the first time it is added to this cache. Metadata is kept @@ -60,130 +80,184 @@ struct VersionedRuntime { /// /// For now the cache grows indefinitely, but that should be fine for now since runtimes can only be /// upgraded rarely and there are no other ways to make the node to execute some other runtime. -pub struct RuntimesCache { - /// A cache of runtime instances along with metadata, ready to be reused. +pub struct RuntimeCache { + /// A cache of runtimes along with metadata. /// - /// Instances are keyed by the Wasm execution method and the hash of their code. - instances: HashMap<(WasmExecutionMethod, Vec), Result>, + /// Runtimes sorted by recent usage. The most recently used is at the front. + runtimes: Mutex<[Option>; MAX_RUNTIMES]>, } -impl RuntimesCache { +impl RuntimeCache { /// Creates a new instance of a runtimes cache. - pub fn new() -> RuntimesCache { - RuntimesCache { - instances: HashMap::new(), + pub fn new() -> RuntimeCache { + RuntimeCache { + runtimes: Default::default(), } } - /// Fetches an instance of the runtime. - /// - /// On first use we create a new runtime instance, save it to the cache - /// and persist its initial memory. - /// - /// Each subsequent request will return this instance, with its memory restored - /// to the persisted initial memory. Thus, we reuse one single runtime instance - /// for every `fetch_runtime` invocation. + /// Prepares a WASM module instance and executes given function for it. /// + /// This uses internal cache to find avaiable instance or create a new one. /// # Parameters /// + /// `code` - Provides external code or tells the executor to fetch it from storage. + /// /// `ext` - Externalities to use for the runtime. This is used for setting /// up an initial runtime instance. /// /// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution. /// + /// `wasm_method` - Type of WASM backend to use. + /// /// `host_functions` - The host functions that should be registered for the Wasm runtime. /// - /// # Return value + /// `allow_missing_func_imports` - Ignore missing function imports. /// - /// If no error occurred a tuple `(&mut WasmRuntime, H256)` is - /// returned. `H256` is the hash of the runtime code. + /// `f` - Function to execute. /// + /// # Returns result of `f` wrapped in an additonal result. /// In case of failure one of two errors can be returned: /// /// `Err::InvalidCode` is returned for runtime code issues. /// /// `Error::InvalidMemoryReference` is returned if no memory export with the /// identifier `memory` can be found in the runtime. - pub fn fetch_runtime( - &mut self, - ext: &mut E, + pub fn with_instance<'c, R, F>( + &self, + code: CodeSource<'c>, + ext: &mut dyn Externalities, wasm_method: WasmExecutionMethod, default_heap_pages: u64, host_functions: &[&'static dyn Function], - ) -> Result<(&mut (dyn WasmRuntime + 'static), &RuntimeVersion, Vec), Error> { - let code_hash = ext - .original_storage_hash(well_known_keys::CODE) - .ok_or(Error::InvalidCode("`CODE` not found in storage.".into()))?; - - let heap_pages = ext - .storage(well_known_keys::HEAP_PAGES) - .and_then(|pages| u64::decode(&mut &pages[..]).ok()) - .unwrap_or(default_heap_pages); - - let result = match self.instances.entry((wasm_method, code_hash.clone())) { - Entry::Occupied(o) => { - let result = o.into_mut(); - if let Ok(ref mut cached_runtime) = result { - let heap_pages_changed = cached_runtime.heap_pages != heap_pages; - let host_functions_changed = cached_runtime.runtime.host_functions() - != host_functions; - if heap_pages_changed || host_functions_changed { - let changed = if heap_pages_changed { - "heap_pages" - } else { - "host functions" - }; - - trace!( - target: "runtimes_cache", - "{} were changed. Reinstantiating the instance", - changed, - ); - *result = create_versioned_wasm_runtime( - ext, - wasm_method, - heap_pages, - host_functions.into(), - ); - if let Err(ref err) = result { - warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); - } - } - } - result + allow_missing_func_imports: bool, + f: F, + ) -> Result, Error> + where F: FnOnce( + &dyn WasmInstance, + Option<&RuntimeVersion>, + &mut dyn Externalities) + -> Result, + { + let (code_hash, heap_pages) = match &code { + CodeSource::Externalities => { + ( + ext + .original_storage_hash(well_known_keys::CODE) + .ok_or(Error::InvalidCode("`CODE` not found in storage.".into()))?, + ext + .storage(well_known_keys::HEAP_PAGES) + .and_then(|pages| u64::decode(&mut &pages[..]).ok()) + .unwrap_or(default_heap_pages), + ) }, - Entry::Vacant(v) => { - trace!(target: "runtimes_cache", "no instance found in cache, creating now."); + CodeSource::Custom(code) => { + (sp_core::blake2_256(code).to_vec(), default_heap_pages) + } + }; + + let mut runtimes = self.runtimes.lock(); // this must be released prior to calling f + let pos = runtimes.iter().position(|r| r.as_ref().map_or( + false, + |r| r.wasm_method == wasm_method && + r.code_hash == code_hash && + r.heap_pages == heap_pages + )); + + let runtime = match pos { + Some(n) => runtimes[n] + .clone() + .expect("`position` only returns `Some` for entries that are `Some`"), + None => { + let code = match code { + CodeSource::Externalities => { + Cow::Owned(ext.original_storage(well_known_keys::CODE) + .ok_or(WasmError::CodeNotFound)?) + } + CodeSource::Custom(code) => { + Cow::Borrowed(code) + } + }; + let result = create_versioned_wasm_runtime( + &code, + code_hash, ext, wasm_method, heap_pages, host_functions.into(), + allow_missing_func_imports, ); if let Err(ref err) = result { - warn!(target: "runtimes_cache", "cannot create a runtime: {:?}", err); + log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err); } - v.insert(result) + Arc::new(result?) } }; - result.as_mut() - .map(|entry| (entry.runtime.as_mut(), &entry.version, code_hash)) - .map_err(|ref e| Error::InvalidCode(format!("{:?}", e))) - } + // Rearrange runtimes by last recently used. + match pos { + Some(0) => {}, + Some(n) => { + for i in (1 .. n + 1).rev() { + runtimes.swap(i, i - 1); + } + } + None => { + runtimes[MAX_RUNTIMES-1] = Some(runtime.clone()); + for i in (1 .. MAX_RUNTIMES).rev() { + runtimes.swap(i, i - 1); + } + } + } + drop(runtimes); - /// Invalidate the runtime for the given `wasm_method` and `code_hash`. - /// - /// Invalidation of a runtime is useful when there was a `panic!` in native while executing it. - /// The `panic!` maybe have brought the runtime into a poisoned state and so, it is better to - /// invalidate this runtime instance. - pub fn invalidate_runtime( - &mut self, - wasm_method: WasmExecutionMethod, - code_hash: Vec, - ) { - // Just remove the instance, it will be re-created the next time it is requested. - self.instances.remove(&(wasm_method, code_hash)); + let result = { + // Find a free instance + let instance_pool = runtime.instances.read().clone(); + let instance = instance_pool + .iter() + .find_map(|i| i.as_ref().and_then(|i| i.try_lock())); + if let Some(mut locked) = instance { + let result = f(&**locked, runtime.version.as_ref(), ext); + if let Err(e) = &result { + log::warn!(target: "wasm-runtime", "Evicting failed runtime instance: {:?}", e); + *locked = runtime.module.new_instance()?; + } + result + } else { + // Allocate a new instance + let instance = runtime.module.new_instance()?; + + let result = f(&*instance, runtime.version.as_ref(), ext); + match &result { + Ok(_) => { + let mut instance_pool = runtime.instances.write(); + if let Some(ref mut slot) = instance_pool.iter_mut().find(|s| s.is_none()) { + **slot = Some(Arc::new(Mutex::new(instance))); + log::debug!( + target: "wasm-runtime", + "Allocated WASM instance {}/{}", + instance_pool.len(), + MAX_INSTANCES, + ); + } else { + log::warn!(target: "wasm-runtime", "Ran out of free WASM instances"); + } + } + Err(e) => { + log::warn!( + target: + "wasm-runtime", + "Fresh runtime instance failed with {:?}", + e, + ); + } + } + result + } + }; + + Ok(result) } } @@ -194,28 +268,43 @@ pub fn create_wasm_runtime_with_code( code: &[u8], host_functions: Vec<&'static dyn Function>, allow_missing_func_imports: bool, -) -> Result, WasmError> { +) -> Result, WasmError> { match wasm_method { WasmExecutionMethod::Interpreted => - sc_executor_wasmi::create_instance(code, heap_pages, host_functions, allow_missing_func_imports) - .map(|runtime| -> Box { Box::new(runtime) }), + sc_executor_wasmi::create_runtime( + code, + heap_pages, + host_functions, + allow_missing_func_imports + ).map(|runtime| -> Box { Box::new(runtime) }), #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => - sc_executor_wasmtime::create_instance(code, heap_pages, host_functions, allow_missing_func_imports) - .map(|runtime| -> Box { Box::new(runtime) }), + sc_executor_wasmtime::create_runtime( + code, + heap_pages, + host_functions, + allow_missing_func_imports + ).map(|runtime| -> Box { Box::new(runtime) }), } } -fn create_versioned_wasm_runtime( - ext: &mut E, +fn create_versioned_wasm_runtime( + code: &[u8], + code_hash: Vec, + ext: &mut dyn Externalities, wasm_method: WasmExecutionMethod, heap_pages: u64, host_functions: Vec<&'static dyn Function>, + allow_missing_func_imports: bool, ) -> Result { - let code = ext - .original_storage(well_known_keys::CODE) - .ok_or(WasmError::CodeNotFound)?; - let mut runtime = create_wasm_runtime_with_code(wasm_method, heap_pages, &code, host_functions, false)?; + let time = std::time::Instant::now(); + let mut runtime = create_wasm_runtime_with_code( + wasm_method, + heap_pages, + &code, + host_functions, + allow_missing_func_imports, + )?; // Call to determine runtime version. let version_result = { @@ -224,21 +313,33 @@ fn create_versioned_wasm_runtime( // The following unwind safety assertion is OK because if the method call panics, the // runtime will be dropped. - let mut runtime = AssertUnwindSafe(runtime.as_mut()); + let runtime = AssertUnwindSafe(runtime.as_mut()); crate::native_executor::with_externalities_safe( &mut **ext, - move || runtime.call("Core_version", &[]) + move || runtime.new_instance()?.call("Core_version", &[]) ).map_err(|_| WasmError::Instantiation("panic in call to get runtime version".into()))? }; - let encoded_version = version_result - .map_err(|e| WasmError::Instantiation(format!("failed to call \"Core_version\": {}", e)))?; - let version = RuntimeVersion::decode(&mut encoded_version.as_slice()) - .map_err(|_| WasmError::Instantiation("failed to decode \"Core_version\" result".into()))?; + let version = match version_result { + Ok(version) => Some(RuntimeVersion::decode(&mut version.as_slice()) + .map_err(|_| + WasmError::Instantiation("failed to decode \"Core_version\" result".into()) + )?), + Err(_) => None, + }; + log::debug!( + target: "wasm-runtime", + "Prepared new runtime version {:?} in {} ms.", + version, + time.elapsed().as_millis(), + ); Ok(VersionedRuntime { - runtime, + code_hash, + module: runtime, version, heap_pages, + wasm_method, + instances: Default::default(), }) } diff --git a/substrate/client/executor/wasmi/src/lib.rs b/substrate/client/executor/wasmi/src/lib.rs index a0e11dfcf8..6348c24133 100644 --- a/substrate/client/executor/wasmi/src/lib.rs +++ b/substrate/client/executor/wasmi/src/lib.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! This crate provides an implementation of `WasmRuntime` that is baked by wasmi. +//! This crate provides an implementation of `WasmModule` that is baked by wasmi. use sc_executor_common::{error::{Error, WasmError}, sandbox}; -use std::{str, mem, cell::RefCell}; +use std::{str, mem, cell::RefCell, sync::Arc}; use wasmi::{ Module, ModuleInstance, MemoryInstance, MemoryRef, TableRef, ImportsBuilder, ModuleRef, memory_units::Pages, RuntimeValue::{I32, I64, self}, @@ -30,7 +30,7 @@ use sp_wasm_interface::{ FunctionContext, Pointer, WordSize, Sandbox, MemoryId, Result as WResult, Function, }; use sp_runtime_interface::unpack_ptr_and_len; -use sc_executor_common::wasm_runtime::WasmRuntime; +use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance}; struct FunctionExecutor<'a> { sandbox_store: sandbox::Store, @@ -623,9 +623,77 @@ impl StateSnapshot { } } -/// A runtime along with its initial state snapshot. -#[derive(Clone)] +/// A runtime along with initial copy of data segments. pub struct WasmiRuntime { + /// A wasm module. + module: Module, + /// The host functions registered for this instance. + host_functions: Arc>, + /// Enable stub generation for functions that are not available in `host_functions`. + /// These stubs will error when the wasm blob tries to call them. + allow_missing_func_imports: bool, + /// Numer of heap pages this runtime uses. + heap_pages: u64, + /// Data segments created for each new instance. + data_segments: Vec, +} + +impl WasmModule for WasmiRuntime { + fn new_instance(&self) -> Result, Error> { + // Instantiate this module. + let (instance, missing_functions, memory) = instantiate_module( + self.heap_pages as usize, + &self.module, + &self.host_functions, + self.allow_missing_func_imports, + ).map_err(|e| WasmError::Instantiation(e.to_string()))?; + + // Take state snapshot before executing anything. + let state_snapshot = StateSnapshot::take(&instance, self.data_segments.clone()) + .expect( + "`take` returns `Err` if the module is not valid; + we already loaded module above, thus the `Module` is proven to be valid at this point; + qed + ", + ); + + Ok(Box::new(WasmiInstance { + instance, + memory, + state_snapshot, + host_functions: self.host_functions.clone(), + allow_missing_func_imports: self.allow_missing_func_imports, + missing_functions, + })) + } +} + +/// Create a new `WasmiRuntime` given the code. This function loads the module and +/// stores it in the instance. +pub fn create_runtime( + code: &[u8], + heap_pages: u64, + host_functions: Vec<&'static dyn Function>, + allow_missing_func_imports: bool, +) -> Result { + let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?; + + // Extract the data segments from the wasm code. + // + // A return of this error actually indicates that there is a problem in logic, since + // we just loaded and validated the `module` above. + let data_segments = extract_data_segments(&code)?; + Ok(WasmiRuntime { + module, + data_segments, + host_functions: Arc::new(host_functions), + allow_missing_func_imports, + heap_pages, + }) +} + +/// Wasmi instance wrapper along with the state snapshot. +pub struct WasmiInstance { /// A wasm module instance. instance: ModuleRef, /// The memory instance of used by the wasm module. @@ -633,7 +701,7 @@ pub struct WasmiRuntime { /// The snapshot of the instance's state taken just after the instantiation. state_snapshot: StateSnapshot, /// The host functions registered for this instance. - host_functions: Vec<&'static dyn Function>, + host_functions: Arc>, /// Enable stub generation for functions that are not available in `host_functions`. /// These stubs will error when the wasm blob tries to call them. allow_missing_func_imports: bool, @@ -641,13 +709,12 @@ pub struct WasmiRuntime { missing_functions: Vec, } -impl WasmRuntime for WasmiRuntime { - fn host_functions(&self) -> &[&'static dyn Function] { - &self.host_functions - } +// This is safe because `WasmiInstance` does not leak any references to `self.memory` and `self.instance` +unsafe impl Send for WasmiInstance {} +impl WasmInstance for WasmiInstance { fn call( - &mut self, + &self, method: &str, data: &[u8], ) -> Result, Error> { @@ -664,67 +731,26 @@ impl WasmRuntime for WasmiRuntime { &self.memory, method, data, - &self.host_functions, + self.host_functions.as_ref(), self.allow_missing_func_imports, - &self.missing_functions, + self.missing_functions.as_ref(), ) } - fn get_global_val(&self, name: &str) -> Result, Error> { + fn get_global_const(&self, name: &str) -> Result, Error> { match self.instance.export_by_name(name) { Some(global) => Ok(Some( global - .as_global() - .ok_or_else(|| format!("`{}` is not a global", name))? - .get() - .into() + .as_global() + .ok_or_else(|| format!("`{}` is not a global", name))? + .get() + .into() )), None => Ok(None), } } } -pub fn create_instance( - code: &[u8], - heap_pages: u64, - host_functions: Vec<&'static dyn Function>, - allow_missing_func_imports: bool, -) -> Result { - let module = Module::from_buffer(&code).map_err(|_| WasmError::InvalidModule)?; - - // Extract the data segments from the wasm code. - // - // A return of this error actually indicates that there is a problem in logic, since - // we just loaded and validated the `module` above. - let data_segments = extract_data_segments(&code)?; - - // Instantiate this module. - let (instance, missing_functions, memory) = instantiate_module( - heap_pages as usize, - &module, - &host_functions, - allow_missing_func_imports, - ).map_err(|e| WasmError::Instantiation(e.to_string()))?; - - // Take state snapshot before executing anything. - let state_snapshot = StateSnapshot::take(&instance, data_segments) - .expect( - "`take` returns `Err` if the module is not valid; - we already loaded module above, thus the `Module` is proven to be valid at this point; - qed - ", - ); - - Ok(WasmiRuntime { - instance, - memory, - state_snapshot, - host_functions, - allow_missing_func_imports, - missing_functions, - }) -} - /// Extract the data segments from the given wasm code. /// /// Returns `Err` if the given wasm code cannot be deserialized. diff --git a/substrate/client/executor/wasmtime/Cargo.toml b/substrate/client/executor/wasmtime/Cargo.toml index 57f4c2a842..9f8784cc98 100644 --- a/substrate/client/executor/wasmtime/Cargo.toml +++ b/substrate/client/executor/wasmtime/Cargo.toml @@ -10,7 +10,7 @@ description = "Defines a `WasmRuntime` that uses the Wasmtime JIT to execute." [dependencies] log = "0.4.8" -wasmi = "0.6.2" +scoped-tls = "1.0" parity-wasm = "0.41.0" codec = { package = "parity-scale-codec", version = "1.2.0" } sc-executor-common = { version = "0.8.0-alpha.2", path = "../common" } @@ -18,8 +18,7 @@ sp-wasm-interface = { version = "2.0.0-alpha.2", path = "../../../primitives/was sp-runtime-interface = { version = "2.0.0-alpha.2", path = "../../../primitives/runtime-interface" } sp-core = { version = "2.0.0-alpha.2", path = "../../../primitives/core" } sp-allocator = { version = "2.0.0-alpha.2", path = "../../../primitives/allocator" } - -wasmtime = "0.11" +wasmtime = { git = "https://github.com/paritytech/wasmtime", branch = "a-thread-safe-api" } [dev-dependencies] assert_matches = "1.3.0" diff --git a/substrate/client/executor/wasmtime/src/host.rs b/substrate/client/executor/wasmtime/src/host.rs index e0cc6ecc9a..29187ac663 100644 --- a/substrate/client/executor/wasmtime/src/host.rs +++ b/substrate/client/executor/wasmtime/src/host.rs @@ -19,7 +19,7 @@ use crate::instance_wrapper::InstanceWrapper; use crate::util; -use std::cell::RefCell; +use std::{cell::RefCell, rc::Rc}; use log::trace; use codec::{Encode, Decode}; use sp_allocator::FreeingBumpHeapAllocator; @@ -51,12 +51,12 @@ pub struct HostState { // borrow after performing necessary queries/changes. sandbox_store: RefCell>, allocator: RefCell, - instance: InstanceWrapper, + instance: Rc, } impl HostState { /// Constructs a new `HostState`. - pub fn new(allocator: FreeingBumpHeapAllocator, instance: InstanceWrapper) -> Self { + pub fn new(allocator: FreeingBumpHeapAllocator, instance: Rc) -> Self { HostState { sandbox_store: RefCell::new(sandbox::Store::new()), allocator: RefCell::new(allocator), @@ -64,11 +64,6 @@ impl HostState { } } - /// Destruct the host state and extract the `InstanceWrapper` passed at the creation. - pub fn into_instance(self) -> InstanceWrapper { - self.instance - } - /// Materialize `HostContext` that can be used to invoke a substrate host `dyn Function`. pub fn materialize<'a>(&'a self) -> HostContext<'a> { HostContext(self) diff --git a/substrate/client/executor/wasmtime/src/imports.rs b/substrate/client/executor/wasmtime/src/imports.rs index 349f84a0d7..48299ffd62 100644 --- a/substrate/client/executor/wasmtime/src/imports.rs +++ b/substrate/client/executor/wasmtime/src/imports.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use crate::state_holder::StateHolder; +use crate::state_holder; use sc_executor_common::error::WasmError; use sp_wasm_interface::{Function, Value, ValueType}; use std::any::Any; @@ -34,7 +34,6 @@ pub struct Imports { /// Goes over all imports of a module and prepares a vector of `Extern`s that can be used for /// instantiation of the module. Returns an error if there are imports that cannot be satisfied. pub fn resolve_imports( - state_holder: &StateHolder, module: &Module, host_functions: &[&'static dyn Function], heap_pages: u32, @@ -58,7 +57,6 @@ pub fn resolve_imports( } _ => resolve_func_import( module, - state_holder, import_ty, host_functions, allow_missing_func_imports, @@ -112,7 +110,6 @@ fn resolve_memory_import( fn resolve_func_import( module: &Module, - state_holder: &StateHolder, import_ty: &ImportType, host_functions: &[&'static dyn Function], allow_missing_func_imports: bool, @@ -152,7 +149,7 @@ fn resolve_func_import( ))); } - Ok(HostFuncHandler::new(&state_holder, *host_func).into_extern(module)) + Ok(HostFuncHandler::new(*host_func).into_extern(module)) } /// Returns `true` if `lhs` and `rhs` represent the same signature. @@ -163,14 +160,12 @@ fn signature_matches(lhs: &wasmtime::FuncType, rhs: &wasmtime::FuncType) -> bool /// This structure implements `Callable` and acts as a bridge between wasmtime and /// substrate host functions. struct HostFuncHandler { - state_holder: StateHolder, host_func: &'static dyn Function, } impl HostFuncHandler { - fn new(state_holder: &StateHolder, host_func: &'static dyn Function) -> Self { + fn new(host_func: &'static dyn Function) -> Self { Self { - state_holder: state_holder.clone(), host_func, } } @@ -188,7 +183,7 @@ impl Callable for HostFuncHandler { wasmtime_params: &[Val], wasmtime_results: &mut [Val], ) -> Result<(), wasmtime::Trap> { - let unwind_result = self.state_holder.with_context(|host_ctx| { + let unwind_result = state_holder::with_context(|host_ctx| { let mut host_ctx = host_ctx.expect( "host functions can be called only from wasm instance; wasm instance is always called initializing context; diff --git a/substrate/client/executor/wasmtime/src/lib.rs b/substrate/client/executor/wasmtime/src/lib.rs index 8f4801e6da..66e4e08523 100644 --- a/substrate/client/executor/wasmtime/src/lib.rs +++ b/substrate/client/executor/wasmtime/src/lib.rs @@ -23,4 +23,4 @@ mod imports; mod instance_wrapper; mod util; -pub use runtime::create_instance; +pub use runtime::create_runtime; diff --git a/substrate/client/executor/wasmtime/src/runtime.rs b/substrate/client/executor/wasmtime/src/runtime.rs index b99d334787..02acd33e69 100644 --- a/substrate/client/executor/wasmtime/src/runtime.rs +++ b/substrate/client/executor/wasmtime/src/runtime.rs @@ -15,57 +15,85 @@ // along with Substrate. If not, see . //! Defines the compiled Wasm runtime that uses Wasmtime internally. +use std::rc::Rc; +use std::sync::Arc; use crate::host::HostState; -use crate::imports::{resolve_imports, Imports}; +use crate::imports::{Imports, resolve_imports}; use crate::instance_wrapper::InstanceWrapper; -use crate::state_holder::StateHolder; +use crate::state_holder; use sc_executor_common::{ error::{Error, Result, WasmError}, - wasm_runtime::WasmRuntime, + wasm_runtime::{WasmModule, WasmInstance}, }; use sp_allocator::FreeingBumpHeapAllocator; use sp_runtime_interface::unpack_ptr_and_len; use sp_wasm_interface::{Function, Pointer, WordSize, Value}; use wasmtime::{Config, Engine, Module, Store}; -/// A `WasmRuntime` implementation using wasmtime to compile the runtime module to machine code +/// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code /// and execute the compiled code. pub struct WasmtimeRuntime { - module: Module, - imports: Imports, - state_holder: StateHolder, + module: Arc, heap_pages: u32, + allow_missing_func_imports: bool, host_functions: Vec<&'static dyn Function>, } -impl WasmRuntime for WasmtimeRuntime { - fn host_functions(&self) -> &[&'static dyn Function] { - &self.host_functions - } - - fn call(&mut self, method: &str, data: &[u8]) -> Result> { - call_method( +impl WasmModule for WasmtimeRuntime { + fn new_instance(&self) -> Result> { + // Scan all imports, find the matching host functions, and create stubs that adapt arguments + // and results. + let imports = resolve_imports( &self.module, - &mut self.imports, - &self.state_holder, - method, - data, + &self.host_functions, self.heap_pages, - ) - } + self.allow_missing_func_imports, + )?; - fn get_global_val(&self, name: &str) -> Result> { - // Yeah, there is no better way currently :( - InstanceWrapper::new(&self.module, &self.imports, self.heap_pages)? - .get_global_val(name) + Ok(Box::new(WasmtimeInstance { + module: self.module.clone(), + imports, + heap_pages: self.heap_pages, + })) } } +/// A `WasmInstance` implementation that reuses compiled module and spawns instances +/// to execute the compiled code. +pub struct WasmtimeInstance { + module: Arc, + imports: Imports, + heap_pages: u32, +} + +// This is safe because `WasmtimeInstance` does not leak reference to `self.imports` +// and all imports don't reference any anything, other than host functions and memory +unsafe impl Send for WasmtimeInstance {} + +impl WasmInstance for WasmtimeInstance { + fn call(&self, method: &str, data: &[u8]) -> Result> { + // TODO: reuse the instance and reset globals after call + // https://github.com/paritytech/substrate/issues/5141 + let instance = Rc::new(InstanceWrapper::new(&self.module, &self.imports, self.heap_pages)?); + call_method( + instance, + method, + data, + ) + } + + fn get_global_const(&self, name: &str) -> Result> { + let instance = InstanceWrapper::new(&self.module, &self.imports, self.heap_pages)?; + instance.get_global_val(name) + } +} + + /// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to /// machine code, which can be computationally heavy. -pub fn create_instance( +pub fn create_runtime( code: &[u8], heap_pages: u64, host_functions: Vec<&'static dyn Function>, @@ -80,55 +108,37 @@ pub fn create_instance( let module = Module::new(&store, code) .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; - let state_holder = StateHolder::empty(); - - // Scan all imports, find the matching host functions, and create stubs that adapt arguments - // and results. - let imports = resolve_imports( - &state_holder, - &module, - &host_functions, - heap_pages as u32, - allow_missing_func_imports, - )?; - Ok(WasmtimeRuntime { - module, - imports, - state_holder, + module: Arc::new(module), heap_pages: heap_pages as u32, + allow_missing_func_imports, host_functions, }) } /// Call a function inside a precompiled Wasm module. fn call_method( - module: &Module, - imports: &mut Imports, - state_holder: &StateHolder, + instance_wrapper: Rc, method: &str, data: &[u8], - heap_pages: u32, ) -> Result> { - let instance_wrapper = InstanceWrapper::new(module, imports, heap_pages)?; let entrypoint = instance_wrapper.resolve_entrypoint(method)?; let heap_base = instance_wrapper.extract_heap_base()?; let allocator = FreeingBumpHeapAllocator::new(heap_base); - perform_call(data, state_holder, instance_wrapper, entrypoint, allocator) + perform_call(data, instance_wrapper, entrypoint, allocator) } fn perform_call( data: &[u8], - state_holder: &StateHolder, - instance_wrapper: InstanceWrapper, + instance_wrapper: Rc, entrypoint: wasmtime::Func, mut allocator: FreeingBumpHeapAllocator, ) -> Result> { let (data_ptr, data_len) = inject_input_data(&instance_wrapper, &mut allocator, data)?; - let host_state = HostState::new(allocator, instance_wrapper); - let (ret, host_state) = state_holder.with_initialized_state(host_state, || { + let host_state = HostState::new(allocator, instance_wrapper.clone()); + let ret = state_holder::with_initialized_state(&host_state, || { match entrypoint.call(&[ wasmtime::Val::I32(u32::from(data_ptr) as i32), wasmtime::Val::I32(u32::from(data_len) as i32), @@ -146,9 +156,7 @@ fn perform_call( } }); let (output_ptr, output_len) = ret?; - - let instance = host_state.into_instance(); - let output = extract_output_data(&instance, output_ptr, output_len)?; + let output = extract_output_data(&instance_wrapper, output_ptr, output_len)?; Ok(output) } diff --git a/substrate/client/executor/wasmtime/src/state_holder.rs b/substrate/client/executor/wasmtime/src/state_holder.rs index 57564ed3ec..42cb79e7a3 100644 --- a/substrate/client/executor/wasmtime/src/state_holder.rs +++ b/substrate/client/executor/wasmtime/src/state_holder.rs @@ -15,63 +15,29 @@ // along with Substrate. If not, see . use crate::host::{HostContext, HostState}; -use std::cell::RefCell; -use std::rc::Rc; -/// A common place to store a reference to the `HostState`. +scoped_tls::scoped_thread_local!(static HOST_STATE: HostState); + +/// Provide `HostState` for the runtime method call and execute the given function `f`. /// -/// This structure is passed into each host function handler and retained in the implementation of -/// `WasmRuntime`. Whenever a call into a runtime method is initiated, the host state is populated -/// with the state for that runtime method call. -/// -/// During the execution of the runtime method call, wasm can call imported host functions. When -/// that happens the host function handler gets a `HostContext` (obtainable through having a -/// `HostState` reference). -#[derive(Clone)] -pub struct StateHolder { - // This is `Some` only during a call. - state: Rc>>, +/// During the execution of the provided function `with_context` will be callable. +pub fn with_initialized_state(s: &HostState, f: F) -> R +where + F: FnOnce() -> R, +{ + HOST_STATE.set(s, f) } -impl StateHolder { - /// Create a placeholder `StateHolder`. - pub fn empty() -> StateHolder { - StateHolder { - state: Rc::new(RefCell::new(None)), - } - } - - /// Provide `HostState` for the runtime method call and execute the given function `f`. - /// - /// During the execution of the provided function `with_context` will be callable. - pub fn with_initialized_state(&self, state: HostState, f: F) -> (R, HostState) - where - F: FnOnce() -> R, - { - *self.state.borrow_mut() = Some(state); - - let ret = f(); - let state = self - .state - .borrow_mut() - .take() - .expect("cannot be None since was just assigned; qed"); - - (ret, state) - } - - /// Create a `HostContext` from the contained `HostState` and execute the given function `f`. - /// - /// This function is only callable within closure passed to `init_state`. Otherwise, the passed - /// context will be `None`. - pub fn with_context(&self, f: F) -> R - where - F: FnOnce(Option) -> R, - { - let state = self.state.borrow(); - match *state { - Some(ref state) => f(Some(state.materialize())), - None => f(None), - } +/// Create a `HostContext` from the contained `HostState` and execute the given function `f`. +/// +/// This function is only callable within closure passed to `init_state`. Otherwise, the passed +/// context will be `None`. +pub fn with_context(f: F) -> R +where + F: FnOnce(Option) -> R, +{ + if !HOST_STATE.is_set() { + return f(None) } + HOST_STATE.with(|state| f(Some(state.materialize()))) } diff --git a/substrate/primitives/core/src/traits.rs b/substrate/primitives/core/src/traits.rs index bd02d39fb5..e86a0234bf 100644 --- a/substrate/primitives/core/src/traits.rs +++ b/substrate/primitives/core/src/traits.rs @@ -92,12 +92,11 @@ pub trait CodeExecutor: Sized + Send + Sync + CallInWasm + Clone + 'static { /// Call a given method in the runtime. Returns a tuple of the result (either the output data /// or an execution error) together with a `bool`, which is true if native execution was used. fn call< - E: Externalities, R: codec::Codec + PartialEq, NC: FnOnce() -> Result + UnwindSafe, >( &self, - ext: &mut E, + ext: &mut dyn Externalities, method: &str, data: &[u8], use_native: bool, diff --git a/substrate/primitives/runtime-interface/test/src/lib.rs b/substrate/primitives/runtime-interface/test/src/lib.rs index 01fc0a46b7..014a46e9d7 100644 --- a/substrate/primitives/runtime-interface/test/src/lib.rs +++ b/substrate/primitives/runtime-interface/test/src/lib.rs @@ -22,26 +22,27 @@ use sp_runtime_interface::*; use sp_runtime_interface_test_wasm::{WASM_BINARY, test_api::HostFunctions}; use sp_wasm_interface::HostFunctions as HostFunctionsT; +use sc_executor::CallInWasm; type TestExternalities = sp_state_machine::TestExternalities; fn call_wasm_method(method: &str) -> TestExternalities { let mut ext = TestExternalities::default(); let mut ext_ext = ext.ext(); + let mut host_functions = HF::host_functions(); + host_functions.extend(sp_io::SubstrateHostFunctions::host_functions()); - sc_executor::call_in_wasm::< - ( - HF, - sp_io::SubstrateHostFunctions, - ) - >( + let executor = sc_executor::WasmExecutor::new( + sc_executor::WasmExecutionMethod::Interpreted, + Some(8), + host_functions, + false, + ); + executor.call_in_wasm( + &WASM_BINARY[..], method, &[], - sc_executor::WasmExecutionMethod::Interpreted, &mut ext_ext, - &WASM_BINARY[..], - 8, - false, ).expect(&format!("Executes `{}`", method)); ext @@ -87,7 +88,7 @@ fn test_return_input_public_key() { #[test] #[should_panic( - expected = "Other(\"Instantiation: Export ext_test_api_return_input_version_1 not found\")" + expected = "\"Instantiation: Export ext_test_api_return_input_version_1 not found\"" )] fn host_function_not_found() { call_wasm_method::<()>("test_return_data"); @@ -96,8 +97,9 @@ fn host_function_not_found() { #[test] #[should_panic( expected = - "FunctionExecution(\"ext_test_api_invalid_utf8_data_version_1\", \ - \"Invalid utf8 data provided\")" + "Executes `test_invalid_utf8_data_should_return_an_error`: \ + \"Trap: Trap { kind: Host(FunctionExecution(\\\"ext_test_api_invalid_utf8_data_version_1\\\", \ + \\\"Invalid utf8 data provided\\\")) }\"" )] fn test_invalid_utf8_data_should_return_an_error() { call_wasm_method::("test_invalid_utf8_data_should_return_an_error"); diff --git a/substrate/primitives/state-machine/src/lib.rs b/substrate/primitives/state-machine/src/lib.rs index ce7fc243c1..ff41237c83 100644 --- a/substrate/primitives/state-machine/src/lib.rs +++ b/substrate/primitives/state-machine/src/lib.rs @@ -709,12 +709,11 @@ mod tests { type Error = u8; fn call< - E: Externalities, R: Encode + Decode + PartialEq, NC: FnOnce() -> result::Result, >( &self, - ext: &mut E, + ext: &mut dyn Externalities, _method: &str, _data: &[u8], use_native: bool, diff --git a/substrate/test-utils/runtime/src/system.rs b/substrate/test-utils/runtime/src/system.rs index b410d317a1..9eb501c4c8 100644 --- a/substrate/test-utils/runtime/src/system.rs +++ b/substrate/test-utils/runtime/src/system.rs @@ -401,7 +401,7 @@ mod tests { fn block_import_works_wasm() { block_import_works(|b, ext| { let mut ext = ext.ext(); - executor().call::<_, NeverNativeValue, fn() -> _>( + executor().call:: _>( &mut ext, "Core_execute_block", &b.encode(), @@ -494,7 +494,7 @@ mod tests { fn block_import_with_transaction_works_wasm() { block_import_with_transaction_works(|b, ext| { let mut ext = ext.ext(); - executor().call::<_, NeverNativeValue, fn() -> _>( + executor().call:: _>( &mut ext, "Core_execute_block", &b.encode(),