mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 23:57:56 +00:00
3da069e359
* WIP Forked at:d6aa8e954cParent branch: origin/master * Rename IntoConfiguration to CliConfiguration * Renamed into_configuration to create_configuration * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * Move keystore params to its own module * Use in-memory keystore even for build-spec * Enforce proper value for node name * dev_key_seed * Telemetry endpoints * rustfmt * Converted all RunCmd * rustfmt * Added export-blocks * Missed something * Removed config_path in NetworkConfiguration (not used) * Fixed warnings * public_addresses is used but never set, keeping it * Merge Configuration.node and NetworkConfiguration.node_name ...because they are the same thing * Added: import-blocks * Adding a proc_macro to help impl SubstrateCli * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * Re-export spec_factory from sc_cli * Re-added all the commands * Refactored node_key_params * Fixed previous refucktoring * Clean-up and removed full_version() * Renamed get_is_dev to not confuse with Configuration field * Fixed sc-cli-derive example * Fixing tests * Fixing tests and removing some (will re-add later) * Fixing more tests * Removes the need of type parameter * Converting bin/node and simplifying API * Converting more * Converting last command * WIP Forked at:d6aa8e954cParent branch: origin/master * Fixing tests and added default for WasmExecutionMethod * Fixing stuff * Fixed something I broke oops * Update Cargo.lock * Moving things around * Convert everything to Result * Added new macros to simplify the impl of CliConfiguration * Added a macro to generate CliConfiguration automatically for subcommands * Revert... too many macros (this one is not really useful) This reverts commit 9c516dd38b40fbc420b02c1f8e61d5b2b1a4e434. * Renamed is_dev to get_is_dev Good enough for now * Fixed name roles (this is plural, not singular) * Clean-up * Re-export NodeKeyConfig and TelemetryEndpoints from sc_service * Improve styling/formatting * Added copyrights * Added doc and fixed warnings * Added myself to code owners * Yes it is needed according to the history * Revert formatting * Fixing conflict * Updated build.rs * Cargo.lock * Clean-up * Update client/cli-derive/Cargo.toml Co-Authored-By: Seun Lanlege <seunlanlege@gmail.com> * Fail if using proc_macro and build.rs is not set properly * Dropped all get_ in front of methods * Clean-up * Fixing proc macro missing env var * Get the configuration inside the Runtime (needed for polkadot) * Clean-up * Get is_dev from argument like the others * Get chain ID instead of chain spec from shared params * &self is passed to spec_factory/load_spec * Wrong text * Fix example * Officialize macro and made a cool doc * Renamed spec_factory to load_spec (substrate_cli_configuration) * Removed not so useful ChainSpec * Renamed SubstrateCLI to SubstrateCli * Added changelog for impl_version being full now * Renamed Runtime to Runner * Update changelog to show example * Removed option on database cache size * WIP Forked at:d6aa8e954cParent branch: origin/master * Fix on removal of option * typo * Clean-up imports * Added info in Cargo.toml * typo * remarks * Moved function for build.rs to substrate-build-script-utils * Fixed example & test of cli-derive * Moved function for build.rs to substrate-build-script-utils * Renamed substrate_cli_configuration to substrate_cli oops It implements SubstrateCli not CliConfiguration! * Added documentation and wrapper macro * Removed option on database cache size * Removed option on database cache size * Clean-up * Reduce risk of errors due to typos * Removed option on database cache size * Added NOTE as suggested * Added doc as suggested * Fixed test * typo * renamed runtime to runner * Fixed weird argument * More commas * Moved client/cli-derive to client/cli/derive * Added 7 tests for the macros * Improve error message * Upgrade assert_cmd * Fixing missing stuff * Fixed unused import * Improve SubstrateCli doc * Applied suggestions * Fix and clean-up imports * Started replacing macros WIP * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * Started removing substrate_cli * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * WIP Forked at:d6aa8e954cParent branch: origin/master * fixed bug introduced while refactoring * Renamed NetworkConfigurationParams to NetworkParams for consistency sake * Fixed test * Update client/cli/src/commands/runcmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/runcmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/export_blocks_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/check_block_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update bin/node/cli/src/command.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update bin/node/cli/src/command.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/export_blocks_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Revert "Update client/cli/src/commands/export_blocks_cmd.rs" This reverts commit 5906776953392c02beac6bc0bf50f8cbe1a12a01. * Revert "Update client/cli/src/commands/check_block_cmd.rs" This reverts commit f705f42b7f3d732be001141afee210fe46a1ef47. * Revert "Update client/cli/src/commands/export_blocks_cmd.rs" This reverts commit 8d57c0550164449e6eb2d3bacb04c750c714fcea. * Revert "Update client/cli/src/commands/runcmd.rs" This reverts commit 93e74cf5d2e1c0dc49cdff8608d59fc40fc59338. * Revert "Update client/cli/src/commands/runcmd.rs" This reverts commit 11d527ba345c0d79f0d3b5b071933d95474d0614. * Update client/cli/src/commands/export_blocks_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/import_blocks_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/purge_chain_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Changed ::sc_cli to $crate in the macro * fixed tests * fixed conflicts * Fixing test * Update client/cli/src/commands/purge_chain_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/params/pruning_params.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Remove comment as suggested * Apply suggestion * Update client/cli/src/commands/purge_chain_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/purge_chain_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/commands/purge_chain_cmd.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update utils/frame/benchmarking-cli/src/command.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/runner.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/runner.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/runner.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/params/pruning_params.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/params/node_key_params.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/params/network_params.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/lib.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Update client/cli/src/config.rs Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * Added doc * Fixed error introduced after applying suggestion * Revert "Update client/cli/src/params/pruning_params.rs" This reverts commit 0574d06a4f1efd86e94c1214420a12e7a4be0099. * Print error * Apply suggestions from code review * Remove useless Results * Fixed CI failing on polkadot approval Co-authored-by: Seun Lanlege <seunlanlege@gmail.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
363 lines
10 KiB
Rust
363 lines
10 KiB
Rust
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
|
|
// This file is part of Substrate.
|
|
|
|
// Substrate 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.
|
|
|
|
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Traits and accessor functions for calling into the Substrate Wasm runtime.
|
|
//!
|
|
//! The primary means of accessing the runtimes is through a cache which saves the reusable
|
|
//! components of the runtime that are expensive to initialize.
|
|
|
|
use std::sync::Arc;
|
|
use crate::error::{Error, WasmError};
|
|
use parking_lot::Mutex;
|
|
use codec::Decode;
|
|
use sp_core::traits::{Externalities, RuntimeCode, FetchRuntimeCode};
|
|
use sp_version::RuntimeVersion;
|
|
use std::panic::AssertUnwindSafe;
|
|
use sc_executor_common::wasm_runtime::{WasmModule, WasmInstance};
|
|
|
|
use sp_wasm_interface::Function;
|
|
|
|
/// 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.
|
|
#[cfg(feature = "wasmtime")]
|
|
Compiled,
|
|
}
|
|
|
|
impl Default for WasmExecutionMethod {
|
|
fn default() -> WasmExecutionMethod {
|
|
WasmExecutionMethod::Interpreted
|
|
}
|
|
}
|
|
|
|
/// A Wasm runtime object along with its cached runtime version.
|
|
struct VersionedRuntime {
|
|
/// Runtime code hash.
|
|
code_hash: Vec<u8>,
|
|
/// Wasm runtime type.
|
|
wasm_method: WasmExecutionMethod,
|
|
/// Shared runtime that can spawn instances.
|
|
module: Box<dyn WasmModule>,
|
|
/// The number of WebAssembly heap pages this instance was created with.
|
|
heap_pages: u64,
|
|
/// Runtime version according to `Core_version` if any.
|
|
version: Option<RuntimeVersion>,
|
|
/// Cached instance pool.
|
|
instances: Vec<Mutex<Option<Box<dyn WasmInstance>>>>,
|
|
}
|
|
|
|
impl VersionedRuntime {
|
|
/// Run the given closure `f` with an instance of this runtime.
|
|
fn with_instance<'c, R, F>(
|
|
&self,
|
|
ext: &mut dyn Externalities,
|
|
f: F,
|
|
) -> Result<R, Error>
|
|
where F: FnOnce(
|
|
&dyn WasmInstance,
|
|
Option<&RuntimeVersion>,
|
|
&mut dyn Externalities)
|
|
-> Result<R, Error>,
|
|
{
|
|
// Find a free instance
|
|
let instance = self.instances
|
|
.iter()
|
|
.enumerate()
|
|
.find_map(|(index, i)| i.try_lock().map(|i| (index, i)));
|
|
|
|
match instance {
|
|
Some((index, mut locked)) => {
|
|
let (instance, new_inst) = locked.take()
|
|
.map(|r| Ok((r, false)))
|
|
.unwrap_or_else(|| self.module.new_instance().map(|i| (i, true)))?;
|
|
|
|
let result = f(&*instance, self.version.as_ref(), ext);
|
|
if let Err(e) = &result {
|
|
if new_inst {
|
|
log::warn!(
|
|
target: "wasm-runtime",
|
|
"Fresh runtime instance failed with {:?}",
|
|
e,
|
|
)
|
|
} else {
|
|
log::warn!(
|
|
target: "wasm-runtime",
|
|
"Evicting failed runtime instance: {:?}",
|
|
e,
|
|
);
|
|
}
|
|
} else {
|
|
*locked = Some(instance);
|
|
|
|
if new_inst {
|
|
log::debug!(
|
|
target: "wasm-runtime",
|
|
"Allocated WASM instance {}/{}",
|
|
index + 1,
|
|
self.instances.len(),
|
|
);
|
|
}
|
|
}
|
|
|
|
result
|
|
},
|
|
None => {
|
|
log::warn!(target: "wasm-runtime", "Ran out of free WASM instances");
|
|
|
|
// Allocate a new instance
|
|
let instance = self.module.new_instance()?;
|
|
|
|
f(&*instance, self.version.as_ref(), ext)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const MAX_RUNTIMES: usize = 2;
|
|
|
|
/// Cache for the runtimes.
|
|
///
|
|
/// When an instance is requested for the first time it is added to this cache. Metadata is kept
|
|
/// with the instance so that it can be efficiently reinitialized.
|
|
///
|
|
/// When using the Wasmi interpreter execution method, the metadata includes the initial memory and
|
|
/// values of mutable globals. Follow-up requests to fetch a runtime return this one instance with
|
|
/// the memory reset to the initial memory. So, one runtime instance is reused for every fetch
|
|
/// request.
|
|
///
|
|
/// The size of cache is equal to `MAX_RUNTIMES`.
|
|
pub struct RuntimeCache {
|
|
/// A cache of runtimes along with metadata.
|
|
///
|
|
/// Runtimes sorted by recent usage. The most recently used is at the front.
|
|
runtimes: Mutex<[Option<Arc<VersionedRuntime>>; MAX_RUNTIMES]>,
|
|
/// The size of the instances cache for each runtime.
|
|
max_runtime_instances: usize,
|
|
}
|
|
|
|
impl RuntimeCache {
|
|
/// Creates a new instance of a runtimes cache.
|
|
pub fn new(max_runtime_instances: usize) -> RuntimeCache {
|
|
RuntimeCache {
|
|
runtimes: Default::default(),
|
|
max_runtime_instances,
|
|
}
|
|
}
|
|
|
|
/// Prepares a WASM module instance and executes given function for it.
|
|
///
|
|
/// This uses internal cache to find avaiable instance or create a new one.
|
|
/// # Parameters
|
|
///
|
|
/// `code` - Provides external code or tells the executor to fetch it from storage.
|
|
///
|
|
/// `runtime_code` - The runtime wasm code used setup the runtime.
|
|
///
|
|
/// `default_heap_pages` - Number of 64KB pages to allocate for Wasm execution.
|
|
///
|
|
/// `wasm_method` - Type of WASM backend to use.
|
|
///
|
|
/// `host_functions` - The host functions that should be registered for the Wasm runtime.
|
|
///
|
|
/// `allow_missing_func_imports` - Ignore missing function imports.
|
|
///
|
|
/// `max_runtime_instances` - The size of the instances cache.
|
|
///
|
|
/// `f` - Function to execute.
|
|
///
|
|
/// # Returns result of `f` wrapped in an additonal result.
|
|
/// In case of failure one of two errors can be returned:
|
|
///
|
|
/// `Err::InvalidCode` is returned for runtime code issues.
|
|
///
|
|
/// `Error::InvalidMemoryReference` is returned if no memory export with the
|
|
/// identifier `memory` can be found in the runtime.
|
|
pub fn with_instance<'c, R, F>(
|
|
&self,
|
|
runtime_code: &'c RuntimeCode<'c>,
|
|
ext: &mut dyn Externalities,
|
|
wasm_method: WasmExecutionMethod,
|
|
default_heap_pages: u64,
|
|
host_functions: &[&'static dyn Function],
|
|
allow_missing_func_imports: bool,
|
|
f: F,
|
|
) -> Result<Result<R, Error>, Error>
|
|
where F: FnOnce(
|
|
&dyn WasmInstance,
|
|
Option<&RuntimeVersion>,
|
|
&mut dyn Externalities)
|
|
-> Result<R, Error>,
|
|
{
|
|
let code_hash = &runtime_code.hash;
|
|
let heap_pages = runtime_code.heap_pages.unwrap_or(default_heap_pages);
|
|
|
|
let mut runtimes = self.runtimes.lock(); // this must be released prior to calling f
|
|
let pos = runtimes.iter().position(|r| r.as_ref().map_or(
|
|
false,
|
|
|r| r.wasm_method == wasm_method &&
|
|
r.code_hash == *code_hash &&
|
|
r.heap_pages == heap_pages
|
|
));
|
|
|
|
let runtime = match pos {
|
|
Some(n) => runtimes[n]
|
|
.clone()
|
|
.expect("`position` only returns `Some` for entries that are `Some`"),
|
|
None => {
|
|
let code = runtime_code.fetch_runtime_code().ok_or(WasmError::CodeNotFound)?;
|
|
|
|
let result = create_versioned_wasm_runtime(
|
|
&code,
|
|
code_hash.clone(),
|
|
ext,
|
|
wasm_method,
|
|
heap_pages,
|
|
host_functions.into(),
|
|
allow_missing_func_imports,
|
|
self.max_runtime_instances,
|
|
);
|
|
if let Err(ref err) = result {
|
|
log::warn!(target: "wasm-runtime", "Cannot create a runtime: {:?}", err);
|
|
}
|
|
Arc::new(result?)
|
|
}
|
|
};
|
|
|
|
// Rearrange runtimes by last recently used.
|
|
match pos {
|
|
Some(0) => {},
|
|
Some(n) => {
|
|
for i in (1 .. n + 1).rev() {
|
|
runtimes.swap(i, i - 1);
|
|
}
|
|
}
|
|
None => {
|
|
runtimes[MAX_RUNTIMES-1] = Some(runtime.clone());
|
|
for i in (1 .. MAX_RUNTIMES).rev() {
|
|
runtimes.swap(i, i - 1);
|
|
}
|
|
}
|
|
}
|
|
drop(runtimes);
|
|
|
|
Ok(runtime.with_instance(ext, f))
|
|
}
|
|
}
|
|
|
|
/// Create a wasm runtime with the given `code`.
|
|
pub fn create_wasm_runtime_with_code(
|
|
wasm_method: WasmExecutionMethod,
|
|
heap_pages: u64,
|
|
code: &[u8],
|
|
host_functions: Vec<&'static dyn Function>,
|
|
allow_missing_func_imports: bool,
|
|
) -> Result<Box<dyn WasmModule>, WasmError> {
|
|
match wasm_method {
|
|
WasmExecutionMethod::Interpreted =>
|
|
sc_executor_wasmi::create_runtime(
|
|
code,
|
|
heap_pages,
|
|
host_functions,
|
|
allow_missing_func_imports
|
|
).map(|runtime| -> Box<dyn WasmModule> { Box::new(runtime) }),
|
|
#[cfg(feature = "wasmtime")]
|
|
WasmExecutionMethod::Compiled =>
|
|
sc_executor_wasmtime::create_runtime(
|
|
code,
|
|
heap_pages,
|
|
host_functions,
|
|
allow_missing_func_imports
|
|
).map(|runtime| -> Box<dyn WasmModule> { Box::new(runtime) }),
|
|
}
|
|
}
|
|
|
|
fn create_versioned_wasm_runtime(
|
|
code: &[u8],
|
|
code_hash: Vec<u8>,
|
|
ext: &mut dyn Externalities,
|
|
wasm_method: WasmExecutionMethod,
|
|
heap_pages: u64,
|
|
host_functions: Vec<&'static dyn Function>,
|
|
allow_missing_func_imports: bool,
|
|
max_instances: usize,
|
|
) -> Result<VersionedRuntime, WasmError> {
|
|
#[cfg(not(target_os = "unknown"))]
|
|
let time = std::time::Instant::now();
|
|
let mut runtime = create_wasm_runtime_with_code(
|
|
wasm_method,
|
|
heap_pages,
|
|
&code,
|
|
host_functions,
|
|
allow_missing_func_imports,
|
|
)?;
|
|
|
|
// Call to determine runtime version.
|
|
let version_result = {
|
|
// `ext` is already implicitly handled as unwind safe, as we store it in a global variable.
|
|
let mut ext = AssertUnwindSafe(ext);
|
|
|
|
// The following unwind safety assertion is OK because if the method call panics, the
|
|
// runtime will be dropped.
|
|
let runtime = AssertUnwindSafe(runtime.as_mut());
|
|
crate::native_executor::with_externalities_safe(
|
|
&mut **ext,
|
|
move || runtime.new_instance()?.call("Core_version", &[])
|
|
).map_err(|_| WasmError::Instantiation("panic in call to get runtime version".into()))?
|
|
};
|
|
let version = match version_result {
|
|
Ok(version) => Some(RuntimeVersion::decode(&mut version.as_slice())
|
|
.map_err(|_|
|
|
WasmError::Instantiation("failed to decode \"Core_version\" result".into())
|
|
)?),
|
|
Err(_) => None,
|
|
};
|
|
#[cfg(not(target_os = "unknown"))]
|
|
log::debug!(
|
|
target: "wasm-runtime",
|
|
"Prepared new runtime version {:?} in {} ms.",
|
|
version,
|
|
time.elapsed().as_millis(),
|
|
);
|
|
|
|
let mut instances = Vec::with_capacity(max_instances);
|
|
instances.resize_with(max_instances, || Mutex::new(None));
|
|
|
|
Ok(VersionedRuntime {
|
|
code_hash,
|
|
module: runtime,
|
|
version,
|
|
heap_pages,
|
|
wasm_method,
|
|
instances,
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use sp_wasm_interface::HostFunctions;
|
|
|
|
#[test]
|
|
fn host_functions_are_equal() {
|
|
let host_functions = sp_io::SubstrateHostFunctions::host_functions();
|
|
|
|
let equal = &host_functions[..] == &host_functions[..];
|
|
assert!(equal, "Host functions are not equal");
|
|
}
|
|
}
|