From 123b710ae6f13b9d27c0948d5df82482a38cdadb Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Thu, 18 Feb 2021 11:54:39 +0000 Subject: [PATCH 1/9] Establish the runtime_blob module Seed it with the existing contents of the `util` module. --- client/executor/common/src/lib.rs | 2 +- .../data_segments_snapshot.rs} | 55 ++-------------- .../executor/common/src/runtime_blob/mod.rs | 55 ++++++++++++++++ .../common/src/runtime_blob/runtime_blob.rs | 66 +++++++++++++++++++ client/executor/wasmi/src/lib.rs | 4 +- .../executor/wasmtime/src/instance_wrapper.rs | 4 +- 6 files changed, 131 insertions(+), 55 deletions(-) rename client/executor/common/src/{util.rs => runtime_blob/data_segments_snapshot.rs} (64%) create mode 100644 client/executor/common/src/runtime_blob/mod.rs create mode 100644 client/executor/common/src/runtime_blob/runtime_blob.rs diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs index 050bad27d6c30..25e06314aba39 100644 --- a/client/executor/common/src/lib.rs +++ b/client/executor/common/src/lib.rs @@ -23,5 +23,5 @@ pub mod error; pub mod sandbox; -pub mod util; pub mod wasm_runtime; +pub mod runtime_blob; diff --git a/client/executor/common/src/util.rs b/client/executor/common/src/runtime_blob/data_segments_snapshot.rs similarity index 64% rename from client/executor/common/src/util.rs rename to client/executor/common/src/runtime_blob/data_segments_snapshot.rs index 5947be4469cd0..3850ec6753bef 100644 --- a/client/executor/common/src/util.rs +++ b/client/executor/common/src/runtime_blob/data_segments_snapshot.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2021 Parity Technologies (UK) Ltd. +// Copyright (C) 2021 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 @@ -16,53 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! A set of utilities for resetting a wasm instance to its initial state. - use crate::error::{self, Error}; +use super::RuntimeBlob; use std::mem; -use parity_wasm::elements::{deserialize_buffer, DataSegment, Instruction, Module as RawModule}; - -/// A bunch of information collected from a WebAssembly module. -pub struct WasmModuleInfo { - raw_module: RawModule, -} - -impl WasmModuleInfo { - /// Create `WasmModuleInfo` from the given wasm code. - /// - /// Returns `None` if the wasm code cannot be deserialized. - pub fn new(wasm_code: &[u8]) -> Option { - let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?; - Some(Self { raw_module }) - } - - /// Extract the data segments from the given wasm code. - /// - /// Returns `Err` if the given wasm code cannot be deserialized. - fn data_segments(&self) -> Vec { - self.raw_module - .data_section() - .map(|ds| ds.entries()) - .unwrap_or(&[]) - .to_vec() - } - - /// The number of globals defined in locally in this module. - pub fn declared_globals_count(&self) -> u32 { - self.raw_module - .global_section() - .map(|gs| gs.entries().len() as u32) - .unwrap_or(0) - } - - /// The number of imports of globals. - pub fn imported_globals_count(&self) -> u32 { - self.raw_module - .import_section() - .map(|is| is.globals() as u32) - .unwrap_or(0) - } -} +use parity_wasm::elements::Instruction; /// This is a snapshot of data segments specialzied for a particular instantiation. /// @@ -75,7 +32,7 @@ pub struct DataSegmentsSnapshot { impl DataSegmentsSnapshot { /// Create a snapshot from the data segments from the module. - pub fn take(module: &WasmModuleInfo) -> error::Result { + pub fn take(module: &RuntimeBlob) -> error::Result { let data_segments = module .data_segments() .into_iter() @@ -105,9 +62,7 @@ impl DataSegmentsSnapshot { // if/when we gain those. return Err(Error::ImportedGlobalsUnsupported); } - insn => { - return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))) - } + insn => return Err(Error::InvalidInitializerExpression(format!("{:?}", insn))), }; Ok((offset, contents)) diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs new file mode 100644 index 0000000000000..502dc59cce98a --- /dev/null +++ b/client/executor/common/src/runtime_blob/mod.rs @@ -0,0 +1,55 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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 module allows for inspection and instrumentation, i.e. modifying the module to alter it's +//! structure or behavior, of a wasm module. +//! +//! ## Instrumentation +//! +//! In ideal world, there would be no instrumentation. However, in the real world the execution +//! engines we use are somewhat limited in their APIs or abilities. +//! +//! 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. +//! +//! - stack depth metering can be performed via instrumentation or deferred to the engine and say +//! be added directly in machine code. Implementing this in machine code is rather cumbersome so +//! instrumentation looks like a good solution. +//! +//! Stack depth metering is needed to make a wasm blob +//! execution deterministic, which is in turn is needed by the Parachain Validation Function in Polkadot. +//! +//! ## Inspection +//! +//! Inspection of a wasm module may be needed to extract some useful information, such as to extract +//! data segment snapshot, which is helpful for quickly restoring the initial state of instances. +//! Inspection can be also useful to prove that a wasm module possesses some properties, such as, +//! is free of any floating point operations, which is a useful step towards making instances produced +//! from such a module deterministic. + +mod data_segments_snapshot; +mod runtime_blob; + +pub use data_segments_snapshot::DataSegmentsSnapshot; +pub use runtime_blob::RuntimeBlob; diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs new file mode 100644 index 0000000000000..c6a127fdcfebf --- /dev/null +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -0,0 +1,66 @@ +// This file is part of Substrate. + +// Copyright (C) 2021 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 . + +use parity_wasm::elements::{deserialize_buffer, DataSegment, Module as RawModule}; + +/// A bunch of information collected from a WebAssembly module. +pub struct RuntimeBlob { + raw_module: RawModule, +} + +impl RuntimeBlob { + /// Create `RuntimeBlob` from the given wasm code. + /// + /// Returns `None` if the wasm code cannot be deserialized. + pub fn new(wasm_code: &[u8]) -> Option { + let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?; + Some(Self { raw_module }) + } + + /// Extract the data segments from the given wasm code. + /// + /// Returns `Err` if the given wasm code cannot be deserialized. + pub(super) fn data_segments(&self) -> Vec { + self.raw_module + .data_section() + .map(|ds| ds.entries()) + .unwrap_or(&[]) + .to_vec() + } + + /// The number of globals defined in locally in this module. + pub fn declared_globals_count(&self) -> u32 { + self.raw_module + .global_section() + .map(|gs| gs.entries().len() as u32) + .unwrap_or(0) + } + + /// The number of imports of globals. + pub fn imported_globals_count(&self) -> u32 { + self.raw_module + .import_section() + .map(|is| is.globals() as u32) + .unwrap_or(0) + } + + /// Make sure that the mutable globals are exported + pub fn expose_mutable_globals(&mut self) { + pwasm_utils::export_mutable_globals(&mut self.raw_module, "exported_internal_global"); + } +} diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index e6a6ef3a61039..81fc08865c400 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -36,7 +36,7 @@ use sc_executor_common::{ error::{Error, WasmError}, sandbox, }; -use sc_executor_common::util::{DataSegmentsSnapshot, WasmModuleInfo}; +use sc_executor_common::runtime_blob::{RuntimeBlob, DataSegmentsSnapshot}; struct FunctionExecutor<'a> { sandbox_store: sandbox::Store, @@ -662,7 +662,7 @@ pub fn create_runtime( .map_err(|e| WasmError::Instantiation(e.to_string()))?; let data_segments_snapshot = DataSegmentsSnapshot::take( - &WasmModuleInfo::new(code) + &RuntimeBlob::new(code) .ok_or_else(|| WasmError::Other("cannot deserialize module".to_string()))?, ) .map_err(|e| WasmError::Other(e.to_string()))?; diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index f0543a7ef9506..4bc16b9e1de5f 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -25,7 +25,7 @@ use crate::imports::Imports; use std::{slice, marker}; use sc_executor_common::{ error::{Error, Result}, - util::{WasmModuleInfo, DataSegmentsSnapshot}, + runtime_blob::{RuntimeBlob, DataSegmentsSnapshot, GlobalsSnapshot}, wasm_runtime::InvokeMethod, }; use sp_wasm_interface::{Pointer, WordSize, Value}; @@ -52,7 +52,7 @@ impl ModuleWrapper { let module = Module::new(engine, &instrumented_code) .map_err(|e| Error::from(format!("cannot create module: {}", e)))?; - let module_info = WasmModuleInfo::new(code) + let module_info = RuntimeBlob::new(code) .ok_or_else(|| Error::from("cannot deserialize module".to_string()))?; let data_segments_snapshot = DataSegmentsSnapshot::take(&module_info) From e0e9b1195444cdff58c9ef9b00ebe660ad759d77 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Thu, 18 Feb 2021 16:36:13 +0000 Subject: [PATCH 2/9] Port wasmtime mutable globals instrumentation into runtime blob APIs --- Cargo.lock | 2 +- client/executor/common/Cargo.toml | 1 + .../src/runtime_blob/globals_snapshot.rs | 109 ++++++++++++++++++ .../executor/common/src/runtime_blob/mod.rs | 2 + .../common/src/runtime_blob/runtime_blob.rs | 27 ++++- client/executor/src/wasm_runtime.rs | 13 ++- client/executor/wasmtime/Cargo.toml | 1 - client/executor/wasmtime/src/imports.rs | 32 +---- .../executor/wasmtime/src/instance_wrapper.rs | 53 ++++++--- .../src/instance_wrapper/globals_snapshot.rs | 84 -------------- client/executor/wasmtime/src/runtime.rs | 19 ++- client/executor/wasmtime/src/util.rs | 26 +++++ 12 files changed, 227 insertions(+), 142 deletions(-) create mode 100644 client/executor/common/src/runtime_blob/globals_snapshot.rs delete mode 100644 client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs diff --git a/Cargo.lock b/Cargo.lock index e3c9131a86e3b..88ffece2cb411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7056,6 +7056,7 @@ dependencies = [ "derive_more", "parity-scale-codec", "parity-wasm 0.41.0", + "pwasm-utils 0.14.0", "sp-allocator", "sp-core", "sp-serializer", @@ -7086,7 +7087,6 @@ dependencies = [ "log", "parity-scale-codec", "parity-wasm 0.41.0", - "pwasm-utils 0.14.0", "sc-executor-common", "scoped-tls", "sp-allocator", diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index 7e13e37d33fbe..95c090686e83b 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" parity-wasm = "0.41.0" +pwasm-utils = "0.14.0" codec = { package = "parity-scale-codec", version = "2.0.0" } wasmi = "0.6.2" sp-core = { version = "3.0.0", path = "../../../primitives/core" } diff --git a/client/executor/common/src/runtime_blob/globals_snapshot.rs b/client/executor/common/src/runtime_blob/globals_snapshot.rs new file mode 100644 index 0000000000000..579f3f8855d95 --- /dev/null +++ b/client/executor/common/src/runtime_blob/globals_snapshot.rs @@ -0,0 +1,109 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2021 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 . + +use super::RuntimeBlob; + +/// Saved value of particular exported global. +struct SavedValue { + /// The handle of this global which can be used to refer to this global. + handle: Global, + /// The global value that was observed during the snapshot creation. + value: sp_wasm_interface::Value, +} + +/// An adapter for a wasm module instance that is focused on getting and setting globals. +pub trait InstanceGlobals { + /// A handle to a global. + type Global: Clone; + /// Get a handle to a global by it's export name. + /// + /// The export is guaranteed to exist, which points to a mutable global. + fn get_global(&self, export_name: &str) -> Self::Global; + /// Get the current value of the global. + fn get_global_value(&self, global: &Self::Global) -> sp_wasm_interface::Value; + /// Update the current value of the global. + /// + /// The global behind the handle is guaranteed to be mutable and the value to be the same type + /// as the global. + fn set_global_value(&self, global: &Self::Global, value: sp_wasm_interface::Value); +} + +/// A set of exposed mutable globals. +/// +/// This is set of globals required to create a [`GlobalsSnapshot`] and that are collected from +/// a runtime blob that was instrumented by [`InstrumentModule::expose_mutable_globals`]. +/// +/// If the code wasn't instrumented then it would be empty and snapshot would do nothing. +pub struct ExposedMutableGlobalsSet(Vec); + +impl ExposedMutableGlobalsSet { + /// Collect the set from the given runtime blob. See the struct documentation for details. + pub fn collect(runtime_blob: &RuntimeBlob) -> Self { + let global_names = runtime_blob + .exported_internal_global_names() + .map(ToOwned::to_owned) + .collect(); + Self(global_names) + } +} + +/// A snapshot of a global variables values. This snapshot can be used later for restoring the +/// values to the preserved state. +/// +/// Technically, a snapshot stores only values of mutable global variables. This is because +/// immutable global variables always have the same values. +/// +/// We take it from an instance rather from a module because the start function could potentially +/// change any of the mutable global values. +pub struct GlobalsSnapshot(Vec>); + +impl GlobalsSnapshot { + /// Take a snapshot of global variables for a given instance. + /// + /// # Panics + /// + /// This function panics if the instance doesn't correspond to the module from which the + /// [`ExposedMutableGlobalsSet`] was collected. + pub fn take(mutable_globals: &ExposedMutableGlobalsSet, instance: &Instance) -> Self + where + Instance: InstanceGlobals, + { + let global_names = &mutable_globals.0; + let mut saved_values = Vec::with_capacity(global_names.len()); + + for global_name in global_names { + let handle = instance.get_global(global_name); + let value = instance.get_global_value(&handle); + saved_values.push(SavedValue { handle, value }); + } + + Self(saved_values) + } + + /// Apply the snapshot to the given instance. + /// + /// This instance must be the same that was used for creation of this snapshot. + pub fn apply(&self, instance: &Instance) + where + Instance: InstanceGlobals, + { + for saved_value in &self.0 { + instance.set_global_value(&saved_value.handle, saved_value.value); + } + } +} diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs index 502dc59cce98a..dfdb74fcc257d 100644 --- a/client/executor/common/src/runtime_blob/mod.rs +++ b/client/executor/common/src/runtime_blob/mod.rs @@ -49,7 +49,9 @@ //! from such a module deterministic. mod data_segments_snapshot; +mod globals_snapshot; mod runtime_blob; pub use data_segments_snapshot::DataSegmentsSnapshot; +pub use globals_snapshot::{GlobalsSnapshot, ExposedMutableGlobalsSet, InstanceGlobals}; pub use runtime_blob::RuntimeBlob; diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs index c6a127fdcfebf..e34d8d389e899 100644 --- a/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -16,9 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use parity_wasm::elements::{deserialize_buffer, DataSegment, Module as RawModule}; +use parity_wasm::elements::{DataSegment, Module as RawModule, deserialize_buffer, serialize}; /// A bunch of information collected from a WebAssembly module. +#[derive(Clone)] pub struct RuntimeBlob { raw_module: RawModule, } @@ -63,4 +64,28 @@ impl RuntimeBlob { pub fn expose_mutable_globals(&mut self) { pwasm_utils::export_mutable_globals(&mut self.raw_module, "exported_internal_global"); } + + /// Returns an iterator of all globals which were exported by [`expose_mutable_globals`]. + pub(super) fn exported_internal_global_names<'module>( + &'module self, + ) -> impl Iterator { + let exports = self + .raw_module + .export_section() + .map(|es| es.entries()) + .unwrap_or(&[]); + exports.iter().filter_map(|export| match export.internal() { + parity_wasm::elements::Internal::Global(_) + if export.field().starts_with("exported_internal_global") => + { + Some(export.field()) + } + _ => None, + }) + } + + /// Consumes this runtime blob and serializes it. + pub fn serialize(self) -> Vec { + serialize(self.raw_module).unwrap() + } } diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 351a2b5f40f00..21173cd451712 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -300,14 +300,21 @@ pub fn create_wasm_runtime_with_code( .map(|runtime| -> Arc { Arc::new(runtime) }) } #[cfg(feature = "wasmtime")] - WasmExecutionMethod::Compiled => + WasmExecutionMethod::Compiled => { + let mut blob = sc_executor_common::runtime_blob::RuntimeBlob::new(code).unwrap(); + + // Substrate Runtime relies on quick wasm instance reuse and that requires working mutable + // globals snapshot taking. Hence we perform this instrumentation here. + blob.expose_mutable_globals(); + sc_executor_wasmtime::create_runtime( - code, + blob, heap_pages, host_functions, allow_missing_func_imports, cache_path, - ).map(|runtime| -> Arc { Arc::new(runtime) }), + ).map(|runtime| -> Arc { Arc::new(runtime) }) + }, } } diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 051b314e4498a..dbd41b2caf1ef 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -23,7 +23,6 @@ sp-runtime-interface = { version = "3.0.0", path = "../../../primitives/runtime- sp-core = { version = "3.0.0", path = "../../../primitives/core" } sp-allocator = { version = "3.0.0", path = "../../../primitives/allocator" } wasmtime = "0.22" -pwasm-utils = "0.14.0" [dev-dependencies] assert_matches = "1.3.0" diff --git a/client/executor/wasmtime/src/imports.rs b/client/executor/wasmtime/src/imports.rs index 08cedd434e366..21b7728c323c8 100644 --- a/client/executor/wasmtime/src/imports.rs +++ b/client/executor/wasmtime/src/imports.rs @@ -16,9 +16,9 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::state_holder; +use crate::{state_holder, util}; use sc_executor_common::error::WasmError; -use sp_wasm_interface::{Function, Value, ValueType}; +use sp_wasm_interface::{Function, ValueType}; use std::any::Any; use wasmtime::{ Extern, ExternType, Func, FuncType, ImportType, Limits, Memory, MemoryType, Module, @@ -187,12 +187,12 @@ fn call_static( qed ", ); - // `into_value` panics if it encounters a value that doesn't fit into the values + // `from_wasmtime_val` panics if it encounters a value that doesn't fit into the values // available in substrate. // // This, however, cannot happen since the signature of this function is created from // a `dyn Function` signature of which cannot have a non substrate value by definition. - let mut params = wasmtime_params.iter().cloned().map(into_value); + let mut params = wasmtime_params.iter().cloned().map(util::from_wasmtime_val); std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { static_func.execute(&mut host_ctx, &mut params) @@ -211,7 +211,7 @@ fn call_static( "wasmtime function signature, therefore the number of results, should always \ correspond to the number of results returned by the host function", ); - wasmtime_results[0] = into_wasmtime_val(ret_val); + wasmtime_results[0] = util::into_wasmtime_val(ret_val); Ok(()) } Ok(None) => { @@ -295,28 +295,6 @@ fn into_wasmtime_val_type(val_ty: ValueType) -> wasmtime::ValType { } } -/// Converts a `Val` into a substrate runtime interface `Value`. -/// -/// Panics if the given value doesn't have a corresponding variant in `Value`. -pub fn into_value(val: Val) -> Value { - match val { - Val::I32(v) => Value::I32(v), - Val::I64(v) => Value::I64(v), - Val::F32(f_bits) => Value::F32(f_bits), - Val::F64(f_bits) => Value::F64(f_bits), - _ => panic!("Given value type is unsupported by substrate"), - } -} - -pub fn into_wasmtime_val(value: Value) -> wasmtime::Val { - match value { - Value::I32(v) => Val::I32(v), - Value::I64(v) => Val::I64(v), - Value::F32(f_bits) => Val::F32(f_bits), - Value::F64(f_bits) => Val::F64(f_bits), - } -} - /// Attempt to convert a opaque panic payload to a string. fn stringify_panic_payload(payload: Box) -> String { match payload.downcast::<&'static str>() { diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index 4bc16b9e1de5f..556f35d03e603 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -25,41 +25,32 @@ use crate::imports::Imports; use std::{slice, marker}; use sc_executor_common::{ error::{Error, Result}, - runtime_blob::{RuntimeBlob, DataSegmentsSnapshot, GlobalsSnapshot}, + runtime_blob::{self, ExposedMutableGlobalsSet, DataSegmentsSnapshot, RuntimeBlob}, wasm_runtime::InvokeMethod, }; use sp_wasm_interface::{Pointer, WordSize, Value}; use wasmtime::{Engine, Instance, Module, Memory, Table, Val, Func, Extern, Global, Store}; -use parity_wasm::elements; - -mod globals_snapshot; - -pub use globals_snapshot::GlobalsSnapshot; pub struct ModuleWrapper { module: Module, + mutable_globals: ExposedMutableGlobalsSet, data_segments_snapshot: DataSegmentsSnapshot, } impl ModuleWrapper { - pub fn new(engine: &Engine, code: &[u8]) -> Result { - let mut raw_module: elements::Module = elements::deserialize_buffer(code) - .map_err(|e| Error::from(format!("cannot decode module: {}", e)))?; - pwasm_utils::export_mutable_globals(&mut raw_module, "exported_internal_global"); - let instrumented_code = elements::serialize(raw_module) - .map_err(|e| Error::from(format!("cannot encode module: {}", e)))?; - - let module = Module::new(engine, &instrumented_code) - .map_err(|e| Error::from(format!("cannot create module: {}", e)))?; + pub fn new(engine: &Engine, runtime_blob: RuntimeBlob) -> Result { + let data_segments_snapshot = DataSegmentsSnapshot::take(&runtime_blob) + .map_err(|e| Error::from(format!("cannot take data segments snapshot: {}", e)))?; - let module_info = RuntimeBlob::new(code) - .ok_or_else(|| Error::from("cannot deserialize module".to_string()))?; + let mutable_globals = ExposedMutableGlobalsSet::collect(&runtime_blob); - let data_segments_snapshot = DataSegmentsSnapshot::take(&module_info) - .map_err(|e| Error::from(format!("cannot take data segments snapshot: {}", e)))?; + let bytecode = runtime_blob.serialize(); + let module = Module::new(engine, &bytecode) + .map_err(|e| Error::from(format!("cannot create module: {}", e)))?; Ok(Self { module, + mutable_globals, data_segments_snapshot, }) } @@ -68,6 +59,10 @@ impl ModuleWrapper { &self.module } + pub fn mutable_globals(&self) -> &ExposedMutableGlobalsSet { + &self.mutable_globals + } + pub fn data_segments_snapshot(&self) -> &DataSegmentsSnapshot { &self.data_segments_snapshot } @@ -462,3 +457,23 @@ impl InstanceWrapper { } } } + +impl runtime_blob::InstanceGlobals for InstanceWrapper { + type Global = wasmtime::Global; + + fn get_global(&self, export_name: &str) -> Self::Global { + self.instance + .get_global(export_name) + .expect("get_global is guaranteed to be called with an export name of a global; qed") + } + + fn get_global_value(&self, global: &Self::Global) -> Value { + util::from_wasmtime_val(global.get()) + } + + fn set_global_value(&self, global: &Self::Global, value: Value) { + global.set(util::into_wasmtime_val(value)).expect( + "the value is guaranteed to be of the same value; the global is guaranteed to be mutable; qed", + ); + } +} diff --git a/client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs b/client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs deleted file mode 100644 index a6b1ed394150d..0000000000000 --- a/client/executor/wasmtime/src/instance_wrapper/globals_snapshot.rs +++ /dev/null @@ -1,84 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020-2021 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 . - -use super::InstanceWrapper; -use sc_executor_common::error::{Result, Error}; -use sp_wasm_interface::Value; -use crate::imports::{into_value, into_wasmtime_val}; - -/// Saved value of particular exported global. -struct SavedValue { - /// Index of the export. - index: usize, - /// Global value. - value: Value, -} - -/// A snapshot of a global variables values. This snapshot can be used later for restoring the -/// values to the preserved state. -/// -/// Technically, a snapshot stores only values of mutable global variables. This is because -/// immutable global variables always have the same values. -pub struct GlobalsSnapshot(Vec); - -impl GlobalsSnapshot { - /// Take a snapshot of global variables for a given instance. - pub fn take(instance_wrapper: &InstanceWrapper) -> Result { - let data = instance_wrapper.instance - .exports() - .enumerate() - .filter_map(|(index, export)| { - if export.name().starts_with("exported_internal_global") { - export.into_global().map( - |g| SavedValue { index, value: into_value(g.get()) } - ) - } else { None } - }) - .collect::>(); - - Ok(Self(data)) - } - - /// Apply the snapshot to the given instance. - /// - /// This instance must be the same that was used for creation of this snapshot. - pub fn apply(&self, instance_wrapper: &InstanceWrapper) -> Result<()> { - // This is a pointer over saved items, it moves forward when the loop value below takes over it's current value. - // Since both pointers (`current` and `index` below) are over ordered lists, they eventually hit all - // equal referenced values. - let mut current = 0; - for (index, export) in instance_wrapper.instance.exports().enumerate() { - if current >= self.0.len() { break; } - let current_saved = &self.0[current]; - if index < current_saved.index { continue; } - else if index > current_saved.index { current += 1; continue; } - else { - export.into_global() - .ok_or_else(|| Error::Other( - "Wrong instance in GlobalsSnapshot::apply: what should be global is not global.".to_string() - ))? - .set(into_wasmtime_val(current_saved.value)) - .map_err(|_e| Error::Other( - "Wrong instance in GlobalsSnapshot::apply: global saved type does not matched applied.".to_string() - ))?; - } - } - - Ok(()) - } -} diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 64ad5a1f4e49f..61bd02b27b33d 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -20,7 +20,7 @@ use crate::host::HostState; use crate::imports::{Imports, resolve_imports}; -use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, GlobalsSnapshot, EntryPoint}; +use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, EntryPoint}; use crate::state_holder; use std::rc::Rc; @@ -28,6 +28,7 @@ use std::sync::Arc; use std::path::Path; use sc_executor_common::{ error::{Result, WasmError}, + runtime_blob::{RuntimeBlob, GlobalsSnapshot}, wasm_runtime::{WasmModule, WasmInstance, InvokeMethod}, }; use sp_allocator::FreeingBumpHeapAllocator; @@ -62,7 +63,13 @@ impl WasmModule for WasmtimeRuntime { let instance_wrapper = InstanceWrapper::new(&store, &self.module_wrapper, &imports, self.heap_pages)?; let heap_base = instance_wrapper.extract_heap_base()?; - let globals_snapshot = GlobalsSnapshot::take(&instance_wrapper)?; + + // This function panics if the instance was created from a runtime blob different from which + // the mutable globals were collected. Here, it is easy to see that there is only a single + // runtime blob and thus it's the same that was used for both creating the instance and + // collecting the mutable globals. + let globals_snapshot = + GlobalsSnapshot::take(&self.module_wrapper.mutable_globals(), &instance_wrapper); Ok(Box::new(WasmtimeInstance { store, @@ -82,7 +89,7 @@ pub struct WasmtimeInstance { store: Store, module_wrapper: Arc, instance_wrapper: Rc, - globals_snapshot: GlobalsSnapshot, + globals_snapshot: GlobalsSnapshot, imports: Imports, heap_pages: u32, heap_base: u32, @@ -104,7 +111,7 @@ impl WasmInstance for WasmtimeInstance { .write_memory_from(Pointer::new(offset), contents) })?; - self.globals_snapshot.apply(&*self.instance_wrapper)?; + self.globals_snapshot.apply(&*self.instance_wrapper); perform_call( data, @@ -163,7 +170,7 @@ directory = \"{cache_dir}\" /// /// The `cache_path` designates where this executor implementation can put compiled artifacts. pub fn create_runtime( - code: &[u8], + runtime_blob: RuntimeBlob, heap_pages: u64, host_functions: Vec<&'static dyn Function>, allow_missing_func_imports: bool, @@ -182,7 +189,7 @@ pub fn create_runtime( } let engine = Engine::new(&config); - let module_wrapper = ModuleWrapper::new(&engine, code) + let module_wrapper = ModuleWrapper::new(&engine, runtime_blob) .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; Ok(WasmtimeRuntime { diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 1437c6f8509bf..2e42632258697 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -18,6 +18,9 @@ use std::ops::Range; +use sp_wasm_interface::Value; +use wasmtime::Val; + /// Construct a range from an offset to a data length after the offset. /// Returns None if the end of the range would exceed some maximum offset. pub fn checked_range(offset: usize, len: usize, max: usize) -> Option> { @@ -28,3 +31,26 @@ pub fn checked_range(offset: usize, len: usize, max: usize) -> Option Value { + match val { + Val::I32(v) => Value::I32(v), + Val::I64(v) => Value::I64(v), + Val::F32(f_bits) => Value::F32(f_bits), + Val::F64(f_bits) => Value::F64(f_bits), + _ => panic!("Given value type is unsupported by substrate"), + } +} + +/// Converts a sp_wasm_interface's [`Value`] into the corresponding variant in wasmtime's [`Val`]. +pub fn into_wasmtime_val(value: Value) -> wasmtime::Val { + match value { + Value::I32(v) => Val::I32(v), + Value::I64(v) => Val::I64(v), + Value::F32(f_bits) => Val::F32(f_bits), + Value::F64(f_bits) => Val::F64(f_bits), + } +} From 3d8a8049075d459f848987a7097849a521d7673a Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Wed, 3 Mar 2021 13:27:42 +0000 Subject: [PATCH 3/9] Opt-out from fast instance reuse --- .../common/src/runtime_blob/runtime_blob.rs | 9 +- client/executor/src/wasm_runtime.rs | 21 +- client/executor/wasmi/src/lib.rs | 7 +- .../executor/wasmtime/src/instance_wrapper.rs | 45 +-- client/executor/wasmtime/src/lib.rs | 7 +- client/executor/wasmtime/src/runtime.rs | 328 ++++++++++++++---- 6 files changed, 282 insertions(+), 135 deletions(-) diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs index e34d8d389e899..f69477e1ac29f 100644 --- a/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -18,6 +18,8 @@ use parity_wasm::elements::{DataSegment, Module as RawModule, deserialize_buffer, serialize}; +use crate::error::WasmError; + /// A bunch of information collected from a WebAssembly module. #[derive(Clone)] pub struct RuntimeBlob { @@ -28,9 +30,10 @@ impl RuntimeBlob { /// Create `RuntimeBlob` from the given wasm code. /// /// Returns `None` if the wasm code cannot be deserialized. - pub fn new(wasm_code: &[u8]) -> Option { - let raw_module: RawModule = deserialize_buffer(wasm_code).ok()?; - Some(Self { raw_module }) + pub fn new(wasm_code: &[u8]) -> Result { + let raw_module: RawModule = deserialize_buffer(wasm_code) + .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?; + Ok(Self { raw_module }) } /// Extract the data segments from the given wasm code. diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 21173cd451712..126a0db2990bb 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -301,18 +301,19 @@ pub fn create_wasm_runtime_with_code( } #[cfg(feature = "wasmtime")] WasmExecutionMethod::Compiled => { - let mut blob = sc_executor_common::runtime_blob::RuntimeBlob::new(code).unwrap(); - - // Substrate Runtime relies on quick wasm instance reuse and that requires working mutable - // globals snapshot taking. Hence we perform this instrumentation here. - blob.expose_mutable_globals(); - + let blob = sc_executor_common::runtime_blob::RuntimeBlob::new(code)?; sc_executor_wasmtime::create_runtime( - blob, - heap_pages, + sc_executor_wasmtime::CodeSupplyMode::Verbatim { blob }, + sc_executor_wasmtime::Config { + heap_pages: heap_pages as u32, + allow_missing_func_imports, + cache_path: cache_path.map(ToOwned::to_owned), + semantics: sc_executor_wasmtime::Semantics { + fast_instance_reuse: true, + stack_depth_metering: false, + }, + }, host_functions, - allow_missing_func_imports, - cache_path, ).map(|runtime| -> Arc { Arc::new(runtime) }) }, } diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index 81fc08865c400..0163e07e654bf 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -661,11 +661,8 @@ pub fn create_runtime( ) .map_err(|e| WasmError::Instantiation(e.to_string()))?; - let data_segments_snapshot = DataSegmentsSnapshot::take( - &RuntimeBlob::new(code) - .ok_or_else(|| WasmError::Other("cannot deserialize module".to_string()))?, - ) - .map_err(|e| WasmError::Other(e.to_string()))?; + let data_segments_snapshot = DataSegmentsSnapshot::take(&RuntimeBlob::new(code)?) + .map_err(|e| WasmError::Other(e.to_string()))?; let global_vals_snapshot = GlobalValsSnapshot::take(&instance); (data_segments_snapshot, global_vals_snapshot) diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index 556f35d03e603..fec88a472fb9b 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -25,48 +25,11 @@ use crate::imports::Imports; use std::{slice, marker}; use sc_executor_common::{ error::{Error, Result}, - runtime_blob::{self, ExposedMutableGlobalsSet, DataSegmentsSnapshot, RuntimeBlob}, + runtime_blob, wasm_runtime::InvokeMethod, }; use sp_wasm_interface::{Pointer, WordSize, Value}; -use wasmtime::{Engine, Instance, Module, Memory, Table, Val, Func, Extern, Global, Store}; - -pub struct ModuleWrapper { - module: Module, - mutable_globals: ExposedMutableGlobalsSet, - data_segments_snapshot: DataSegmentsSnapshot, -} - -impl ModuleWrapper { - pub fn new(engine: &Engine, runtime_blob: RuntimeBlob) -> Result { - let data_segments_snapshot = DataSegmentsSnapshot::take(&runtime_blob) - .map_err(|e| Error::from(format!("cannot take data segments snapshot: {}", e)))?; - - let mutable_globals = ExposedMutableGlobalsSet::collect(&runtime_blob); - - let bytecode = runtime_blob.serialize(); - let module = Module::new(engine, &bytecode) - .map_err(|e| Error::from(format!("cannot create module: {}", e)))?; - - Ok(Self { - module, - mutable_globals, - data_segments_snapshot, - }) - } - - pub fn module(&self) -> &Module { - &self.module - } - - pub fn mutable_globals(&self) -> &ExposedMutableGlobalsSet { - &self.mutable_globals - } - - pub fn data_segments_snapshot(&self) -> &DataSegmentsSnapshot { - &self.data_segments_snapshot - } -} +use wasmtime::{Instance, Module, Memory, Table, Val, Func, Extern, Global, Store}; /// Invoked entrypoint format. pub enum EntryPointType { @@ -192,8 +155,8 @@ fn extern_func(extern_: &Extern) -> Option<&Func> { impl InstanceWrapper { /// Create a new instance wrapper from the given wasm module. - pub fn new(store: &Store, module_wrapper: &ModuleWrapper, imports: &Imports, heap_pages: u32) -> Result { - let instance = Instance::new(store, &module_wrapper.module, &imports.externs) + pub fn new(store: &Store, module: &Module, imports: &Imports, heap_pages: u32) -> Result { + let instance = Instance::new(store, module, &imports.externs) .map_err(|e| Error::from(format!("cannot instantiate: {}", e)))?; let memory = match imports.memory_import_index { diff --git a/client/executor/wasmtime/src/lib.rs b/client/executor/wasmtime/src/lib.rs index db7776d4c5845..3679c15249653 100644 --- a/client/executor/wasmtime/src/lib.rs +++ b/client/executor/wasmtime/src/lib.rs @@ -17,12 +17,11 @@ // along with this program. If not, see . ///! Defines a `WasmRuntime` that uses the Wasmtime JIT to execute. - mod host; -mod runtime; -mod state_holder; mod imports; mod instance_wrapper; +mod runtime; +mod state_holder; mod util; -pub use runtime::create_runtime; +pub use runtime::{create_runtime, prepare_runtime_artifact, CodeSupplyMode, Config, Semantics}; diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 61bd02b27b33d..322f116ffbb6a 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -20,28 +20,52 @@ use crate::host::HostState; use crate::imports::{Imports, resolve_imports}; -use crate::instance_wrapper::{ModuleWrapper, InstanceWrapper, EntryPoint}; +use crate::instance_wrapper::{InstanceWrapper, EntryPoint}; use crate::state_holder; -use std::rc::Rc; +use std::{path::PathBuf, rc::Rc}; use std::sync::Arc; use std::path::Path; use sc_executor_common::{ error::{Result, WasmError}, - runtime_blob::{RuntimeBlob, GlobalsSnapshot}, + runtime_blob::{DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob}, wasm_runtime::{WasmModule, WasmInstance, InvokeMethod}, }; use sp_allocator::FreeingBumpHeapAllocator; use sp_runtime_interface::unpack_ptr_and_len; use sp_wasm_interface::{Function, Pointer, WordSize, Value}; -use wasmtime::{Config, Engine, Store}; +use wasmtime::{Engine, Store}; + +enum Strategy { + FastInstanceReuse { + instance_wrapper: Rc, + globals_snapshot: GlobalsSnapshot, + data_segments_snapshot: Arc, + heap_base: u32, + }, + RecreateInstance(InstanceCreator), +} + +struct InstanceCreator { + store: Store, + module: Arc, + imports: Arc, + heap_pages: u32, +} + +impl InstanceCreator { + fn instantiate(&self) -> Result { + InstanceWrapper::new(&self.store, &*self.module, &*self.imports, self.heap_pages) + } +} /// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code /// and execute the compiled code. pub struct WasmtimeRuntime { - module_wrapper: Arc, - heap_pages: u32, - allow_missing_func_imports: bool, + module: Arc, + mutable_globals: Option, + data_segments_snapshot: Option>, + config: Config, host_functions: Vec<&'static dyn Function>, engine: Engine, } @@ -52,47 +76,62 @@ impl WasmModule for WasmtimeRuntime { // Scan all imports, find the matching host functions, and create stubs that adapt arguments // and results. + // + // NOTE: Attentive reader may notice that this could've been moved in `WasmModule` creation. + // However, I am not sure if that's a good idea since it would be pushing our luck further + // by assuming that `Store` not only `Send` but also `Sync`. let imports = resolve_imports( &store, - self.module_wrapper.module(), + &self.module, &self.host_functions, - self.heap_pages, - self.allow_missing_func_imports, + self.config.heap_pages, + self.config.allow_missing_func_imports, )?; - let instance_wrapper = - InstanceWrapper::new(&store, &self.module_wrapper, &imports, self.heap_pages)?; - let heap_base = instance_wrapper.extract_heap_base()?; - - // This function panics if the instance was created from a runtime blob different from which - // the mutable globals were collected. Here, it is easy to see that there is only a single - // runtime blob and thus it's the same that was used for both creating the instance and - // collecting the mutable globals. - let globals_snapshot = - GlobalsSnapshot::take(&self.module_wrapper.mutable_globals(), &instance_wrapper); - - Ok(Box::new(WasmtimeInstance { - store, - instance_wrapper: Rc::new(instance_wrapper), - module_wrapper: Arc::clone(&self.module_wrapper), - imports, - globals_snapshot, - heap_pages: self.heap_pages, - heap_base, - })) + let strategy = if self.config.semantics.fast_instance_reuse { + let instance_wrapper = + InstanceWrapper::new(&store, &self.module, &imports, self.config.heap_pages)?; + let heap_base = instance_wrapper.extract_heap_base()?; + + let mut_globals = self + .mutable_globals + .as_ref() + .expect("mutable_globals are Some when fast_instance_reuse is set; qed"); + + // This function panics if the instance was created from a runtime blob different from which + // the mutable globals were collected. Here, it is easy to see that there is only a single + // runtime blob and thus it's the same that was used for both creating the instance and + // collecting the mutable globals. + let globals_snapshot = GlobalsSnapshot::take(mut_globals, &instance_wrapper); + + let data_segments_snapshot = self + .data_segments_snapshot + .clone() + .expect("data_segments_shanpshot is Some when fast_instance_reuse is set; qed"); + + Strategy::FastInstanceReuse { + instance_wrapper: Rc::new(instance_wrapper), + globals_snapshot, + data_segments_snapshot, + heap_base, + } + } else { + Strategy::RecreateInstance(InstanceCreator { + imports: Arc::new(imports), + module: self.module.clone(), + store, + heap_pages: self.config.heap_pages, + }) + }; + + Ok(Box::new(WasmtimeInstance { strategy })) } } /// A `WasmInstance` implementation that reuses compiled module and spawns instances /// to execute the compiled code. pub struct WasmtimeInstance { - store: Store, - module_wrapper: Arc, - instance_wrapper: Rc, - globals_snapshot: GlobalsSnapshot, - imports: Imports, - heap_pages: u32, - heap_base: u32, + strategy: Strategy, } // This is safe because `WasmtimeInstance` does not leak reference to `self.imports` @@ -101,29 +140,43 @@ unsafe impl Send for WasmtimeInstance {} impl WasmInstance for WasmtimeInstance { fn call(&self, method: InvokeMethod, data: &[u8]) -> Result> { - let entrypoint = self.instance_wrapper.resolve_entrypoint(method)?; - let allocator = FreeingBumpHeapAllocator::new(self.heap_base); - - self.module_wrapper - .data_segments_snapshot() - .apply(|offset, contents| { - self.instance_wrapper - .write_memory_from(Pointer::new(offset), contents) - })?; - - self.globals_snapshot.apply(&*self.instance_wrapper); - - perform_call( - data, - Rc::clone(&self.instance_wrapper), - entrypoint, - allocator, - ) + match &self.strategy { + Strategy::FastInstanceReuse { + instance_wrapper, + globals_snapshot, + data_segments_snapshot, + heap_base, + } => { + let entrypoint = instance_wrapper.resolve_entrypoint(method)?; + + data_segments_snapshot.apply(|offset, contents| { + instance_wrapper.write_memory_from(Pointer::new(offset), contents) + })?; + globals_snapshot.apply(&**instance_wrapper); + let allocator = FreeingBumpHeapAllocator::new(*heap_base); + + perform_call(data, Rc::clone(&instance_wrapper), entrypoint, allocator) + } + Strategy::RecreateInstance(instance_creator) => { + let instance_wrapper = instance_creator.instantiate()?; + let heap_base = instance_wrapper.extract_heap_base()?; + let entrypoint = instance_wrapper.resolve_entrypoint(method)?; + + let allocator = FreeingBumpHeapAllocator::new(heap_base); + perform_call(data, Rc::new(instance_wrapper), entrypoint, allocator) + } + } } fn get_global_const(&self, name: &str) -> Result> { - let instance = InstanceWrapper::new(&self.store, &self.module_wrapper, &self.imports, self.heap_pages)?; - instance.get_global_val(name) + match &self.strategy { + Strategy::FastInstanceReuse { + instance_wrapper, .. + } => instance_wrapper.get_global_val(name), + Strategy::RecreateInstance(instance_creator) => { + instance_creator.instantiate()?.get_global_val(name) + } + } } } @@ -132,7 +185,7 @@ impl WasmInstance for WasmtimeInstance { /// In case of an error the caching will not be enabled. fn setup_wasmtime_caching( cache_path: &Path, - config: &mut Config, + config: &mut wasmtime::Config, ) -> std::result::Result<(), String> { use std::fs; @@ -165,22 +218,99 @@ directory = \"{cache_dir}\" Ok(()) } +fn common_config() -> wasmtime::Config { + let mut config = wasmtime::Config::new(); + config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize); + config +} + +pub struct Semantics { + /// Enabling this will lead to some optimization shenanigans that make calling [`WasmInstance`] + /// extermely fast. + /// + /// Primarily this is achieved by not recreating the instance for each call and performing a + /// bare minimum clean up: reapplying the data segments and restoring the values for global + /// variables. The vast majority of the linear memory is not restored, meaning that effects + /// of previous executions on the same [`WasmInstance`] can be observed there. + /// + /// This is not a problem for a standard substrate runtime execution because it's up to the + /// runtime itself to make sure that it doesn't involve any non-determinism. + /// + /// Since this feature depends on instrumentation, it can be set only if [`CodeSupplyMode::Verbatim`] + /// is used. + pub fast_instance_reuse: bool, + + /// The WebAssembly standard defines a call/value stack but it doesn't say anything about its + /// size except that it has to be finite. The implementations are free to choose their own notion + /// of limit: some may count the number of calls or values, others would rely on the host machine + /// stack and trap on reaching a guard page. + /// + /// This obviously is a source of non-determinism during execution. This feature can be used + /// to instrument the code so that it will count the depth of execution in some deterministic + /// way (the machine stack limit should be so high that the deterministic limit always triggers + /// first). + /// + /// See [here][stack_height] for more details of the instrumentation + /// + /// Since this feature depends on instrumentation, it can be set only if [`CodeSupplyMode::Verbatim`] + /// is used. + /// + /// [stack_height]: https://github.com/paritytech/wasm-utils/blob/d9432baf/src/stack_height/mod.rs#L1-L50 + pub stack_depth_metering: bool, + // Other things like nan canonicalization can be added here. +} + +pub struct Config { + /// The number of wasm pages to be mounted after instantiation. + pub heap_pages: u32, + + /// The WebAssembly standard requires all imports of an instantiated module to be resolved, + /// othewise, the instantiation fails. If this option is set to `true`, then this behavior is + /// overriden and imports that are requested by the module and not provided by the host functions + /// will be resolved using stubs. These stubs will trap upon a call. + pub allow_missing_func_imports: bool, + + /// A directory in which wasmtime can store its compiled artifacts cache. + pub cache_path: Option, + + /// Tuning of various semantics of the wasmtime executor. + pub semantics: Semantics, +} + +pub enum CodeSupplyMode<'a> { + /// The runtime is instantiated using the given runtime blob. + Verbatim { + // Rationale to take the `RuntimeBlob` here is so that the client will be able to reuse + // the blob e.g. if they did a prevalidation. If they didn't they can `RuntimeBlob` + // instance and it will be used anyway in most cases, because we are going to do at least + // some instrumentations for both anticipated paths: substrate execution and PVF execution. + // + // Should there raise a need in performing no instrumentation and the client doesn't need + // to do any checks, then we can provide a `Cow` like semantics here: if we need the blob and + // the user got `RuntimeBlob` then extract it, or otherwise create it from the given + // bytecode. + blob: RuntimeBlob, + }, + + /// The code is supplied in a form of a compiled artifact. + /// + /// This assumes that the code is already prepared for execution and the same `Config` was used. + Artifact { compiled_artifact: &'a [u8] }, +} + /// Create a new `WasmtimeRuntime` given the code. This function performs translation from Wasm to /// machine code, which can be computationally heavy. /// /// The `cache_path` designates where this executor implementation can put compiled artifacts. pub fn create_runtime( - runtime_blob: RuntimeBlob, - heap_pages: u64, + code_supply_mode: CodeSupplyMode<'_>, + config: Config, host_functions: Vec<&'static dyn Function>, - allow_missing_func_imports: bool, - cache_path: Option<&Path>, ) -> std::result::Result { // Create the engine, store and finally the module from the given code. - let mut config = Config::new(); - config.cranelift_opt_level(wasmtime::OptLevel::SpeedAndSize); - if let Some(cache_path) = cache_path { - if let Err(reason) = setup_wasmtime_caching(cache_path, &mut config) { + let mut wasmtime_config = common_config(); + if let Some(ref cache_path) = config.cache_path { + if let Err(reason) = setup_wasmtime_caching(cache_path, &mut wasmtime_config) { log::warn!( "failed to setup wasmtime cache. Performance may degrade significantly: {}.", reason, @@ -188,19 +318,73 @@ pub fn create_runtime( } } - let engine = Engine::new(&config); - let module_wrapper = ModuleWrapper::new(&engine, runtime_blob) - .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; + let engine = Engine::new(&wasmtime_config); + + let (module, data_segments_snapshot, mutable_globals) = match code_supply_mode { + CodeSupplyMode::Verbatim { mut blob } => { + instrument(&mut blob, &config.semantics); + + if config.semantics.fast_instance_reuse { + let data_segments_snapshot = DataSegmentsSnapshot::take(&blob).map_err(|e| { + WasmError::Other(format!("cannot take data segments snapshot: {}", e)) + })?; + + let mutable_globals = ExposedMutableGlobalsSet::collect(&blob); + + let module = wasmtime::Module::new(&engine, &blob.serialize()) + .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; + + (module, Some(data_segments_snapshot), Some(mutable_globals)) + } else { + let module = wasmtime::Module::new(&engine, &blob.serialize()) + .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; + (module, None, None) + } + } + CodeSupplyMode::Artifact { compiled_artifact } => { + let module = wasmtime::Module::deserialize(&engine, compiled_artifact) + .map_err(|e| WasmError::Other(format!("cannot deserialize module: {}", e)))?; + + (module, None, None) + } + }; Ok(WasmtimeRuntime { - module_wrapper: Arc::new(module_wrapper), - heap_pages: heap_pages as u32, - allow_missing_func_imports, + module: Arc::new(module), + mutable_globals, + data_segments_snapshot: data_segments_snapshot.map(Arc::new), + config, host_functions, engine, }) } +fn instrument(blob: &mut RuntimeBlob, semantics: &Semantics) { + if semantics.fast_instance_reuse { + blob.expose_mutable_globals(); + } + + if semantics.stack_depth_metering { + todo!() + } +} + +/// Takes a [`RuntimeBlob`] and precompiles it returning the serialized result of compilation. It +/// can then be used for calling [`create_runtime`] avoiding long compilation times. +pub fn prepare_runtime_artifact( + mut blob: RuntimeBlob, + semantics: &Semantics, +) -> std::result::Result, WasmError> { + instrument(&mut blob, semantics); + + let engine = Engine::new(&common_config()); + let module = wasmtime::Module::new(&engine, &blob.serialize()) + .map_err(|e| WasmError::Other(format!("cannot compile module: {}", e)))?; + module + .serialize() + .map_err(|e| WasmError::Other(format!("cannot serialize module: {}", e))) +} + fn perform_call( data: &[u8], instance_wrapper: Rc, From 395992c9d6668064a2a22bd981edc61f76776d14 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Thu, 18 Mar 2021 13:28:24 +0000 Subject: [PATCH 4/9] Minor clean up --- .../common/src/runtime_blob/runtime_blob.rs | 3 ++- client/executor/wasmtime/src/runtime.rs | 2 +- client/executor/wasmtime/src/util.rs | 23 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs index f69477e1ac29f..f93b4723b23d7 100644 --- a/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -89,6 +89,7 @@ impl RuntimeBlob { /// Consumes this runtime blob and serializes it. pub fn serialize(self) -> Vec { - serialize(self.raw_module).unwrap() + serialize(self.raw_module) + .expect("serializing into a vec should succeed; qed") } } diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 322f116ffbb6a..d2e0b43bba28a 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -365,7 +365,7 @@ fn instrument(blob: &mut RuntimeBlob, semantics: &Semantics) { } if semantics.stack_depth_metering { - todo!() + // TODO: implement deterministic stack metering https://github.com/paritytech/substrate/issues/8393 } } diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 2e42632258697..72e6cfa0ef8aa 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -19,7 +19,6 @@ use std::ops::Range; use sp_wasm_interface::Value; -use wasmtime::Val; /// Construct a range from an offset to a data length after the offset. /// Returns None if the end of the range would exceed some maximum offset. @@ -32,25 +31,25 @@ pub fn checked_range(offset: usize, len: usize, max: usize) -> Option Value { +pub fn from_wasmtime_val(val: wasmtime::Val) -> Value { match val { - Val::I32(v) => Value::I32(v), - Val::I64(v) => Value::I64(v), - Val::F32(f_bits) => Value::F32(f_bits), - Val::F64(f_bits) => Value::F64(f_bits), + wasmtime::Val::I32(v) => Value::I32(v), + wasmtime::Val::I64(v) => Value::I64(v), + wasmtime::Val::F32(f_bits) => Value::F32(f_bits), + wasmtime::Val::F64(f_bits) => Value::F64(f_bits), _ => panic!("Given value type is unsupported by substrate"), } } -/// Converts a sp_wasm_interface's [`Value`] into the corresponding variant in wasmtime's [`Val`]. +/// Converts a sp_wasm_interface's [`Value`] into the corresponding variant in wasmtime's [`wasmtime::Val`]. pub fn into_wasmtime_val(value: Value) -> wasmtime::Val { match value { - Value::I32(v) => Val::I32(v), - Value::I64(v) => Val::I64(v), - Value::F32(f_bits) => Val::F32(f_bits), - Value::F64(f_bits) => Val::F64(f_bits), + Value::I32(v) => wasmtime::Val::I32(v), + Value::I64(v) => wasmtime::Val::I64(v), + Value::F32(f_bits) => wasmtime::Val::F32(f_bits), + Value::F64(f_bits) => wasmtime::Val::F64(f_bits), } } From ca8d2aa598af988f832f54aad442359109e5f92f Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Thu, 18 Mar 2021 13:36:52 +0000 Subject: [PATCH 5/9] Spaces --- client/executor/src/wasm_runtime.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/executor/src/wasm_runtime.rs b/client/executor/src/wasm_runtime.rs index 126a0db2990bb..268a060182876 100644 --- a/client/executor/src/wasm_runtime.rs +++ b/client/executor/src/wasm_runtime.rs @@ -305,12 +305,12 @@ pub fn create_wasm_runtime_with_code( sc_executor_wasmtime::create_runtime( sc_executor_wasmtime::CodeSupplyMode::Verbatim { blob }, sc_executor_wasmtime::Config { - heap_pages: heap_pages as u32, - allow_missing_func_imports, - cache_path: cache_path.map(ToOwned::to_owned), - semantics: sc_executor_wasmtime::Semantics { - fast_instance_reuse: true, - stack_depth_metering: false, + heap_pages: heap_pages as u32, + allow_missing_func_imports, + cache_path: cache_path.map(ToOwned::to_owned), + semantics: sc_executor_wasmtime::Semantics { + fast_instance_reuse: true, + stack_depth_metering: false, }, }, host_functions, From b361477a1a06cd602e1f8641bedfcb9d8a94ade7 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Thu, 18 Mar 2021 14:05:05 +0000 Subject: [PATCH 6/9] Docs clean up --- .../executor/common/src/runtime_blob/globals_snapshot.rs | 7 ++++--- client/executor/common/src/runtime_blob/runtime_blob.rs | 6 ++---- client/executor/wasmtime/src/runtime.rs | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/client/executor/common/src/runtime_blob/globals_snapshot.rs b/client/executor/common/src/runtime_blob/globals_snapshot.rs index 579f3f8855d95..a43814e1d4e14 100644 --- a/client/executor/common/src/runtime_blob/globals_snapshot.rs +++ b/client/executor/common/src/runtime_blob/globals_snapshot.rs @@ -28,11 +28,12 @@ struct SavedValue { /// An adapter for a wasm module instance that is focused on getting and setting globals. pub trait InstanceGlobals { - /// A handle to a global. + /// A handle to a global which can be used to get or set a global variable. This is supposed to + /// be a lightweight handle, like an index or an Rc-like smart-pointer, which is cheap to clone. type Global: Clone; /// Get a handle to a global by it's export name. /// - /// The export is guaranteed to exist, which points to a mutable global. + /// The requested export is must exist in the exported list, and it should be a mutable global. fn get_global(&self, export_name: &str) -> Self::Global; /// Get the current value of the global. fn get_global_value(&self, global: &Self::Global) -> sp_wasm_interface::Value; @@ -62,7 +63,7 @@ impl ExposedMutableGlobalsSet { } } -/// A snapshot of a global variables values. This snapshot can be used later for restoring the +/// A snapshot of a global variables values. This snapshot can be later used for restoring the /// values to the preserved state. /// /// Technically, a snapshot stores only values of mutable global variables. This is because diff --git a/client/executor/common/src/runtime_blob/runtime_blob.rs b/client/executor/common/src/runtime_blob/runtime_blob.rs index f93b4723b23d7..d90a48fde0c81 100644 --- a/client/executor/common/src/runtime_blob/runtime_blob.rs +++ b/client/executor/common/src/runtime_blob/runtime_blob.rs @@ -29,7 +29,7 @@ pub struct RuntimeBlob { impl RuntimeBlob { /// Create `RuntimeBlob` from the given wasm code. /// - /// Returns `None` if the wasm code cannot be deserialized. + /// Returns `Err` if the wasm code cannot be deserialized. pub fn new(wasm_code: &[u8]) -> Result { let raw_module: RawModule = deserialize_buffer(wasm_code) .map_err(|e| WasmError::Other(format!("cannot deserialize module: {:?}", e)))?; @@ -37,8 +37,6 @@ impl RuntimeBlob { } /// Extract the data segments from the given wasm code. - /// - /// Returns `Err` if the given wasm code cannot be deserialized. pub(super) fn data_segments(&self) -> Vec { self.raw_module .data_section() @@ -63,7 +61,7 @@ impl RuntimeBlob { .unwrap_or(0) } - /// Make sure that the mutable globals are exported + /// Perform an instrumentation that makes sure that the mutable globals are exported. pub fn expose_mutable_globals(&mut self) { pwasm_utils::export_mutable_globals(&mut self.raw_module, "exported_internal_global"); } diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index d2e0b43bba28a..4115525479abd 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -334,7 +334,7 @@ pub fn create_runtime( let module = wasmtime::Module::new(&engine, &blob.serialize()) .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; - (module, Some(data_segments_snapshot), Some(mutable_globals)) + (module, Some(Arc::new(data_segments_snapshot)), Some(mutable_globals)) } else { let module = wasmtime::Module::new(&engine, &blob.serialize()) .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; @@ -352,7 +352,7 @@ pub fn create_runtime( Ok(WasmtimeRuntime { module: Arc::new(module), mutable_globals, - data_segments_snapshot: data_segments_snapshot.map(Arc::new), + data_segments_snapshot, config, host_functions, engine, From ca82b7a70900655245c2bcdff58822bf1807f36c Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 30 Mar 2021 00:09:32 +0200 Subject: [PATCH 7/9] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bastian Köcher --- client/executor/common/src/runtime_blob/mod.rs | 2 +- client/executor/wasmtime/src/util.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/executor/common/src/runtime_blob/mod.rs b/client/executor/common/src/runtime_blob/mod.rs index dfdb74fcc257d..372df7bd97eb7 100644 --- a/client/executor/common/src/runtime_blob/mod.rs +++ b/client/executor/common/src/runtime_blob/mod.rs @@ -38,7 +38,7 @@ //! instrumentation looks like a good solution. //! //! Stack depth metering is needed to make a wasm blob -//! execution deterministic, which is in turn is needed by the Parachain Validation Function in Polkadot. +//! execution deterministic, which in turn is needed by the Parachain Validation Function in Polkadot. //! //! ## Inspection //! diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 72e6cfa0ef8aa..9f7e9cf6668c2 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -40,7 +40,7 @@ pub fn from_wasmtime_val(val: wasmtime::Val) -> Value { wasmtime::Val::I64(v) => Value::I64(v), wasmtime::Val::F32(f_bits) => Value::F32(f_bits), wasmtime::Val::F64(f_bits) => Value::F64(f_bits), - _ => panic!("Given value type is unsupported by substrate"), + v => panic!("Given value type is unsupported by Substrate: {:?}"), } } From 853c035d8e9cee9415c6db3006e4ca3c4d8d4402 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 30 Mar 2021 15:52:00 +0000 Subject: [PATCH 8/9] Factor out the expects --- client/executor/wasmtime/src/runtime.rs | 42 ++++++++++++------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 4115525479abd..103b37a681e8b 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -59,12 +59,17 @@ impl InstanceCreator { } } +/// Data required for creating instances with the fast instance reuse strategy. +struct InstanceSnapshotData { + mutable_globals: ExposedMutableGlobalsSet, + data_segments_snapshot: Arc, +} + /// A `WasmModule` implementation using wasmtime to compile the runtime module to machine code /// and execute the compiled code. pub struct WasmtimeRuntime { module: Arc, - mutable_globals: Option, - data_segments_snapshot: Option>, + snapshot_data: Option, config: Config, host_functions: Vec<&'static dyn Function>, engine: Engine, @@ -88,31 +93,21 @@ impl WasmModule for WasmtimeRuntime { self.config.allow_missing_func_imports, )?; - let strategy = if self.config.semantics.fast_instance_reuse { + let strategy = if let Some(ref snapshot_data) = self.snapshot_data { let instance_wrapper = InstanceWrapper::new(&store, &self.module, &imports, self.config.heap_pages)?; let heap_base = instance_wrapper.extract_heap_base()?; - let mut_globals = self - .mutable_globals - .as_ref() - .expect("mutable_globals are Some when fast_instance_reuse is set; qed"); - // This function panics if the instance was created from a runtime blob different from which // the mutable globals were collected. Here, it is easy to see that there is only a single // runtime blob and thus it's the same that was used for both creating the instance and // collecting the mutable globals. - let globals_snapshot = GlobalsSnapshot::take(mut_globals, &instance_wrapper); - - let data_segments_snapshot = self - .data_segments_snapshot - .clone() - .expect("data_segments_shanpshot is Some when fast_instance_reuse is set; qed"); + let globals_snapshot = GlobalsSnapshot::take(&snapshot_data.mutable_globals, &instance_wrapper); Strategy::FastInstanceReuse { instance_wrapper: Rc::new(instance_wrapper), globals_snapshot, - data_segments_snapshot, + data_segments_snapshot: snapshot_data.data_segments_snapshot.clone(), heap_base, } } else { @@ -281,7 +276,7 @@ pub enum CodeSupplyMode<'a> { /// The runtime is instantiated using the given runtime blob. Verbatim { // Rationale to take the `RuntimeBlob` here is so that the client will be able to reuse - // the blob e.g. if they did a prevalidation. If they didn't they can `RuntimeBlob` + // the blob e.g. if they did a prevalidation. If they didn't they can pass a `RuntimeBlob` // instance and it will be used anyway in most cases, because we are going to do at least // some instrumentations for both anticipated paths: substrate execution and PVF execution. // @@ -320,7 +315,7 @@ pub fn create_runtime( let engine = Engine::new(&wasmtime_config); - let (module, data_segments_snapshot, mutable_globals) = match code_supply_mode { + let (module, snapshot_data) = match code_supply_mode { CodeSupplyMode::Verbatim { mut blob } => { instrument(&mut blob, &config.semantics); @@ -328,31 +323,34 @@ pub fn create_runtime( let data_segments_snapshot = DataSegmentsSnapshot::take(&blob).map_err(|e| { WasmError::Other(format!("cannot take data segments snapshot: {}", e)) })?; + let data_segments_snapshot = Arc::new(data_segments_snapshot); let mutable_globals = ExposedMutableGlobalsSet::collect(&blob); let module = wasmtime::Module::new(&engine, &blob.serialize()) .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; - (module, Some(Arc::new(data_segments_snapshot)), Some(mutable_globals)) + (module, Some(InstanceSnapshotData { + data_segments_snapshot, + mutable_globals, + })) } else { let module = wasmtime::Module::new(&engine, &blob.serialize()) .map_err(|e| WasmError::Other(format!("cannot create module: {}", e)))?; - (module, None, None) + (module, None) } } CodeSupplyMode::Artifact { compiled_artifact } => { let module = wasmtime::Module::deserialize(&engine, compiled_artifact) .map_err(|e| WasmError::Other(format!("cannot deserialize module: {}", e)))?; - (module, None, None) + (module, None) } }; Ok(WasmtimeRuntime { module: Arc::new(module), - mutable_globals, - data_segments_snapshot, + snapshot_data, config, host_functions, engine, From 4a813c9d4559b9d946c3bfe8ed5e8b55027cc268 Mon Sep 17 00:00:00 2001 From: Sergei Shulepov Date: Tue, 30 Mar 2021 16:22:37 +0000 Subject: [PATCH 9/9] Fix the suggestion --- client/executor/wasmtime/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 9f7e9cf6668c2..c294f66b5017f 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -40,7 +40,7 @@ pub fn from_wasmtime_val(val: wasmtime::Val) -> Value { wasmtime::Val::I64(v) => Value::I64(v), wasmtime::Val::F32(f_bits) => Value::F32(f_bits), wasmtime::Val::F64(f_bits) => Value::F64(f_bits), - v => panic!("Given value type is unsupported by Substrate: {:?}"), + v => panic!("Given value type is unsupported by Substrate: {:?}", v), } }