-
Notifications
You must be signed in to change notification settings - Fork 726
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add cli option --wasmtime-precompiled
and subcommand precompile-wasm
#1641
base: master
Are you sure you want to change the base?
Changes from all commits
d7b3887
0616813
f7705b6
68e7765
6b122c3
c56bffd
e0b1111
50eac51
2755221
0b26c2e
5c038e0
85d5570
3bedb37
582c404
9f92b3c
ac73de9
2da154a
9e4abc7
8061c87
fe0b4db
635219c
e3405e0
d420424
d844373
9dbee39
11ede5c
8e5f8ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,170 @@ | ||||||||
// 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 <https://www.gnu.org/licenses/>. | ||||||||
|
||||||||
use crate::{ | ||||||||
arg_enums::{ | ||||||||
execution_method_from_cli, WasmExecutionMethod, WasmtimeInstantiationStrategy, | ||||||||
DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, | ||||||||
}, | ||||||||
error::{self, Error}, | ||||||||
params::{DatabaseParams, PruningParams, SharedParams}, | ||||||||
CliConfiguration, | ||||||||
}; | ||||||||
|
||||||||
use clap::Parser; | ||||||||
use sc_client_api::{Backend, HeaderBackend}; | ||||||||
use sc_executor::{ | ||||||||
precompile_and_serialize_versioned_wasm_runtime, HeapAllocStrategy, DEFAULT_HEAP_ALLOC_PAGES, | ||||||||
}; | ||||||||
use sc_service::ChainSpec; | ||||||||
use sp_core::traits::RuntimeCode; | ||||||||
use sp_runtime::traits::{Block as BlockT, Hash, Header}; | ||||||||
use sp_state_machine::backend::BackendRuntimeCode; | ||||||||
use std::{fmt::Debug, path::PathBuf, sync::Arc}; | ||||||||
|
||||||||
/// The `precompile-wasm` command used to serialize a precompiled WASM module. | ||||||||
/// | ||||||||
/// The WASM code precompiled will be the one used at the latest finalized block | ||||||||
/// this node is aware of, if this node has the state for that finalized block in | ||||||||
/// its storage. If that's not the case, it will use the WASM code from the chain | ||||||||
/// spec passed as parameter when running the node. | ||||||||
#[derive(Debug, Parser)] | ||||||||
pub struct PrecompileWasmCmd { | ||||||||
#[allow(missing_docs)] | ||||||||
#[clap(flatten)] | ||||||||
pub database_params: DatabaseParams, | ||||||||
|
||||||||
/// The default number of 64KB pages to ever allocate for Wasm execution. | ||||||||
/// | ||||||||
/// Don't alter this unless you know what you're doing. | ||||||||
#[arg(long, value_name = "COUNT")] | ||||||||
pub default_heap_pages: Option<u32>, | ||||||||
|
||||||||
/// Path to the directory where precompiled artifact will be written. | ||||||||
#[arg(value_parser = PrecompileWasmCmd::validate_existing_directory)] | ||||||||
pub output_dir: PathBuf, | ||||||||
|
||||||||
#[allow(missing_docs)] | ||||||||
#[clap(flatten)] | ||||||||
pub pruning_params: PruningParams, | ||||||||
|
||||||||
#[allow(missing_docs)] | ||||||||
#[clap(flatten)] | ||||||||
pub shared_params: SharedParams, | ||||||||
|
||||||||
/// The WASM instantiation method to use. | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
/// | ||||||||
/// Only has an effect when `wasm-execution` is set to `compiled`. | ||||||||
/// The copy-on-write strategies are only supported on Linux. | ||||||||
/// If the copy-on-write variant of a strategy is unsupported | ||||||||
/// the executor will fall back to the non-CoW equivalent. | ||||||||
/// The fastest (and the default) strategy available is `pooling-copy-on-write`. | ||||||||
/// The `legacy-instance-reuse` strategy is deprecated and will | ||||||||
/// be removed in the future. It should only be used in case of | ||||||||
/// issues with the default instantiation strategy. | ||||||||
#[arg( | ||||||||
long, | ||||||||
value_name = "STRATEGY", | ||||||||
default_value_t = DEFAULT_WASMTIME_INSTANTIATION_STRATEGY, | ||||||||
value_enum, | ||||||||
)] | ||||||||
pub wasmtime_instantiation_strategy: WasmtimeInstantiationStrategy, | ||||||||
} | ||||||||
|
||||||||
impl PrecompileWasmCmd { | ||||||||
/// Run the precompile-wasm command | ||||||||
pub async fn run<B, BA>(&self, backend: Arc<BA>, spec: Box<dyn ChainSpec>) -> error::Result<()> | ||||||||
where | ||||||||
B: BlockT, | ||||||||
BA: Backend<B>, | ||||||||
{ | ||||||||
let heap_pages = self.default_heap_pages.unwrap_or(DEFAULT_HEAP_ALLOC_PAGES); | ||||||||
|
||||||||
let blockchain_info = backend.blockchain().info(); | ||||||||
|
||||||||
if backend.have_state_at(blockchain_info.finalized_hash, blockchain_info.finalized_number) { | ||||||||
let state = backend.state_at(backend.blockchain().info().finalized_hash)?; | ||||||||
|
||||||||
precompile_and_serialize_versioned_wasm_runtime( | ||||||||
HeapAllocStrategy::Static { extra_pages: heap_pages }, | ||||||||
&BackendRuntimeCode::new(&state).runtime_code()?, | ||||||||
execution_method_from_cli( | ||||||||
WasmExecutionMethod::Compiled, | ||||||||
self.wasmtime_instantiation_strategy, | ||||||||
), | ||||||||
&self.output_dir, | ||||||||
) | ||||||||
.map_err(|e| Error::Application(Box::new(e)))?; | ||||||||
} else { | ||||||||
let storage = spec.as_storage_builder().build_storage()?; | ||||||||
if let Some(wasm_bytecode) = storage.top.get(sp_storage::well_known_keys::CODE) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||||||||
let runtime_code = RuntimeCode { | ||||||||
code_fetcher: &sp_core::traits::WrappedRuntimeCode( | ||||||||
wasm_bytecode.as_slice().into(), | ||||||||
), | ||||||||
hash: <<B::Header as Header>::Hashing as Hash>::hash(&wasm_bytecode) | ||||||||
.as_ref() | ||||||||
.to_vec(), | ||||||||
heap_pages: Some(heap_pages as u64), | ||||||||
}; | ||||||||
precompile_and_serialize_versioned_wasm_runtime( | ||||||||
HeapAllocStrategy::Static { extra_pages: heap_pages }, | ||||||||
&runtime_code, | ||||||||
execution_method_from_cli( | ||||||||
WasmExecutionMethod::Compiled, | ||||||||
self.wasmtime_instantiation_strategy, | ||||||||
), | ||||||||
&self.output_dir, | ||||||||
) | ||||||||
.map_err(|e| Error::Application(Box::new(e)))?; | ||||||||
} else { | ||||||||
return Err(Error::Input(format!( | ||||||||
"The chain spec used does not contain a wasm bytecode in the {} storage key", | ||||||||
String::from_utf8(sp_storage::well_known_keys::CODE.to_vec()) | ||||||||
.expect("Well known key should be a valid UTF8 string.") | ||||||||
))); | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
Ok(()) | ||||||||
} | ||||||||
|
||||||||
/// Validate that the path is a valid directory. | ||||||||
fn validate_existing_directory(path: &str) -> Result<PathBuf, String> { | ||||||||
let path_buf = PathBuf::from(path); | ||||||||
if path_buf.is_dir() { | ||||||||
Ok(path_buf) | ||||||||
} else { | ||||||||
Err(format!("The path '{}' is not a valid directory", path)) | ||||||||
} | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
impl CliConfiguration for PrecompileWasmCmd { | ||||||||
fn shared_params(&self) -> &SharedParams { | ||||||||
&self.shared_params | ||||||||
} | ||||||||
|
||||||||
fn pruning_params(&self) -> Option<&PruningParams> { | ||||||||
Some(&self.pruning_params) | ||||||||
} | ||||||||
|
||||||||
fn database_params(&self) -> Option<&DatabaseParams> { | ||||||||
Some(&self.database_params) | ||||||||
} | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,10 +65,28 @@ pub struct ImportParams { | |
)] | ||
pub wasmtime_instantiation_strategy: WasmtimeInstantiationStrategy, | ||
|
||
/// Specify the path where local precompiled WASM runtimes are stored. | ||
/// Only has an effect when `wasm-execution` is set to `compiled`. | ||
/// | ||
/// The precompiled runtimes must have been generated using the `precompile-wasm` | ||
/// subcommand with the same version of wasmtime and the exact same configuration. | ||
/// The file name must end with the hash of the configuration. This hash must match, otherwise | ||
/// the runtime will be recompiled. | ||
#[arg( | ||
long, | ||
value_name = "PATH", | ||
value_parser = ImportParams::validate_existing_directory, | ||
)] | ||
pub wasmtime_precompiled: Option<PathBuf>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clap-rs/clap#4074 we should some of these validations here to ensure it is a directory. |
||
|
||
/// Specify the path where local WASM runtimes are stored. | ||
/// | ||
/// These runtimes will override on-chain runtimes when the version matches. | ||
#[arg(long, value_name = "PATH")] | ||
#[arg( | ||
long, | ||
value_name = "PATH", | ||
value_parser = ImportParams::validate_existing_directory, | ||
)] | ||
pub wasm_runtime_overrides: Option<PathBuf>, | ||
|
||
#[allow(missing_docs)] | ||
|
@@ -99,11 +117,27 @@ impl ImportParams { | |
crate::execution_method_from_cli(self.wasm_method, self.wasmtime_instantiation_strategy) | ||
} | ||
|
||
/// Enable using precompiled WASM module with locally-stored artifacts | ||
/// by specifying the path where artifacts are stored. | ||
pub fn wasmtime_precompiled(&self) -> Option<PathBuf> { | ||
self.wasmtime_precompiled.clone() | ||
} | ||
|
||
/// Enable overriding on-chain WASM with locally-stored WASM | ||
/// by specifying the path where local WASM is stored. | ||
pub fn wasm_runtime_overrides(&self) -> Option<PathBuf> { | ||
self.wasm_runtime_overrides.clone() | ||
} | ||
|
||
/// Validate that the path is a valid directory. | ||
fn validate_existing_directory(path: &str) -> Result<PathBuf, String> { | ||
let path_buf = PathBuf::from(path); | ||
if path_buf.is_dir() { | ||
Ok(path_buf) | ||
} else { | ||
Err(format!("The path '{}' is not a valid directory", path)) | ||
} | ||
} | ||
} | ||
|
||
/// Execution strategies parameters. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also mention which WASM is actually pre-compiled.