diff --git a/Cargo.lock b/Cargo.lock index b29ed95b87766..d577347f101d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7814,14 +7814,14 @@ version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e30165d31df606f5726b090ec7592c308a0eaf61721ff64c9a3018e344a8753e" dependencies = [ - "portable-atomic 1.3.1", + "portable-atomic 1.3.2", ] [[package]] name = "portable-atomic" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bbda379e6e462c97ea6afe9f6233619b202bbc4968d7caa6917788d2070a044" +checksum = "dc59d1bcc64fc5d021d67521f818db868368028108d37f0e98d74e33f68297b5" [[package]] name = "ppv-lite86" @@ -8387,18 +8387,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags", - "libc", - "mach", - "winapi", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -9290,7 +9278,6 @@ dependencies = [ "paste", "regex", "sc-executor-common", - "sc-executor-wasmi", "sc-executor-wasmtime", "sc-runtime-test", "sc-tracing", @@ -9311,7 +9298,6 @@ dependencies = [ "tempfile", "tracing", "tracing-subscriber 0.2.25", - "wasmi 0.13.2", "wat", ] @@ -9324,19 +9310,6 @@ dependencies = [ "sp-wasm-interface", "thiserror", "wasm-instrument 0.3.0", - "wasmi 0.13.2", -] - -[[package]] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -dependencies = [ - "log", - "sc-allocator", - "sc-executor-common", - "sp-runtime-interface", - "sp-wasm-interface", - "wasmi 0.13.2", ] [[package]] @@ -13061,7 +13034,6 @@ dependencies = [ "memory_units", "num-rational", "num-traits", - "region", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2b1659d4fb37b..44ec5b0b313c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ members = [ "client/executor", "client/executor/common", "client/executor/runtime-test", - "client/executor/wasmi", "client/executor/wasmtime", "client/informant", "client/keystore", diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index ca827001b5bcc..745b14073d151 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -69,7 +69,6 @@ pub fn new_partial( .transpose()?; let executor = sc_service::new_native_or_wasm_executor(&config); - let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::( config, diff --git a/bin/node/executor/benches/bench.rs b/bin/node/executor/benches/bench.rs index aa7d9eb0f31ff..6b082744e49f5 100644 --- a/bin/node/executor/benches/bench.rs +++ b/bin/node/executor/benches/bench.rs @@ -182,7 +182,6 @@ fn bench_execute_block(c: &mut Criterion) { let mut group = c.benchmark_group("execute blocks"); let execution_methods = vec![ ExecutionMethod::Native, - ExecutionMethod::Wasm(WasmExecutionMethod::Interpreted), ExecutionMethod::Wasm(WasmExecutionMethod::Compiled { instantiation_strategy: WasmtimeInstantiationStrategy::PoolingCopyOnWrite, }), diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 7e2498cec4b7f..89cb4500803b9 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -54,7 +54,7 @@ pub const DEFAULT_WASMTIME_INSTANTIATION_STRATEGY: WasmtimeInstantiationStrategy #[derive(Debug, Clone, Copy, ValueEnum)] #[value(rename_all = "kebab-case")] pub enum WasmExecutionMethod { - /// Uses an interpreter. + /// Uses an interpreter which now is deprecated. #[clap(name = "interpreted-i-know-what-i-do")] Interpreted, /// Uses a compiled runtime. @@ -76,21 +76,24 @@ pub fn execution_method_from_cli( execution_method: WasmExecutionMethod, instantiation_strategy: WasmtimeInstantiationStrategy, ) -> sc_service::config::WasmExecutionMethod { - match execution_method { - WasmExecutionMethod::Interpreted => sc_service::config::WasmExecutionMethod::Interpreted, - WasmExecutionMethod::Compiled => sc_service::config::WasmExecutionMethod::Compiled { - instantiation_strategy: match instantiation_strategy { - WasmtimeInstantiationStrategy::PoolingCopyOnWrite => - sc_service::config::WasmtimeInstantiationStrategy::PoolingCopyOnWrite, - WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite => - sc_service::config::WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite, - WasmtimeInstantiationStrategy::Pooling => - sc_service::config::WasmtimeInstantiationStrategy::Pooling, - WasmtimeInstantiationStrategy::RecreateInstance => - sc_service::config::WasmtimeInstantiationStrategy::RecreateInstance, - WasmtimeInstantiationStrategy::LegacyInstanceReuse => - sc_service::config::WasmtimeInstantiationStrategy::LegacyInstanceReuse, - }, + if let WasmExecutionMethod::Interpreted = execution_method { + log::warn!( + "`interpreted-i-know-what-i-do` is deprecated and will be removed in the future. Defaults to `compiled` execution mode." + ); + } + + sc_service::config::WasmExecutionMethod::Compiled { + instantiation_strategy: match instantiation_strategy { + WasmtimeInstantiationStrategy::PoolingCopyOnWrite => + sc_service::config::WasmtimeInstantiationStrategy::PoolingCopyOnWrite, + WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite => + sc_service::config::WasmtimeInstantiationStrategy::RecreateInstanceCopyOnWrite, + WasmtimeInstantiationStrategy::Pooling => + sc_service::config::WasmtimeInstantiationStrategy::Pooling, + WasmtimeInstantiationStrategy::RecreateInstance => + sc_service::config::WasmtimeInstantiationStrategy::RecreateInstance, + WasmtimeInstantiationStrategy::LegacyInstanceReuse => + sc_service::config::WasmtimeInstantiationStrategy::LegacyInstanceReuse, }, } } diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 21a9bd70dde65..31ed3bfed196d 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -17,11 +17,9 @@ targets = ["x86_64-unknown-linux-gnu"] lru = "0.8.1" parking_lot = "0.12.1" tracing = "0.1.29" -wasmi = "0.13.2" codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor-common = { version = "0.10.0-dev", path = "common" } -sc-executor-wasmi = { version = "0.10.0-dev", path = "wasmi" } sc-executor-wasmtime = { version = "0.10.0-dev", path = "wasmtime" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/executor/benches/bench.rs b/client/executor/benches/bench.rs index 2844b510edc74..38ae58adb615b 100644 --- a/client/executor/benches/bench.rs +++ b/client/executor/benches/bench.rs @@ -33,7 +33,6 @@ use std::sync::{ #[derive(Clone)] enum Method { - Interpreted, Compiled { instantiation_strategy: InstantiationStrategy, precompile: bool }, } @@ -50,17 +49,10 @@ fn initialize( method: Method, ) -> Box { let blob = RuntimeBlob::uncompress_if_needed(runtime).unwrap(); - let host_functions = sp_io::SubstrateHostFunctions::host_functions(); + let allow_missing_func_imports = true; match method { - Method::Interpreted => sc_executor_wasmi::create_runtime( - blob, - DEFAULT_HEAP_ALLOC_STRATEGY, - host_functions, - allow_missing_func_imports, - ) - .map(|runtime| -> Box { Box::new(runtime) }), Method::Compiled { instantiation_strategy, precompile } => { let config = sc_executor_wasmtime::Config { allow_missing_func_imports, @@ -215,7 +207,6 @@ fn bench_call_instance(c: &mut Criterion) { precompile: true, }, ), - ("interpreted", Method::Interpreted), ]; let runtimes = [("kusama_runtime", kusama_runtime()), ("test_runtime", test_runtime())]; diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index dd74ea2cfd209..19aebc70f0c0e 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -16,7 +16,6 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] thiserror = "1.0.30" wasm-instrument = "0.3" -wasmi = "0.13.2" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../../primitives/maybe-compressed-blob" } sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index 2dfe0bf02df2f..2a0dc364b4103 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -18,8 +18,6 @@ //! Rust executor possible errors. -use wasmi; - /// Result type alias. pub type Result = std::result::Result; @@ -27,9 +25,6 @@ pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { - #[error(transparent)] - Wasmi(#[from] wasmi::Error), - #[error("Error calling api function: {0}")] ApiError(Box), @@ -48,9 +43,6 @@ pub enum Error { #[error("Invalid type returned (should be u64)")] InvalidReturn, - #[error("Runtime error")] - Runtime, - #[error("Runtime panicked: {0}")] RuntimePanicked(String), @@ -109,8 +101,6 @@ pub enum Error { OutputExceedsBounds, } -impl wasmi::HostError for Error {} - impl From<&'static str> for Error { fn from(err: &'static str) -> Error { Error::Other(err.into()) diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs index 58278493af51b..07a0945cc2b66 100644 --- a/client/executor/common/src/runtime_blob/mod.rs +++ b/client/executor/common/src/runtime_blob/mod.rs @@ -26,9 +26,6 @@ //! //! To give you some examples: //! -//! - wasmi allows reaching to non-exported mutable globals so that we could reset them. Wasmtime -//! doesn’t support that. -//! //! We need to reset the globals because when we //! execute the Substrate Runtime, we do not drop and create the instance anew, instead //! we restore some selected parts of the state. diff --git a/client/executor/src/integration_tests/linux.rs b/client/executor/src/integration_tests/linux.rs index 434cb69bfdd32..68ac37e9011a1 100644 --- a/client/executor/src/integration_tests/linux.rs +++ b/client/executor/src/integration_tests/linux.rs @@ -18,34 +18,15 @@ //! Tests that are only relevant for Linux. +mod smaps; + use super::mk_test_runtime; use crate::WasmExecutionMethod; use codec::Encode as _; use sc_executor_common::wasm_runtime::DEFAULT_HEAP_ALLOC_STRATEGY; -mod smaps; - use self::smaps::Smaps; -#[test] -fn memory_consumption_interpreted() { - let _ = sp_tracing::try_init_simple(); - - if std::env::var("RUN_TEST").is_ok() { - memory_consumption(WasmExecutionMethod::Interpreted); - } else { - // We need to run the test in isolation, to not getting interfered by the other tests. - let executable = std::env::current_exe().unwrap(); - let output = std::process::Command::new(executable) - .env("RUN_TEST", "1") - .args(&["--nocapture", "memory_consumption_interpreted"]) - .output() - .unwrap(); - - assert!(output.status.success()); - } -} - #[test] fn memory_consumption_compiled() { let _ = sp_tracing::try_init_simple(); diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index b65aeb8d01109..37aed8eef96a1 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -24,7 +24,7 @@ use codec::{Decode, Encode}; use sc_executor_common::{ error::Error, runtime_blob::RuntimeBlob, - wasm_runtime::{HeapAllocStrategy, WasmModule, DEFAULT_HEAP_ALLOC_STRATEGY}, + wasm_runtime::{HeapAllocStrategy, WasmModule}, }; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ @@ -50,12 +50,6 @@ type HostFunctions = sp_io::SubstrateHostFunctions; macro_rules! test_wasm_execution { ($method_name:ident) => { paste::item! { - #[test] - fn [<$method_name _interpreted>]() { - let _ = sp_tracing::try_init_simple(); - $method_name(WasmExecutionMethod::Interpreted); - } - #[test] fn [<$method_name _compiled_recreate_instance_cow>]() { let _ = sp_tracing::try_init_simple(); @@ -97,15 +91,6 @@ macro_rules! test_wasm_execution { } } }; - - (interpreted_only $method_name:ident) => { - paste::item! { - #[test] - fn [<$method_name _interpreted>]() { - $method_name(WasmExecutionMethod::Interpreted); - } - } - }; } fn call_in_wasm( @@ -144,8 +129,8 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) { match call_in_wasm("test_calling_missing_external", &[], wasm_method, &mut ext).unwrap_err() { Error::AbortedDueToTrap(error) => { let expected = match wasm_method { - WasmExecutionMethod::Interpreted => "Other: Function `missing_external` is only a stub. Calling a stub is not allowed.", - WasmExecutionMethod::Compiled { .. } => "call to a missing function env:missing_external" + WasmExecutionMethod::Compiled { .. } => + "call to a missing function env:missing_external", }; assert_eq!(error.message, expected); }, @@ -163,8 +148,8 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) { { Error::AbortedDueToTrap(error) => { let expected = match wasm_method { - WasmExecutionMethod::Interpreted => "Other: Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.", - WasmExecutionMethod::Compiled { .. } => "call to a missing function env:yet_another_missing_external" + WasmExecutionMethod::Compiled { .. } => + "call to a missing function env:yet_another_missing_external", }; assert_eq!(error.message, expected); }, @@ -473,9 +458,6 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) { r#"host code panicked while being called by the runtime: Failed to allocate memory: "Allocator ran out of space""# ); }, - Error::RuntimePanicked(error) if wasm_method == WasmExecutionMethod::Interpreted => { - assert_eq!(error, r#"Failed to allocate memory: "Allocator ran out of space""#); - }, error => panic!("unexpected error: {:?}", error), } } @@ -558,25 +540,6 @@ fn restoration_of_globals(wasm_method: WasmExecutionMethod) { assert!(res.is_ok()); } -test_wasm_execution!(interpreted_only heap_is_reset_between_calls); -fn heap_is_reset_between_calls(wasm_method: WasmExecutionMethod) { - let runtime = mk_test_runtime(wasm_method, DEFAULT_HEAP_ALLOC_STRATEGY); - let mut instance = runtime.new_instance().unwrap(); - - let heap_base = instance - .get_global_const("__heap_base") - .expect("`__heap_base` is valid") - .expect("`__heap_base` exists") - .as_i32() - .expect("`__heap_base` is an `i32`"); - - let params = (heap_base as u32, 512u32 * 64 * 1024).encode(); - instance.call_export("check_and_set_in_heap", ¶ms).unwrap(); - - // Cal it a second time to check that the heap was freed. - instance.call_export("check_and_set_in_heap", ¶ms).unwrap(); -} - test_wasm_execution!(parallel_execution); fn parallel_execution(wasm_method: WasmExecutionMethod) { let executor = Arc::new( @@ -787,7 +750,6 @@ fn unreachable_intrinsic(wasm_method: WasmExecutionMethod) { match call_in_wasm("test_unreachable_intrinsic", &[], wasm_method, &mut ext).unwrap_err() { Error::AbortedDueToTrap(error) => { let expected = match wasm_method { - WasmExecutionMethod::Interpreted => "unreachable", WasmExecutionMethod::Compiled { .. } => "wasm trap: wasm `unreachable` instruction executed", }; @@ -814,9 +776,6 @@ fn return_huge_len(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); match call_in_wasm("test_return_huge_len", &[], wasm_method, &mut ext).unwrap_err() { - Error::Runtime => { - assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); - }, Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); }, @@ -843,9 +802,6 @@ fn return_max_memory_offset_plus_one(wasm_method: WasmExecutionMethod) { match call_in_wasm("test_return_max_memory_offset_plus_one", &[], wasm_method, &mut ext) .unwrap_err() { - Error::Runtime => { - assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); - }, Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); }, @@ -859,9 +815,6 @@ fn return_overflow(wasm_method: WasmExecutionMethod) { let mut ext = ext.ext(); match call_in_wasm("test_return_overflow", &[], wasm_method, &mut ext).unwrap_err() { - Error::Runtime => { - assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); - }, Error::OutputExceedsBounds => { assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); }, diff --git a/client/executor/src/lib.rs b/client/executor/src/lib.rs index 42e7dc7d16bd8..6ee0ab3512ac0 100644 --- a/client/executor/src/lib.rs +++ b/client/executor/src/lib.rs @@ -49,7 +49,6 @@ pub use sp_core::traits::Externalities; pub use sp_version::{NativeVersion, RuntimeVersion}; #[doc(hidden)] pub use sp_wasm_interface; -pub use wasmi; pub use sc_executor_common::{ error, diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 41a095081f8d1..dea84467963c3 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -43,8 +43,6 @@ use sp_wasm_interface::HostFunctions; /// Specification of different methods of executing the runtime Wasm code. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum WasmExecutionMethod { - /// Uses the Wasmi interpreter. - Interpreted, /// Uses the Wasmtime compiled runtime. Compiled { /// The instantiation strategy to use. @@ -53,8 +51,10 @@ pub enum WasmExecutionMethod { } impl Default for WasmExecutionMethod { - fn default() -> WasmExecutionMethod { - WasmExecutionMethod::Interpreted + fn default() -> Self { + Self::Compiled { + instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite, + } } } @@ -299,21 +299,6 @@ where H: HostFunctions, { match wasm_method { - WasmExecutionMethod::Interpreted => { - // Wasmi doesn't have any need in a cache directory. - // - // We drop the cache_path here to silence warnings that cache_path is not used if - // compiling without the `wasmtime` flag. - let _ = cache_path; - - sc_executor_wasmi::create_runtime( - blob, - heap_alloc_strategy, - H::host_functions(), - allow_missing_func_imports, - ) - .map(|runtime| -> Box { Box::new(runtime) }) - }, WasmExecutionMethod::Compiled { instantiation_strategy } => sc_executor_wasmtime::create_runtime::( blob, diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml deleted file mode 100644 index ded44f4ca77a9..0000000000000 --- a/client/executor/wasmi/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "sc-executor-wasmi" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -description = "This crate provides an implementation of `WasmRuntime` that is baked by wasmi." -documentation = "https://docs.rs/sc-executor-wasmi" -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -log = "0.4.17" -wasmi = { version = "0.13.2", features = [ "virtual_memory" ] } -sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } -sc-executor-common = { version = "0.10.0-dev", path = "../common" } -sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" } -sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } diff --git a/client/executor/wasmi/README.md b/client/executor/wasmi/README.md deleted file mode 100644 index ad613aa1245e3..0000000000000 --- a/client/executor/wasmi/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This crate provides an implementation of `WasmModule` that is baked by wasmi. - -License: GPL-3.0-or-later WITH Classpath-exception-2.0 \ No newline at end of file diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs deleted file mode 100644 index c757ff8afe43d..0000000000000 --- a/client/executor/wasmi/src/lib.rs +++ /dev/null @@ -1,599 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! This crate provides an implementation of `WasmModule` that is baked by wasmi. - -use std::{cell::RefCell, str, sync::Arc}; - -use log::{error, trace}; -use wasmi::{ - memory_units::Pages, - FuncInstance, ImportsBuilder, MemoryRef, Module, ModuleInstance, ModuleRef, - RuntimeValue::{self, I32, I64}, - TableRef, -}; - -use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator}; -use sc_executor_common::{ - error::{Error, MessageWithBacktrace, WasmError}, - runtime_blob::{DataSegmentsSnapshot, RuntimeBlob}, - wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule}, -}; -use sp_runtime_interface::unpack_ptr_and_len; -use sp_wasm_interface::{Function, FunctionContext, Pointer, Result as WResult, WordSize}; - -/// Wrapper around [`MemorRef`] that implements [`sc_allocator::Memory`]. -struct MemoryWrapper<'a>(&'a MemoryRef); - -impl sc_allocator::Memory for MemoryWrapper<'_> { - fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R { - self.0.with_direct_access_mut(run) - } - - fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R { - self.0.with_direct_access(run) - } - - fn pages(&self) -> u32 { - self.0.current_size().0 as _ - } - - fn max_pages(&self) -> Option { - self.0.maximum().map(|p| p.0 as _) - } - - fn grow(&mut self, additional: u32) -> Result<(), ()> { - self.0 - .grow(Pages(additional as _)) - .map_err(|e| { - log::error!( - target: "wasm-executor", - "Failed to grow memory by {} pages: {}", - additional, - e, - ) - }) - .map(drop) - } -} - -struct FunctionExecutor { - heap: RefCell, - memory: MemoryRef, - host_functions: Arc>, - allow_missing_func_imports: bool, - missing_functions: Arc>, - panic_message: Option, -} - -impl FunctionExecutor { - fn new( - m: MemoryRef, - heap_base: u32, - host_functions: Arc>, - allow_missing_func_imports: bool, - missing_functions: Arc>, - ) -> Result { - Ok(FunctionExecutor { - heap: RefCell::new(FreeingBumpHeapAllocator::new(heap_base)), - memory: m, - host_functions, - allow_missing_func_imports, - missing_functions, - panic_message: None, - }) - } -} - -impl FunctionContext for FunctionExecutor { - fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { - self.memory.get_into(address.into(), dest).map_err(|e| e.to_string()) - } - - fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> { - self.memory.set(address.into(), data).map_err(|e| e.to_string()) - } - - fn allocate_memory(&mut self, size: WordSize) -> WResult> { - self.heap - .borrow_mut() - .allocate(&mut MemoryWrapper(&self.memory), size) - .map_err(|e| e.to_string()) - } - - fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> { - self.heap - .borrow_mut() - .deallocate(&mut MemoryWrapper(&self.memory), ptr) - .map_err(|e| e.to_string()) - } - - fn register_panic_error_message(&mut self, message: &str) { - self.panic_message = Some(message.to_owned()); - } -} - -/// Will be used on initialization of a module to resolve function and memory imports. -struct Resolver<'a> { - /// All the hot functions that we export for the WASM blob. - host_functions: &'a [&'static dyn Function], - /// Should we allow missing function imports? - /// - /// If `true`, we return a stub that will return an error when being called. - allow_missing_func_imports: bool, - /// All the names of functions for that we did not provide a host function. - missing_functions: RefCell>, -} - -impl<'a> Resolver<'a> { - fn new( - host_functions: &'a [&'static dyn Function], - allow_missing_func_imports: bool, - ) -> Resolver<'a> { - Resolver { - host_functions, - allow_missing_func_imports, - missing_functions: RefCell::new(Vec::new()), - } - } -} - -impl<'a> wasmi::ModuleImportResolver for Resolver<'a> { - fn resolve_func( - &self, - name: &str, - signature: &wasmi::Signature, - ) -> std::result::Result { - let signature = sp_wasm_interface::Signature::from(signature); - for (function_index, function) in self.host_functions.iter().enumerate() { - if name == function.name() { - if signature == function.signature() { - return Ok(wasmi::FuncInstance::alloc_host(signature.into(), function_index)) - } else { - return Err(wasmi::Error::Instantiation(format!( - "Invalid signature for function `{}` expected `{:?}`, got `{:?}`", - function.name(), - signature, - function.signature(), - ))) - } - } - } - - if self.allow_missing_func_imports { - trace!( - target: "wasm-executor", - "Could not find function `{}`, a stub will be provided instead.", - name, - ); - let id = self.missing_functions.borrow().len() + self.host_functions.len(); - self.missing_functions.borrow_mut().push(name.to_string()); - - Ok(wasmi::FuncInstance::alloc_host(signature.into(), id)) - } else { - Err(wasmi::Error::Instantiation(format!("Export {} not found", name))) - } - } - - fn resolve_memory( - &self, - _: &str, - _: &wasmi::MemoryDescriptor, - ) -> Result { - Err(wasmi::Error::Instantiation( - "Internal error, wasmi expects that the wasm blob exports memory.".into(), - )) - } -} - -impl wasmi::Externals for FunctionExecutor { - fn invoke_index( - &mut self, - index: usize, - args: wasmi::RuntimeArgs, - ) -> Result, wasmi::Trap> { - let mut args = args.as_ref().iter().copied().map(Into::into); - - if let Some(function) = self.host_functions.clone().get(index) { - function - .execute(self, &mut args) - .map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg)) - .map_err(wasmi::Trap::from) - .map(|v| v.map(Into::into)) - } else if self.allow_missing_func_imports && - index >= self.host_functions.len() && - index < self.host_functions.len() + self.missing_functions.len() - { - Err(Error::from(format!( - "Function `{}` is only a stub. Calling a stub is not allowed.", - self.missing_functions[index - self.host_functions.len()], - )) - .into()) - } else { - Err(Error::from(format!("Could not find host function with index: {}", index)).into()) - } - } -} - -fn get_mem_instance(module: &ModuleRef) -> Result { - Ok(module - .export_by_name("memory") - .ok_or(Error::InvalidMemoryReference)? - .as_memory() - .ok_or(Error::InvalidMemoryReference)? - .clone()) -} - -/// Find the global named `__heap_base` in the given wasm module instance and -/// tries to get its value. -fn get_heap_base(module: &ModuleRef) -> Result { - let heap_base_val = module - .export_by_name("__heap_base") - .ok_or(Error::HeapBaseNotFoundOrInvalid)? - .as_global() - .ok_or(Error::HeapBaseNotFoundOrInvalid)? - .get(); - - match heap_base_val { - wasmi::RuntimeValue::I32(v) => Ok(v as u32), - _ => Err(Error::HeapBaseNotFoundOrInvalid), - } -} - -/// Call a given method in the given wasm-module runtime. -fn call_in_wasm_module( - module_instance: &ModuleRef, - memory: &MemoryRef, - method: InvokeMethod, - data: &[u8], - host_functions: Arc>, - allow_missing_func_imports: bool, - missing_functions: Arc>, - allocation_stats: &mut Option, -) -> Result, Error> { - // Initialize FunctionExecutor. - let table: Option = module_instance - .export_by_name("__indirect_function_table") - .and_then(|e| e.as_table().cloned()); - let heap_base = get_heap_base(module_instance)?; - - let mut function_executor = FunctionExecutor::new( - memory.clone(), - heap_base, - host_functions, - allow_missing_func_imports, - missing_functions, - )?; - - // Write the call data - let offset = function_executor.allocate_memory(data.len() as u32)?; - function_executor.write_memory(offset, data)?; - - fn convert_trap(executor: &mut FunctionExecutor, trap: wasmi::Trap) -> Error { - if let Some(message) = executor.panic_message.take() { - Error::AbortedDueToPanic(MessageWithBacktrace { message, backtrace: None }) - } else { - Error::AbortedDueToTrap(MessageWithBacktrace { - message: trap.to_string(), - backtrace: None, - }) - } - } - - let result = match method { - InvokeMethod::Export(method) => module_instance - .invoke_export( - method, - &[I32(u32::from(offset) as i32), I32(data.len() as i32)], - &mut function_executor, - ) - .map_err(|error| { - if let wasmi::Error::Trap(trap) = error { - convert_trap(&mut function_executor, trap) - } else { - error.into() - } - }), - InvokeMethod::Table(func_ref) => { - let func = table - .ok_or(Error::NoTable)? - .get(func_ref)? - .ok_or(Error::NoTableEntryWithIndex(func_ref))?; - FuncInstance::invoke( - &func, - &[I32(u32::from(offset) as i32), I32(data.len() as i32)], - &mut function_executor, - ) - .map_err(|trap| convert_trap(&mut function_executor, trap)) - }, - InvokeMethod::TableWithWrapper { dispatcher_ref, func } => { - let dispatcher = table - .ok_or(Error::NoTable)? - .get(dispatcher_ref)? - .ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?; - - FuncInstance::invoke( - &dispatcher, - &[I32(func as _), I32(u32::from(offset) as i32), I32(data.len() as i32)], - &mut function_executor, - ) - .map_err(|trap| convert_trap(&mut function_executor, trap)) - }, - }; - - *allocation_stats = Some(function_executor.heap.borrow().stats()); - - match result { - Ok(Some(I64(r))) => { - let (ptr, length) = unpack_ptr_and_len(r as u64); - #[allow(deprecated)] - memory.get(ptr, length as usize).map_err(|_| Error::Runtime) - }, - Err(e) => { - trace!( - target: "wasm-executor", - "Failed to execute code with {} pages", - memory.current_size().0, - ); - Err(e) - }, - _ => Err(Error::InvalidReturn), - } -} - -/// Prepare module instance -fn instantiate_module( - module: &Module, - host_functions: &[&'static dyn Function], - allow_missing_func_imports: bool, -) -> Result<(ModuleRef, Vec, MemoryRef), Error> { - let resolver = Resolver::new(host_functions, allow_missing_func_imports); - // start module instantiation. Don't run 'start' function yet. - let intermediate_instance = - ModuleInstance::new(module, &ImportsBuilder::new().with_resolver("env", &resolver))?; - - // Verify that the module has the heap base global variable. - let _ = get_heap_base(intermediate_instance.not_started_instance())?; - - // The `module` should export the memory with the correct properties (min, max). - // - // This is ensured by modifying the `RuntimeBlob` before initializing the `Module`. - let memory = get_mem_instance(intermediate_instance.not_started_instance())?; - - if intermediate_instance.has_start() { - // Runtime is not allowed to have the `start` function. - Err(Error::RuntimeHasStartFn) - } else { - Ok(( - intermediate_instance.assert_no_start(), - resolver.missing_functions.into_inner(), - memory, - )) - } -} - -/// A state snapshot of an instance taken just after instantiation. -/// -/// It is used for restoring the state of the module after execution. -#[derive(Clone)] -struct GlobalValsSnapshot { - /// The list of all global mutable variables of the module in their sequential order. - global_mut_values: Vec, -} - -impl GlobalValsSnapshot { - // Returns `None` if instance is not valid. - fn take(module_instance: &ModuleRef) -> Self { - // Collect all values of mutable globals. - let global_mut_values = module_instance - .globals() - .iter() - .filter(|g| g.is_mutable()) - .map(|g| g.get()) - .collect(); - Self { global_mut_values } - } - - /// Reset the runtime instance to the initial version by restoring - /// the preserved memory and globals. - /// - /// Returns `Err` if applying the snapshot is failed. - fn apply(&self, instance: &ModuleRef) -> Result<(), WasmError> { - for (global_ref, global_val) in instance - .globals() - .iter() - .filter(|g| g.is_mutable()) - .zip(self.global_mut_values.iter()) - { - // the instance should be the same as used for preserving and - // we iterate the same way it as we do it for preserving values that means that the - // types should be the same and all the values are mutable. So no error is expected/ - global_ref.set(*global_val).map_err(|_| WasmError::ApplySnapshotFailed)?; - } - Ok(()) - } -} - -/// 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, - - global_vals_snapshot: GlobalValsSnapshot, - data_segments_snapshot: DataSegmentsSnapshot, -} - -impl WasmModule for WasmiRuntime { - fn new_instance(&self) -> Result, Error> { - // Instantiate this module. - let (instance, missing_functions, memory) = - instantiate_module(&self.module, &self.host_functions, self.allow_missing_func_imports) - .map_err(|e| WasmError::Instantiation(e.to_string()))?; - - Ok(Box::new(WasmiInstance { - instance, - memory, - global_vals_snapshot: self.global_vals_snapshot.clone(), - data_segments_snapshot: self.data_segments_snapshot.clone(), - host_functions: self.host_functions.clone(), - allow_missing_func_imports: self.allow_missing_func_imports, - missing_functions: Arc::new(missing_functions), - memory_zeroed: true, - })) - } -} - -/// Create a new `WasmiRuntime` given the code. This function loads the module and -/// stores it in the instance. -pub fn create_runtime( - mut blob: RuntimeBlob, - heap_alloc_strategy: HeapAllocStrategy, - host_functions: Vec<&'static dyn Function>, - allow_missing_func_imports: bool, -) -> Result { - let data_segments_snapshot = - DataSegmentsSnapshot::take(&blob).map_err(|e| WasmError::Other(e.to_string()))?; - - // Make sure we only have exported memory to simplify the code of the wasmi executor. - blob.convert_memory_import_into_export()?; - // Ensure that the memory uses the correct heap pages. - blob.setup_memory_according_to_heap_alloc_strategy(heap_alloc_strategy)?; - - let module = - Module::from_parity_wasm_module(blob.into_inner()).map_err(|_| WasmError::InvalidModule)?; - - let global_vals_snapshot = { - let (instance, _, _) = - instantiate_module(&module, &host_functions, allow_missing_func_imports) - .map_err(|e| WasmError::Instantiation(e.to_string()))?; - GlobalValsSnapshot::take(&instance) - }; - - Ok(WasmiRuntime { - module, - data_segments_snapshot, - global_vals_snapshot, - host_functions: Arc::new(host_functions), - allow_missing_func_imports, - }) -} - -/// 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. - memory: MemoryRef, - /// Is the memory zeroed? - memory_zeroed: bool, - /// The snapshot of global variable values just after instantiation. - global_vals_snapshot: GlobalValsSnapshot, - /// The snapshot of data segments. - data_segments_snapshot: DataSegmentsSnapshot, - /// 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 trie to call them. - allow_missing_func_imports: bool, - /// List of missing functions detected during function resolution - missing_functions: Arc>, -} - -// This is safe because `WasmiInstance` does not leak any references to `self.memory` and -// `self.instance` -unsafe impl Send for WasmiInstance {} - -impl WasmiInstance { - fn call_impl( - &mut self, - method: InvokeMethod, - data: &[u8], - allocation_stats: &mut Option, - ) -> Result, Error> { - // We reuse a single wasm instance for multiple calls and a previous call (if any) - // altered the state. Therefore, we need to restore the instance to original state. - - if !self.memory_zeroed { - // First, zero initialize the linear memory. - self.memory.erase().map_err(|e| { - // Snapshot restoration failed. This is pretty unexpected since this can happen - // if some invariant is broken or if the system is under extreme memory pressure - // (so erasing fails). - error!(target: "wasm-executor", "snapshot restoration failed: {}", e); - WasmError::ErasingFailed(e.to_string()) - })?; - } - - // Second, reapply data segments into the linear memory. - self.data_segments_snapshot - .apply(|offset, contents| self.memory.set(offset, contents))?; - - // Third, restore the global variables to their initial values. - self.global_vals_snapshot.apply(&self.instance)?; - - let res = call_in_wasm_module( - &self.instance, - &self.memory, - method, - data, - self.host_functions.clone(), - self.allow_missing_func_imports, - self.missing_functions.clone(), - allocation_stats, - ); - - // If we couldn't unmap it, erase the memory. - self.memory_zeroed = self.memory.erase().is_ok(); - - res - } -} - -impl WasmInstance for WasmiInstance { - fn call_with_allocation_stats( - &mut self, - method: InvokeMethod, - data: &[u8], - ) -> (Result, Error>, Option) { - let mut allocation_stats = None; - let result = self.call_impl(method, data, &mut allocation_stats); - (result, allocation_stats) - } - - fn get_global_const(&mut 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(), - )), - None => Ok(None), - } - } - - fn linear_memory_base_ptr(&self) -> Option<*const u8> { - Some(self.memory.direct_access().as_ref().as_ptr()) - } -} diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index e1be3b5d99d9b..215704a112154 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -109,8 +109,8 @@ fn host_function_not_found() { .0 .unwrap_err(); - assert!(err.contains("Instantiation: Export ")); - assert!(err.contains(" not found")); + assert!(err.contains("test_return_data")); + assert!(err.contains(" Failed to create module")); } #[test]