mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 14:11:09 +00:00
CLI API refactoring and improvement (#4692)
It changes the way we extended the CLI functionalities of substrate to allow more flexibility. (If this was not clear, here is another version: it changes the `sc_cli` API to allow more flexibility).
This touches a few important things:
- the startup of the async task with tokei:
This was in node and node-template and I moved it to substrate. The idea is to have 1 time the code that handles unix signals (SIGTERM and SIGINT) properly. It is however possible to make this more generic to wait for a future instead and provide only a helper for the basic handling of SIGTERM and SIGINT.
- increased the version of structopt and tokei
- no more use of structopt internal's API
- less use of generics
Related to #4643 and https://github.com/paritytech/cumulus/pull/42: the implementation of "into_configuration" and "get_config" are similar but with better flexibility so it is now possible in cumulus to have the command-line arguments only of the run command for polkadot if we want
Related to https://github.com/paritytech/cumulus/issues/24 and https://github.com/paritytech/cumulus/issues/34 : it will now be possible to make a configuration struct for polkadot with some overrides of the default parameters much more easily.
This commit is contained in:
Generated
+714
-721
File diff suppressed because it is too large
Load Diff
@@ -12,9 +12,7 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
futures = "0.3.1"
|
||||
ctrlc = { version = "3.1.3", features = ["termination"] }
|
||||
log = "0.4.8"
|
||||
tokio = { version = "0.2", features = ["rt-threaded"] }
|
||||
sc-cli = { version = "0.8.0", path = "../../client/cli" }
|
||||
sp-core = { version = "2.0.0", path = "../../primitives/core" }
|
||||
sc-executor = { version = "0.8", path = "../../client/executor" }
|
||||
@@ -31,7 +29,8 @@ grandpa-primitives = { version = "2.0.0", package = "sp-finality-grandpa", path
|
||||
sc-client = { version = "0.8", path = "../../client/" }
|
||||
node-template-runtime = { version = "2.0.0", path = "runtime" }
|
||||
sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" }
|
||||
sc-basic-authorship = { path = "../../client/basic-authorship" }
|
||||
sc-basic-authorship = { path = "../../client/basic-authorship" }
|
||||
structopt = "0.3.8"
|
||||
|
||||
[build-dependencies]
|
||||
vergen = "3.0.4"
|
||||
|
||||
@@ -145,3 +145,10 @@ fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_spec(id: &str) -> Result<Option<ChainSpec>, String> {
|
||||
Ok(match Alternative::from(id) {
|
||||
Some(spec) => Some(spec.load()?),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,121 +1,11 @@
|
||||
use crate::service;
|
||||
use futures::{future::{select, Map, Either}, FutureExt, channel::oneshot};
|
||||
use std::cell::RefCell;
|
||||
use tokio::runtime::Runtime;
|
||||
pub use sc_cli::{VersionInfo, IntoExit, error};
|
||||
use sc_cli::{display_role, informant, parse_and_prepare, ParseAndPrepare, NoCustom};
|
||||
use sc_service::{AbstractService, Roles as ServiceRoles, Configuration};
|
||||
use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};
|
||||
use crate::chain_spec;
|
||||
use log::info;
|
||||
use sc_cli::{RunCmd, Subcommand};
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// Parse command line arguments into service configuration.
|
||||
pub fn run<I, T, E>(args: I, exit: E, version: VersionInfo) -> error::Result<()> where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<std::ffi::OsString> + Clone,
|
||||
E: IntoExit,
|
||||
{
|
||||
type Config<T> = Configuration<(), T>;
|
||||
match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "substrate-node", args) {
|
||||
ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
|
||||
|exit, _cli_args, _custom_args, mut config: Config<_>| {
|
||||
info!("{}", version.name);
|
||||
info!(" version {}", config.full_version());
|
||||
info!(" by {}, 2017, 2018", version.author);
|
||||
info!("Chain specification: {}", config.chain_spec.name());
|
||||
info!("Node name: {}", config.name);
|
||||
info!("Roles: {}", display_role(&config));
|
||||
let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
|
||||
config.tasks_executor = {
|
||||
let runtime_handle = runtime.handle().clone();
|
||||
Some(Box::new(move |fut| { runtime_handle.spawn(fut); }))
|
||||
};
|
||||
match config.roles {
|
||||
ServiceRoles::LIGHT => run_until_exit(
|
||||
runtime,
|
||||
service::new_light(config)?,
|
||||
exit
|
||||
),
|
||||
_ => run_until_exit(
|
||||
runtime,
|
||||
service::new_full(config)?,
|
||||
exit
|
||||
),
|
||||
}
|
||||
}),
|
||||
ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
|
||||
ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
|
||||
Ok(new_full_start!(config).0), load_spec, exit),
|
||||
ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
|
||||
Ok(new_full_start!(config).0), load_spec, exit),
|
||||
ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder(|config: Config<_>|
|
||||
Ok(new_full_start!(config).0), load_spec, exit),
|
||||
ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
|
||||
ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_>|
|
||||
Ok(new_full_start!(config).0), load_spec),
|
||||
ParseAndPrepare::CustomCommand(_) => Ok(())
|
||||
}?;
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub struct Cli {
|
||||
#[structopt(subcommand)]
|
||||
pub subcommand: Option<Subcommand>,
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
|
||||
Ok(match chain_spec::Alternative::from(id) {
|
||||
Some(spec) => Some(spec.load()?),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn run_until_exit<T, E>(
|
||||
mut runtime: Runtime,
|
||||
service: T,
|
||||
e: E,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
T: AbstractService,
|
||||
E: IntoExit,
|
||||
{
|
||||
let (exit_send, exit) = oneshot::channel();
|
||||
|
||||
let informant = informant::build(&service);
|
||||
|
||||
let handle = runtime.spawn(select(exit, informant));
|
||||
|
||||
// we eagerly drop the service so that the internal exit future is fired,
|
||||
// but we need to keep holding a reference to the global telemetry guard
|
||||
let _telemetry = service.telemetry();
|
||||
|
||||
let exit = e.into_exit();
|
||||
let service_res = runtime.block_on(select(service, exit));
|
||||
|
||||
let _ = exit_send.send(());
|
||||
|
||||
if let Err(e) = runtime.block_on(handle) {
|
||||
log::error!("Error running node: {:?}", e);
|
||||
}
|
||||
|
||||
match service_res {
|
||||
Either::Left((res, _)) => res.map_err(error::Error::Service),
|
||||
Either::Right((_, _)) => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// handles ctrl-c
|
||||
pub struct Exit;
|
||||
impl IntoExit for Exit {
|
||||
type Exit = Map<oneshot::Receiver<()>, fn(Result<(), oneshot::Canceled>) -> ()>;
|
||||
fn into_exit(self) -> Self::Exit {
|
||||
// can't use signal directly here because CtrlC takes only `Fn`.
|
||||
let (exit_send, exit) = oneshot::channel();
|
||||
|
||||
let exit_send_cell = RefCell::new(Some(exit_send));
|
||||
ctrlc::set_handler(move || {
|
||||
let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take();
|
||||
if let Some(exit_send) = exit_send {
|
||||
exit_send.send(()).expect("Error sending exit notification");
|
||||
}
|
||||
}).expect("Error setting Ctrl-C handler");
|
||||
|
||||
exit.map(drop)
|
||||
}
|
||||
#[structopt(flatten)]
|
||||
pub run: RunCmd,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2017-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/>.
|
||||
|
||||
use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};
|
||||
use sc_cli::{VersionInfo, error};
|
||||
use crate::service;
|
||||
use crate::chain_spec;
|
||||
use crate::cli::Cli;
|
||||
|
||||
/// Parse and run command line arguments
|
||||
pub fn run(version: VersionInfo) -> error::Result<()>
|
||||
{
|
||||
let opt = sc_cli::from_args::<Cli>(&version);
|
||||
|
||||
let mut config = sc_service::Configuration::default();
|
||||
config.impl_name = "node-template";
|
||||
|
||||
match opt.subcommand {
|
||||
Some(subcommand) => sc_cli::run_subcommand(
|
||||
config,
|
||||
subcommand,
|
||||
chain_spec::load_spec,
|
||||
|config: _| Ok(new_full_start!(config).0),
|
||||
&version,
|
||||
),
|
||||
None => sc_cli::run(
|
||||
config,
|
||||
opt.run,
|
||||
service::new_light,
|
||||
service::new_full,
|
||||
chain_spec::load_spec,
|
||||
&version,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,11 @@ mod chain_spec;
|
||||
#[macro_use]
|
||||
mod service;
|
||||
mod cli;
|
||||
mod command;
|
||||
|
||||
pub use sc_cli::{VersionInfo, IntoExit, error};
|
||||
pub use sc_cli::{VersionInfo, error};
|
||||
|
||||
fn main() -> Result<(), cli::error::Error> {
|
||||
fn main() -> Result<(), error::Error> {
|
||||
let version = VersionInfo {
|
||||
name: "Substrate Node",
|
||||
commit: env!("VERGEN_SHA_SHORT"),
|
||||
@@ -17,7 +18,8 @@ fn main() -> Result<(), cli::error::Error> {
|
||||
author: "Anonymous",
|
||||
description: "Template Node",
|
||||
support_url: "support.anonymous.an",
|
||||
copyright_start_year: 2017,
|
||||
};
|
||||
|
||||
cli::run(std::env::args(), cli::Exit, version)
|
||||
command::run(version)
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ macro_rules! new_full_start {
|
||||
}
|
||||
|
||||
/// Builds a new service for a full client.
|
||||
pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
|
||||
pub fn new_full(config: Configuration<GenesisConfig>)
|
||||
-> Result<impl AbstractService, ServiceError>
|
||||
{
|
||||
let is_authority = config.roles.is_authority();
|
||||
@@ -192,7 +192,7 @@ pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisCon
|
||||
}
|
||||
|
||||
/// Builds a new service for a light client.
|
||||
pub fn new_light<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>)
|
||||
pub fn new_light(config: Configuration<GenesisConfig>)
|
||||
-> Result<impl AbstractService, ServiceError>
|
||||
{
|
||||
let inherent_data_providers = InherentDataProviders::new();
|
||||
|
||||
@@ -31,7 +31,7 @@ hex-literal = "0.2.1"
|
||||
jsonrpc-core = "14.0.3"
|
||||
log = "0.4.8"
|
||||
rand = "0.7.2"
|
||||
structopt = "=0.3.7"
|
||||
structopt = { version = "0.3.8", optional = true }
|
||||
|
||||
# primitives
|
||||
sp-authority-discovery = { version = "2.0.0", path = "../../../primitives/authority-discovery" }
|
||||
@@ -81,9 +81,7 @@ node-primitives = { version = "2.0.0", path = "../primitives" }
|
||||
node-executor = { version = "2.0.0", path = "../executor" }
|
||||
|
||||
# CLI-specific dependencies
|
||||
tokio = { version = "0.2", features = ["rt-threaded"], optional = true }
|
||||
sc-cli = { version = "0.8.0", optional = true, path = "../../../client/cli" }
|
||||
ctrlc = { version = "3.1.3", features = ["termination"], optional = true }
|
||||
node-transaction-factory = { version = "0.8.0", optional = true, path = "../transaction-factory" }
|
||||
|
||||
# WASM-specific dependencies
|
||||
@@ -99,10 +97,19 @@ futures = "0.3.1"
|
||||
tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
sc-cli = { version = "0.8.0", package = "sc-cli", path = "../../../client/cli" }
|
||||
build-script-utils = { version = "2.0.0", package = "substrate-build-script-utils", path = "../../../utils/build-script-utils" }
|
||||
structopt = "=0.3.7"
|
||||
vergen = "3.0.4"
|
||||
structopt = { version = "0.3.8", optional = true }
|
||||
node-transaction-factory = { version = "0.8.0", optional = true, path = "../transaction-factory" }
|
||||
|
||||
[build-dependencies.sc-cli]
|
||||
version = "0.8.0"
|
||||
package = "sc-cli"
|
||||
path = "../../../client/cli"
|
||||
optional = true
|
||||
|
||||
[build-dependencies.vergen]
|
||||
version = "3.0.4"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["cli", "wasmtime"]
|
||||
@@ -114,10 +121,10 @@ browser = [
|
||||
cli = [
|
||||
"sc-cli",
|
||||
"node-transaction-factory",
|
||||
"tokio",
|
||||
"ctrlc",
|
||||
"sc-service/rocksdb",
|
||||
"node-executor/wasmi-errno",
|
||||
"vergen",
|
||||
"structopt",
|
||||
]
|
||||
wasmtime = [
|
||||
"cli",
|
||||
|
||||
@@ -18,31 +18,8 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use futures::channel::oneshot;
|
||||
use futures::{future, FutureExt};
|
||||
use sc_cli::VersionInfo;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
// handles ctrl-c
|
||||
struct Exit;
|
||||
impl sc_cli::IntoExit for Exit {
|
||||
type Exit = future::Map<oneshot::Receiver<()>, fn(Result<(), oneshot::Canceled>) -> ()>;
|
||||
fn into_exit(self) -> Self::Exit {
|
||||
// can't use signal directly here because CtrlC takes only `Fn`.
|
||||
let (exit_send, exit) = oneshot::channel();
|
||||
|
||||
let exit_send_cell = RefCell::new(Some(exit_send));
|
||||
ctrlc::set_handler(move || {
|
||||
if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() {
|
||||
exit_send.send(()).expect("Error sending exit notification");
|
||||
}
|
||||
}).expect("Error setting Ctrl-C handler");
|
||||
|
||||
exit.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), sc_cli::error::Error> {
|
||||
let version = VersionInfo {
|
||||
name: "Substrate Node",
|
||||
@@ -52,7 +29,8 @@ fn main() -> Result<(), sc_cli::error::Error> {
|
||||
author: "Parity Technologies <admin@parity.io>",
|
||||
description: "Generic substrate node",
|
||||
support_url: "https://github.com/paritytech/substrate/issues/new",
|
||||
copyright_start_year: 2017,
|
||||
};
|
||||
|
||||
node_cli::run(std::env::args(), Exit, version)
|
||||
node_cli::run(std::env::args(), version)
|
||||
}
|
||||
|
||||
@@ -14,39 +14,48 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{fs, env, path::Path};
|
||||
use structopt::{StructOpt, clap::Shell};
|
||||
use sc_cli::{NoCustom, CoreParams};
|
||||
use vergen::{ConstantsFlags, generate_cargo_keys};
|
||||
|
||||
fn main() {
|
||||
build_shell_completion();
|
||||
generate_cargo_keys(ConstantsFlags::all()).expect("Failed to generate metadata files");
|
||||
|
||||
build_script_utils::rerun_if_git_head_changed();
|
||||
#[cfg(feature = "cli")]
|
||||
cli::main();
|
||||
}
|
||||
|
||||
/// Build shell completion scripts for all known shells
|
||||
/// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123
|
||||
fn build_shell_completion() {
|
||||
for shell in &[Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] {
|
||||
build_completion(shell);
|
||||
#[cfg(feature = "cli")]
|
||||
mod cli {
|
||||
include!("src/cli.rs");
|
||||
|
||||
use std::{fs, env, path::Path};
|
||||
use sc_cli::{structopt::clap::Shell};
|
||||
use vergen::{ConstantsFlags, generate_cargo_keys};
|
||||
|
||||
pub fn main() {
|
||||
build_shell_completion();
|
||||
generate_cargo_keys(ConstantsFlags::all()).expect("Failed to generate metadata files");
|
||||
|
||||
build_script_utils::rerun_if_git_head_changed();
|
||||
}
|
||||
|
||||
/// Build shell completion scripts for all known shells
|
||||
/// Full list in https://github.com/kbknapp/clap-rs/blob/e9d0562a1dc5dfe731ed7c767e6cee0af08f0cf9/src/app/parser.rs#L123
|
||||
fn build_shell_completion() {
|
||||
for shell in &[Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell] {
|
||||
build_completion(shell);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the shell auto-completion for a given Shell
|
||||
fn build_completion(shell: &Shell) {
|
||||
let outdir = match env::var_os("OUT_DIR") {
|
||||
None => return,
|
||||
Some(dir) => dir,
|
||||
};
|
||||
let path = Path::new(&outdir)
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.join("completion-scripts");
|
||||
|
||||
fs::create_dir(&path).ok();
|
||||
|
||||
Cli::clap().gen_completions("substrate-node", *shell, &path);
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the shell auto-completion for a given Shell
|
||||
fn build_completion(shell: &Shell) {
|
||||
let outdir = match env::var_os("OUT_DIR") {
|
||||
None => return,
|
||||
Some(dir) => dir,
|
||||
};
|
||||
let path = Path::new(&outdir)
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.parent().unwrap()
|
||||
.join("completion-scripts");
|
||||
|
||||
fs::create_dir(&path).ok();
|
||||
|
||||
CoreParams::<NoCustom, NoCustom>::clap().gen_completions("substrate-node", *shell, &path);
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ async fn start_inner(wasm_ext: Transport) -> Result<Client, Box<dyn std::error::
|
||||
let chain_spec = ChainSpec::FlamingFir.load()
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let config: Configuration<(), _, _> = browser_configuration(wasm_ext, chain_spec)
|
||||
let config: Configuration<_, _> = browser_configuration(wasm_ext, chain_spec)
|
||||
.await?;
|
||||
|
||||
info!("Substrate browser node");
|
||||
info!(" version {}", config.full_version());
|
||||
info!(" by Parity Technologies, 2017-2020");
|
||||
info!("Chain specification: {}", config.chain_spec.name());
|
||||
info!("Chain specification: {}", config.expect_chain_spec().name());
|
||||
info!("Node name: {}", config.name);
|
||||
info!("Roles: {:?}", config.roles);
|
||||
|
||||
|
||||
@@ -14,21 +14,26 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use sc_cli::VersionInfo;
|
||||
use tokio::runtime::{Builder as RuntimeBuilder, Runtime};
|
||||
use sc_cli::{IntoExit, NoCustom, SharedParams, ImportParams, error};
|
||||
use sc_service::{AbstractService, Roles as ServiceRoles, Configuration};
|
||||
use log::info;
|
||||
use sc_cli::{SharedParams, ImportParams, RunCmd};
|
||||
use structopt::StructOpt;
|
||||
use sc_cli::{display_role, parse_and_prepare, GetSharedParams, ParseAndPrepare};
|
||||
use crate::{service, ChainSpec, load_spec};
|
||||
use crate::factory_impl::FactoryState;
|
||||
use node_transaction_factory::RuntimeAdapter;
|
||||
use futures::{channel::oneshot, future::{select, Either}};
|
||||
|
||||
/// Custom subcommands.
|
||||
#[derive(Clone, Debug, StructOpt)]
|
||||
pub enum CustomSubcommands {
|
||||
#[structopt(settings = &[
|
||||
structopt::clap::AppSettings::GlobalVersion,
|
||||
structopt::clap::AppSettings::ArgsNegateSubcommands,
|
||||
structopt::clap::AppSettings::SubcommandsNegateReqs,
|
||||
])]
|
||||
pub struct Cli {
|
||||
#[structopt(subcommand)]
|
||||
pub subcommand: Option<Subcommand>,
|
||||
#[structopt(flatten)]
|
||||
pub run: RunCmd,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, StructOpt)]
|
||||
pub enum Subcommand {
|
||||
#[structopt(flatten)]
|
||||
Base(sc_cli::Subcommand),
|
||||
/// The custom factory subcommmand for manufacturing transactions.
|
||||
#[structopt(
|
||||
name = "factory",
|
||||
@@ -38,14 +43,6 @@ pub enum CustomSubcommands {
|
||||
Factory(FactoryCmd),
|
||||
}
|
||||
|
||||
impl GetSharedParams for CustomSubcommands {
|
||||
fn shared_params(&self) -> Option<&SharedParams> {
|
||||
match self {
|
||||
CustomSubcommands::Factory(cmd) => Some(&cmd.shared_params),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `factory` command used to generate transactions.
|
||||
/// Please note: this command currently only works on an empty database!
|
||||
#[derive(Debug, StructOpt, Clone)]
|
||||
@@ -87,125 +84,3 @@ pub struct FactoryCmd {
|
||||
#[structopt(flatten)]
|
||||
pub import_params: ImportParams,
|
||||
}
|
||||
|
||||
/// Parse command line arguments into service configuration.
|
||||
pub fn run<I, T, E>(args: I, exit: E, version: sc_cli::VersionInfo) -> error::Result<()> where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<std::ffi::OsString> + Clone,
|
||||
E: IntoExit,
|
||||
{
|
||||
type Config<A, B> = Configuration<(), A, B>;
|
||||
|
||||
match parse_and_prepare::<CustomSubcommands, NoCustom, _>(&version, "substrate-node", args) {
|
||||
ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
|
||||
|exit, _cli_args, _custom_args, mut config: Config<_, _>| {
|
||||
info!("{}", version.name);
|
||||
info!(" version {}", config.full_version());
|
||||
info!(" by Parity Technologies, 2017-2020");
|
||||
info!("Chain specification: {}", config.chain_spec.name());
|
||||
info!("Node name: {}", config.name);
|
||||
info!("Roles: {}", display_role(&config));
|
||||
let runtime = RuntimeBuilder::new()
|
||||
.thread_name("main-tokio-")
|
||||
.threaded_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
config.tasks_executor = {
|
||||
let runtime_handle = runtime.handle().clone();
|
||||
Some(Box::new(move |fut| { runtime_handle.spawn(fut); }))
|
||||
};
|
||||
match config.roles {
|
||||
ServiceRoles::LIGHT => run_until_exit(
|
||||
runtime,
|
||||
service::new_light(config)?,
|
||||
exit
|
||||
),
|
||||
_ => run_until_exit(
|
||||
runtime,
|
||||
service::new_full(config)?,
|
||||
exit
|
||||
),
|
||||
}
|
||||
}),
|
||||
ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(load_spec),
|
||||
ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_, _>|
|
||||
Ok(new_full_start!(config).0), load_spec, exit),
|
||||
ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: Config<_, _>|
|
||||
Ok(new_full_start!(config).0), load_spec, exit),
|
||||
ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder(|config: Config<_, _>|
|
||||
Ok(new_full_start!(config).0), load_spec, exit),
|
||||
ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
|
||||
ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder(|config: Config<_, _>|
|
||||
Ok(new_full_start!(config).0), load_spec),
|
||||
ParseAndPrepare::CustomCommand(CustomSubcommands::Factory(cli_args)) => {
|
||||
let mut config: Config<_, _> = sc_cli::create_config_with_db_path(
|
||||
load_spec,
|
||||
&cli_args.shared_params,
|
||||
&version,
|
||||
None,
|
||||
)?;
|
||||
sc_cli::fill_import_params(
|
||||
&mut config,
|
||||
&cli_args.import_params,
|
||||
ServiceRoles::FULL,
|
||||
cli_args.shared_params.dev,
|
||||
)?;
|
||||
|
||||
match ChainSpec::from(config.chain_spec.id()) {
|
||||
Some(ref c) if c == &ChainSpec::Development || c == &ChainSpec::LocalTestnet => {},
|
||||
_ => panic!("Factory is only supported for development and local testnet."),
|
||||
}
|
||||
|
||||
let factory_state = FactoryState::new(
|
||||
cli_args.mode.clone(),
|
||||
cli_args.num,
|
||||
cli_args.rounds,
|
||||
);
|
||||
|
||||
let service_builder = new_full_start!(config).0;
|
||||
node_transaction_factory::factory::<FactoryState<_>, _, _, _, _, _>(
|
||||
factory_state,
|
||||
service_builder.client(),
|
||||
service_builder.select_chain()
|
||||
.expect("The select_chain is always initialized by new_full_start!; QED")
|
||||
).map_err(|e| format!("Error in transaction factory: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_until_exit<T, E>(
|
||||
mut runtime: Runtime,
|
||||
service: T,
|
||||
e: E,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
T: AbstractService,
|
||||
E: IntoExit,
|
||||
{
|
||||
let (exit_send, exit) = oneshot::channel();
|
||||
|
||||
let informant = sc_cli::informant::build(&service);
|
||||
|
||||
let handle = runtime.spawn(select(exit, informant));
|
||||
|
||||
// we eagerly drop the service so that the internal exit future is fired,
|
||||
// but we need to keep holding a reference to the global telemetry guard
|
||||
let _telemetry = service.telemetry();
|
||||
|
||||
let exit = e.into_exit();
|
||||
let service_res = runtime.block_on(select(service, exit));
|
||||
|
||||
let _ = exit_send.send(());
|
||||
|
||||
if let Err(e) = runtime.block_on(handle) {
|
||||
log::error!("Error running node: {:?}", e);
|
||||
}
|
||||
|
||||
match service_res {
|
||||
Either::Left((res, _)) => res.map_err(error::Error::Service),
|
||||
Either::Right((_, _)) => Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2017-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/>.
|
||||
|
||||
use sc_cli::{VersionInfo, error};
|
||||
use sc_service::{Roles as ServiceRoles};
|
||||
use node_transaction_factory::RuntimeAdapter;
|
||||
use crate::{Cli, service, ChainSpec, load_spec, Subcommand, factory_impl::FactoryState};
|
||||
|
||||
/// Parse command line arguments into service configuration.
|
||||
pub fn run<I, T>(args: I, version: sc_cli::VersionInfo) -> error::Result<()>
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<std::ffi::OsString> + Clone,
|
||||
{
|
||||
let args: Vec<_> = args.collect();
|
||||
let opt = sc_cli::from_iter::<Cli, _>(args.clone(), &version);
|
||||
|
||||
let mut config = sc_service::Configuration::default();
|
||||
config.impl_name = "substrate-node";
|
||||
|
||||
match opt.subcommand {
|
||||
None => sc_cli::run(
|
||||
config,
|
||||
opt.run,
|
||||
service::new_light,
|
||||
service::new_full,
|
||||
load_spec,
|
||||
&version,
|
||||
),
|
||||
Some(Subcommand::Factory(cli_args)) => {
|
||||
sc_cli::init(&mut config, load_spec, &cli_args.shared_params, &version)?;
|
||||
|
||||
sc_cli::fill_import_params(
|
||||
&mut config,
|
||||
&cli_args.import_params,
|
||||
ServiceRoles::FULL,
|
||||
cli_args.shared_params.dev,
|
||||
)?;
|
||||
|
||||
match ChainSpec::from(config.expect_chain_spec().id()) {
|
||||
Some(ref c) if c == &ChainSpec::Development || c == &ChainSpec::LocalTestnet => {},
|
||||
_ => panic!("Factory is only supported for development and local testnet."),
|
||||
}
|
||||
|
||||
let factory_state = FactoryState::new(
|
||||
cli_args.mode.clone(),
|
||||
cli_args.num,
|
||||
cli_args.rounds,
|
||||
);
|
||||
|
||||
let service_builder = new_full_start!(config).0;
|
||||
node_transaction_factory::factory::<FactoryState<_>, _, _, _, _, _>(
|
||||
factory_state,
|
||||
service_builder.client(),
|
||||
service_builder.select_chain()
|
||||
.expect("The select_chain is always initialized by new_full_start!; QED")
|
||||
).map_err(|e| format!("Error in transaction factory: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Some(Subcommand::Base(subcommand)) => sc_cli::run_subcommand(
|
||||
config,
|
||||
subcommand,
|
||||
load_spec,
|
||||
|config: service::NodeConfiguration| Ok(new_full_start!(config).0),
|
||||
&version,
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -39,11 +39,15 @@ mod browser;
|
||||
mod cli;
|
||||
#[cfg(feature = "cli")]
|
||||
mod factory_impl;
|
||||
#[cfg(feature = "cli")]
|
||||
mod command;
|
||||
|
||||
#[cfg(feature = "browser")]
|
||||
pub use browser::*;
|
||||
#[cfg(feature = "cli")]
|
||||
pub use cli::*;
|
||||
#[cfg(feature = "cli")]
|
||||
pub use command::*;
|
||||
|
||||
/// The chain specification option.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
||||
@@ -274,10 +274,10 @@ type ConcreteTransactionPool = sc_transaction_pool::BasicPool<
|
||||
>;
|
||||
|
||||
/// A specialized configuration object for setting up the node..
|
||||
pub type NodeConfiguration<C> = Configuration<C, GenesisConfig, crate::chain_spec::Extensions>;
|
||||
pub type NodeConfiguration = Configuration<GenesisConfig, crate::chain_spec::Extensions>;
|
||||
|
||||
/// Builds a new service for a full client.
|
||||
pub fn new_full<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
pub fn new_full(config: NodeConfiguration)
|
||||
-> Result<
|
||||
Service<
|
||||
ConcreteBlock,
|
||||
@@ -299,7 +299,7 @@ pub fn new_full<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
}
|
||||
|
||||
/// Builds a new service for a light client.
|
||||
pub fn new_light<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
pub fn new_light(config: NodeConfiguration)
|
||||
-> Result<impl AbstractService, ServiceError> {
|
||||
type RpcExtension = jsonrpc_core::IoHandler<sc_rpc::Metadata>;
|
||||
let inherent_data_providers = InherentDataProviders::new();
|
||||
|
||||
@@ -12,4 +12,4 @@ sc-keystore = { version = "2.0.0", path = "../../../client/keystore" }
|
||||
node-cli = { version = "2.0.0", path = "../../node/cli" }
|
||||
sp-core = { version = "2.0.0", path = "../../../primitives/core" }
|
||||
rand = "0.7.2"
|
||||
structopt = "=0.3.7"
|
||||
structopt = "0.3.8"
|
||||
|
||||
@@ -17,7 +17,7 @@ time = "0.1.42"
|
||||
ansi_term = "0.12.1"
|
||||
lazy_static = "1.4.0"
|
||||
app_dirs = "1.2.1"
|
||||
tokio = "0.2"
|
||||
tokio = { version = "0.2.9", features = [ "signal", "rt-core", "rt-threaded" ] }
|
||||
futures = "0.3.1"
|
||||
fdlimit = "0.1.1"
|
||||
serde_json = "1.0.41"
|
||||
@@ -32,8 +32,9 @@ sp-state-machine = { version = "0.8", path = "../../primitives/state-machine" }
|
||||
sc-telemetry = { version = "2.0.0", path = "../telemetry" }
|
||||
sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" }
|
||||
names = "0.11.0"
|
||||
structopt = "=0.3.7"
|
||||
structopt = "0.3.8"
|
||||
sc-tracing = { version = "2.0.0", path = "../tracing" }
|
||||
chrono = "0.4.10"
|
||||
|
||||
[target.'cfg(not(target_os = "unknown"))'.dependencies]
|
||||
rpassword = "4.0.1"
|
||||
|
||||
+260
-910
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,208 @@
|
||||
// Copyright 2017-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/>.
|
||||
|
||||
use sc_network::{
|
||||
self,
|
||||
config::{
|
||||
NodeKeyConfig,
|
||||
},
|
||||
};
|
||||
use sp_core::H256;
|
||||
use regex::Regex;
|
||||
use std::{path::{Path, PathBuf}, str::FromStr};
|
||||
use crate::error;
|
||||
use crate::params::{NodeKeyParams, NodeKeyType};
|
||||
|
||||
/// The file name of the node's Ed25519 secret key inside the chain-specific
|
||||
/// network config directory, if neither `--node-key` nor `--node-key-file`
|
||||
/// is specified in combination with `--node-key-type=ed25519`.
|
||||
const NODE_KEY_ED25519_FILE: &str = "secret_ed25519";
|
||||
|
||||
/// Check whether a node name is considered as valid
|
||||
pub fn is_node_name_valid(_name: &str) -> Result<(), &str> {
|
||||
let name = _name.to_string();
|
||||
if name.chars().count() >= crate::NODE_NAME_MAX_LENGTH {
|
||||
return Err("Node name too long");
|
||||
}
|
||||
|
||||
let invalid_chars = r"[\\.@]";
|
||||
let re = Regex::new(invalid_chars).unwrap();
|
||||
if re.is_match(&name) {
|
||||
return Err("Node name should not contain invalid chars such as '.' and '@'");
|
||||
}
|
||||
|
||||
let invalid_patterns = r"(https?:\\/+)?(www)+";
|
||||
let re = Regex::new(invalid_patterns).unwrap();
|
||||
if re.is_match(&name) {
|
||||
return Err("Node name should not contain urls");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create a `NodeKeyConfig` from the given `NodeKeyParams` in the context
|
||||
/// of an optional network config storage directory.
|
||||
pub fn node_key_config<P>(params: NodeKeyParams, net_config_dir: &Option<P>)
|
||||
-> error::Result<NodeKeyConfig>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
match params.node_key_type {
|
||||
NodeKeyType::Ed25519 =>
|
||||
params.node_key.as_ref().map(parse_ed25519_secret).unwrap_or_else(||
|
||||
Ok(params.node_key_file
|
||||
.or_else(|| net_config_file(net_config_dir, NODE_KEY_ED25519_FILE))
|
||||
.map(sc_network::config::Secret::File)
|
||||
.unwrap_or(sc_network::config::Secret::New)))
|
||||
.map(NodeKeyConfig::Ed25519)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an error caused by an invalid node key argument.
|
||||
fn invalid_node_key(e: impl std::fmt::Display) -> error::Error {
|
||||
error::Error::Input(format!("Invalid node key: {}", e))
|
||||
}
|
||||
|
||||
/// Parse a Ed25519 secret key from a hex string into a `sc_network::Secret`.
|
||||
fn parse_ed25519_secret(hex: &String) -> error::Result<sc_network::config::Ed25519Secret> {
|
||||
H256::from_str(&hex).map_err(invalid_node_key).and_then(|bytes|
|
||||
sc_network::config::identity::ed25519::SecretKey::from_bytes(bytes)
|
||||
.map(sc_network::config::Secret::Input)
|
||||
.map_err(invalid_node_key))
|
||||
}
|
||||
|
||||
fn net_config_file<P>(net_config_dir: &Option<P>, name: &str) -> Option<PathBuf>
|
||||
where
|
||||
P: AsRef<Path>
|
||||
{
|
||||
net_config_dir.as_ref().map(|d| d.as_ref().join(name))
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sc_network::config::identity::ed25519;
|
||||
|
||||
#[test]
|
||||
fn tests_node_name_good() {
|
||||
assert!(is_node_name_valid("short name").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tests_node_name_bad() {
|
||||
assert!(is_node_name_valid("long names are not very cool for the ui").is_err());
|
||||
assert!(is_node_name_valid("Dots.not.Ok").is_err());
|
||||
assert!(is_node_name_valid("http://visit.me").is_err());
|
||||
assert!(is_node_name_valid("https://visit.me").is_err());
|
||||
assert!(is_node_name_valid("www.visit.me").is_err());
|
||||
assert!(is_node_name_valid("email@domain").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_node_key_config_input() {
|
||||
fn secret_input(net_config_dir: Option<String>) -> error::Result<()> {
|
||||
NodeKeyType::variants().into_iter().try_for_each(|t| {
|
||||
let node_key_type = NodeKeyType::from_str(t).unwrap();
|
||||
let sk = match node_key_type {
|
||||
NodeKeyType::Ed25519 => ed25519::SecretKey::generate().as_ref().to_vec()
|
||||
};
|
||||
let params = NodeKeyParams {
|
||||
node_key_type,
|
||||
node_key: Some(format!("{:x}", H256::from_slice(sk.as_ref()))),
|
||||
node_key_file: None
|
||||
};
|
||||
node_key_config(params, &net_config_dir).and_then(|c| match c {
|
||||
NodeKeyConfig::Ed25519(sc_network::config::Secret::Input(ref ski))
|
||||
if node_key_type == NodeKeyType::Ed25519 &&
|
||||
&sk[..] == ski.as_ref() => Ok(()),
|
||||
_ => Err(error::Error::Input("Unexpected node key config".into()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
assert!(secret_input(None).is_ok());
|
||||
assert!(secret_input(Some("x".to_string())).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_node_key_config_file() {
|
||||
fn secret_file(net_config_dir: Option<String>) -> error::Result<()> {
|
||||
NodeKeyType::variants().into_iter().try_for_each(|t| {
|
||||
let node_key_type = NodeKeyType::from_str(t).unwrap();
|
||||
let tmp = tempfile::Builder::new().prefix("alice").tempdir()?;
|
||||
let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf();
|
||||
let params = NodeKeyParams {
|
||||
node_key_type,
|
||||
node_key: None,
|
||||
node_key_file: Some(file.clone())
|
||||
};
|
||||
node_key_config(params, &net_config_dir).and_then(|c| match c {
|
||||
NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f))
|
||||
if node_key_type == NodeKeyType::Ed25519 && f == &file => Ok(()),
|
||||
_ => Err(error::Error::Input("Unexpected node key config".into()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
assert!(secret_file(None).is_ok());
|
||||
assert!(secret_file(Some("x".to_string())).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_node_key_config_default() {
|
||||
fn with_def_params<F>(f: F) -> error::Result<()>
|
||||
where
|
||||
F: Fn(NodeKeyParams) -> error::Result<()>
|
||||
{
|
||||
NodeKeyType::variants().into_iter().try_for_each(|t| {
|
||||
let node_key_type = NodeKeyType::from_str(t).unwrap();
|
||||
f(NodeKeyParams {
|
||||
node_key_type,
|
||||
node_key: None,
|
||||
node_key_file: None
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn no_config_dir() -> error::Result<()> {
|
||||
with_def_params(|params| {
|
||||
let typ = params.node_key_type;
|
||||
node_key_config::<String>(params, &None)
|
||||
.and_then(|c| match c {
|
||||
NodeKeyConfig::Ed25519(sc_network::config::Secret::New)
|
||||
if typ == NodeKeyType::Ed25519 => Ok(()),
|
||||
_ => Err(error::Error::Input("Unexpected node key config".into()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn some_config_dir(net_config_dir: String) -> error::Result<()> {
|
||||
with_def_params(|params| {
|
||||
let dir = PathBuf::from(net_config_dir.clone());
|
||||
let typ = params.node_key_type;
|
||||
node_key_config(params, &Some(net_config_dir.clone()))
|
||||
.and_then(move |c| match c {
|
||||
NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f))
|
||||
if typ == NodeKeyType::Ed25519 &&
|
||||
f == &dir.join(NODE_KEY_ED25519_FILE) => Ok(()),
|
||||
_ => Err(error::Error::Input("Unexpected node key config".into()))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
assert!(no_config_dir().is_ok());
|
||||
assert!(some_config_dir("x".to_string()).is_ok());
|
||||
}
|
||||
}
|
||||
+360
-164
@@ -14,10 +14,24 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::traits::GetSharedParams;
|
||||
|
||||
use std::{str::FromStr, path::PathBuf};
|
||||
use structopt::{StructOpt, StructOptInternal, clap::{arg_enum, App, AppSettings, SubCommand, Arg}};
|
||||
use structopt::{StructOpt, clap::arg_enum};
|
||||
use sc_service::{
|
||||
AbstractService, Configuration, ChainSpecExtension, RuntimeGenesis, ServiceBuilderCommand,
|
||||
config::DatabaseConfig,
|
||||
};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
|
||||
use crate::VersionInfo;
|
||||
use crate::error;
|
||||
use std::fmt::Debug;
|
||||
use log::info;
|
||||
use sc_network::config::build_multiaddr;
|
||||
use std::io;
|
||||
use std::fs;
|
||||
use std::io::{Read, Write, Seek};
|
||||
use sp_runtime::generic::BlockId;
|
||||
use crate::runtime::run_until_exit;
|
||||
use crate::node_key::node_key_config;
|
||||
use crate::execution_strategy::*;
|
||||
|
||||
pub use crate::execution_strategy::ExecutionStrategy;
|
||||
@@ -408,7 +422,7 @@ pub struct RunCmd {
|
||||
/// available to relay to private nodes.
|
||||
#[structopt(
|
||||
long = "sentry",
|
||||
conflicts_with_all = &[ "validator" ]
|
||||
conflicts_with_all = &[ "validator", "light" ]
|
||||
)]
|
||||
pub sentry: bool,
|
||||
|
||||
@@ -417,10 +431,7 @@ pub struct RunCmd {
|
||||
pub no_grandpa: bool,
|
||||
|
||||
/// Experimental: Run in light client mode.
|
||||
#[structopt(
|
||||
long = "light",
|
||||
conflicts_with_all = &[ "validator" ]
|
||||
)]
|
||||
#[structopt(long = "light", conflicts_with = "sentry")]
|
||||
pub light: bool,
|
||||
|
||||
/// Listen to all RPC interfaces.
|
||||
@@ -532,9 +543,37 @@ pub struct RunCmd {
|
||||
#[structopt(flatten)]
|
||||
pub pool_config: TransactionPoolParams,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub keyring: Keyring,
|
||||
/// Shortcut for `--name Alice --validator` with session keys for `Alice` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["bob", "charlie", "dave", "eve", "ferdie", "one", "two"])]
|
||||
pub alice: bool,
|
||||
|
||||
/// Shortcut for `--name Bob --validator` with session keys for `Bob` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "charlie", "dave", "eve", "ferdie", "one", "two"])]
|
||||
pub bob: bool,
|
||||
|
||||
/// Shortcut for `--name Charlie --validator` with session keys for `Charlie` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "bob", "dave", "eve", "ferdie", "one", "two"])]
|
||||
pub charlie: bool,
|
||||
|
||||
/// Shortcut for `--name Dave --validator` with session keys for `Dave` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "eve", "ferdie", "one", "two"])]
|
||||
pub dave: bool,
|
||||
|
||||
/// Shortcut for `--name Eve --validator` with session keys for `Eve` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "ferdie", "one", "two"])]
|
||||
pub eve: bool,
|
||||
|
||||
/// Shortcut for `--name Ferdie --validator` with session keys for `Ferdie` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "one", "two"])]
|
||||
pub ferdie: bool,
|
||||
|
||||
/// Shortcut for `--name One --validator` with session keys for `One` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "ferdie", "two"])]
|
||||
pub one: bool,
|
||||
|
||||
/// Shortcut for `--name Two --validator` with session keys for `Two` added to keystore.
|
||||
#[structopt(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "ferdie", "one"])]
|
||||
pub two: bool,
|
||||
|
||||
/// Enable authoring even when offline.
|
||||
#[structopt(long = "force-authoring")]
|
||||
@@ -582,71 +621,20 @@ pub struct RunCmd {
|
||||
pub password_filename: Option<PathBuf>
|
||||
}
|
||||
|
||||
/// Stores all required Cli values for a keyring test account.
|
||||
struct KeyringTestAccountCliValues {
|
||||
help: String,
|
||||
conflicts_with: Vec<String>,
|
||||
name: String,
|
||||
variant: sp_keyring::Sr25519Keyring,
|
||||
}
|
||||
impl RunCmd {
|
||||
/// Get the `Sr25519Keyring` matching one of the flag
|
||||
pub fn get_keyring(&self) -> Option<sp_keyring::Sr25519Keyring> {
|
||||
use sp_keyring::Sr25519Keyring::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
/// The Cli values for all test accounts.
|
||||
static ref TEST_ACCOUNTS_CLI_VALUES: Vec<KeyringTestAccountCliValues> = {
|
||||
sp_keyring::Sr25519Keyring::iter().map(|a| {
|
||||
let help = format!(
|
||||
"Shortcut for `--name {} --validator` with session keys for `{}` added to keystore.",
|
||||
a,
|
||||
a,
|
||||
);
|
||||
let conflicts_with = sp_keyring::Sr25519Keyring::iter()
|
||||
.filter(|b| a != *b)
|
||||
.map(|b| b.to_string().to_lowercase())
|
||||
.chain(std::iter::once("name".to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
let name = a.to_string().to_lowercase();
|
||||
|
||||
KeyringTestAccountCliValues {
|
||||
help,
|
||||
conflicts_with,
|
||||
name,
|
||||
variant: a,
|
||||
}
|
||||
}).collect()
|
||||
};
|
||||
}
|
||||
|
||||
/// Wrapper for exposing the keyring test accounts into the Cli.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Keyring {
|
||||
pub account: Option<sp_keyring::Sr25519Keyring>,
|
||||
}
|
||||
|
||||
impl StructOpt for Keyring {
|
||||
fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||
unimplemented!("Should not be called for `TestAccounts`.")
|
||||
}
|
||||
|
||||
fn from_clap(m: &structopt::clap::ArgMatches) -> Self {
|
||||
Keyring {
|
||||
account: TEST_ACCOUNTS_CLI_VALUES.iter().find(|a| m.is_present(&a.name)).map(|a| a.variant),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StructOptInternal for Keyring {
|
||||
fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
TEST_ACCOUNTS_CLI_VALUES.iter().fold(app, |app, a| {
|
||||
let conflicts_with_strs = a.conflicts_with.iter().map(|s| s.as_str()).collect::<Vec<_>>();
|
||||
|
||||
app.arg(
|
||||
Arg::with_name(&a.name)
|
||||
.long(&a.name)
|
||||
.help(&a.help)
|
||||
.conflicts_with_all(&conflicts_with_strs)
|
||||
.takes_value(false)
|
||||
)
|
||||
})
|
||||
if self.alice { Some(Alice) }
|
||||
else if self.bob { Some(Bob) }
|
||||
else if self.charlie { Some(Charlie) }
|
||||
else if self.dave { Some(Dave) }
|
||||
else if self.eve { Some(Eve) }
|
||||
else if self.ferdie { Some(Ferdie) }
|
||||
else if self.one { Some(One) }
|
||||
else if self.two { Some(Two) }
|
||||
else { None }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,11 +850,8 @@ pub struct PurgeChainCmd {
|
||||
/// The core commands are split into multiple subcommands and `Run` is the default subcommand. From
|
||||
/// the CLI user perspective, it is not visible that `Run` is a subcommand. So, all parameters of
|
||||
/// `Run` are exported as main executable parameters.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CoreParams<CC, RP> {
|
||||
/// Run a node.
|
||||
Run(MergeParameters<RunCmd, RP>),
|
||||
|
||||
#[derive(Debug, Clone, StructOpt)]
|
||||
pub enum Subcommand {
|
||||
/// Build a spec.json file, outputing to stdout.
|
||||
BuildSpec(BuildSpecCmd),
|
||||
|
||||
@@ -884,112 +869,323 @@ pub enum CoreParams<CC, RP> {
|
||||
|
||||
/// Remove the whole chain data.
|
||||
PurgeChain(PurgeChainCmd),
|
||||
|
||||
/// Further custom subcommands.
|
||||
Custom(CC),
|
||||
}
|
||||
|
||||
impl<CC, RP> StructOpt for CoreParams<CC, RP> where
|
||||
CC: StructOpt + GetSharedParams,
|
||||
RP: StructOpt + StructOptInternal,
|
||||
{
|
||||
fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||
RP::augment_clap(
|
||||
RunCmd::augment_clap(
|
||||
CC::clap().unset_setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
)
|
||||
).subcommand(
|
||||
BuildSpecCmd::augment_clap(SubCommand::with_name("build-spec"))
|
||||
.about("Build a spec.json file, outputting to stdout.")
|
||||
)
|
||||
.subcommand(
|
||||
ExportBlocksCmd::augment_clap(SubCommand::with_name("export-blocks"))
|
||||
.about("Export blocks to a file. This file can only be re-imported \
|
||||
if it is in binary format (not JSON!)."
|
||||
)
|
||||
)
|
||||
.subcommand(
|
||||
ImportBlocksCmd::augment_clap(SubCommand::with_name("import-blocks"))
|
||||
.about("Import blocks from file.")
|
||||
)
|
||||
.subcommand(
|
||||
CheckBlockCmd::augment_clap(SubCommand::with_name("check-block"))
|
||||
.about("Re-validate a known block.")
|
||||
)
|
||||
.subcommand(
|
||||
RevertCmd::augment_clap(SubCommand::with_name("revert"))
|
||||
.about("Revert chain to the previous state.")
|
||||
)
|
||||
.subcommand(
|
||||
PurgeChainCmd::augment_clap(SubCommand::with_name("purge-chain"))
|
||||
.about("Remove the whole chain data.")
|
||||
)
|
||||
impl Subcommand {
|
||||
/// Get the shared parameters of a `CoreParams` command
|
||||
pub fn get_shared_params(&self) -> &SharedParams {
|
||||
use Subcommand::*;
|
||||
|
||||
match self {
|
||||
BuildSpec(params) => ¶ms.shared_params,
|
||||
ExportBlocks(params) => ¶ms.shared_params,
|
||||
ImportBlocks(params) => ¶ms.shared_params,
|
||||
CheckBlock(params) => ¶ms.shared_params,
|
||||
Revert(params) => ¶ms.shared_params,
|
||||
PurgeChain(params) => ¶ms.shared_params,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
|
||||
match matches.subcommand() {
|
||||
("build-spec", Some(matches)) =>
|
||||
CoreParams::BuildSpec(BuildSpecCmd::from_clap(matches)),
|
||||
("export-blocks", Some(matches)) =>
|
||||
CoreParams::ExportBlocks(ExportBlocksCmd::from_clap(matches)),
|
||||
("import-blocks", Some(matches)) =>
|
||||
CoreParams::ImportBlocks(ImportBlocksCmd::from_clap(matches)),
|
||||
("check-block", Some(matches)) =>
|
||||
CoreParams::CheckBlock(CheckBlockCmd::from_clap(matches)),
|
||||
("revert", Some(matches)) => CoreParams::Revert(RevertCmd::from_clap(matches)),
|
||||
("purge-chain", Some(matches)) =>
|
||||
CoreParams::PurgeChain(PurgeChainCmd::from_clap(matches)),
|
||||
(_, None) => CoreParams::Run(MergeParameters::from_clap(matches)),
|
||||
_ => CoreParams::Custom(CC::from_clap(matches)),
|
||||
/// Run any `CoreParams` command
|
||||
pub fn run<G, E, B, BC, BB>(
|
||||
self,
|
||||
config: Configuration<G, E>,
|
||||
builder: B,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
B: FnOnce(Configuration<G, E>) -> Result<BC, sc_service::error::Error>,
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
BC: ServiceBuilderCommand<Block = BB> + Unpin,
|
||||
BB: sp_runtime::traits::Block + Debug,
|
||||
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
<BB as BlockT>::Hash: std::str::FromStr,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
match self {
|
||||
Subcommand::BuildSpec(cmd) => cmd.run(config),
|
||||
Subcommand::ExportBlocks(cmd) => cmd.run(config, builder),
|
||||
Subcommand::ImportBlocks(cmd) => cmd.run(config, builder),
|
||||
Subcommand::CheckBlock(cmd) => cmd.run(config, builder),
|
||||
Subcommand::PurgeChain(cmd) => cmd.run(config),
|
||||
Subcommand::Revert(cmd) => cmd.run(config, builder),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A special commandline parameter that expands to nothing.
|
||||
/// Should be used as custom subcommand/run arguments if no custom values are required.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct NoCustom {}
|
||||
impl RunCmd {
|
||||
/// Run the command that runs the node
|
||||
pub fn run<G, E, FNL, FNF, SL, SF>(
|
||||
self,
|
||||
mut config: Configuration<G, E>,
|
||||
new_light: FNL,
|
||||
new_full: FNF,
|
||||
version: &VersionInfo,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
FNL: FnOnce(Configuration<G, E>) -> Result<SL, sc_service::error::Error>,
|
||||
FNF: FnOnce(Configuration<G, E>) -> Result<SF, sc_service::error::Error>,
|
||||
SL: AbstractService + Unpin,
|
||||
SF: AbstractService + Unpin,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
impl StructOpt for NoCustom {
|
||||
fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new("NoCustom")
|
||||
}
|
||||
crate::update_config_for_running_node(
|
||||
&mut config,
|
||||
self,
|
||||
)?;
|
||||
|
||||
fn from_clap(_: &::structopt::clap::ArgMatches) -> Self {
|
||||
NoCustom {}
|
||||
crate::run_node(config, new_light, new_full, &version)
|
||||
}
|
||||
}
|
||||
|
||||
impl StructOptInternal for NoCustom {
|
||||
fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
app
|
||||
impl BuildSpecCmd {
|
||||
/// Run the build-spec command
|
||||
pub fn run<G, E>(
|
||||
self,
|
||||
config: Configuration<G, E>,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
info!("Building chain spec");
|
||||
let mut spec = config.expect_chain_spec().clone();
|
||||
let raw_output = self.raw;
|
||||
|
||||
if spec.boot_nodes().is_empty() && !self.disable_default_bootnode {
|
||||
let node_key = node_key_config(
|
||||
self.node_key_params.clone(),
|
||||
&Some(config
|
||||
.in_chain_config_dir(crate::DEFAULT_NETWORK_CONFIG_PATH)
|
||||
.expect("We provided a base_path")),
|
||||
)?;
|
||||
let keys = node_key.into_keypair()?;
|
||||
let peer_id = keys.public().into_peer_id();
|
||||
let addr = build_multiaddr![
|
||||
Ip4([127, 0, 0, 1]),
|
||||
Tcp(30333u16),
|
||||
P2p(peer_id)
|
||||
];
|
||||
spec.add_boot_node(addr)
|
||||
}
|
||||
|
||||
let json = sc_service::chain_ops::build_spec(spec, raw_output)?;
|
||||
|
||||
print!("{}", json);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GetSharedParams for NoCustom {
|
||||
fn shared_params(&self) -> Option<&SharedParams> {
|
||||
None
|
||||
impl ExportBlocksCmd {
|
||||
/// Run the export-blocks command
|
||||
pub fn run<G, E, B, BC, BB>(
|
||||
self,
|
||||
mut config: Configuration<G, E>,
|
||||
builder: B,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
B: FnOnce(Configuration<G, E>) -> Result<BC, sc_service::error::Error>,
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
BC: ServiceBuilderCommand<Block = BB> + Unpin,
|
||||
BB: sp_runtime::traits::Block + Debug,
|
||||
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
<BB as BlockT>::Hash: std::str::FromStr,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
crate::fill_config_keystore_in_memory(&mut config)?;
|
||||
|
||||
if let DatabaseConfig::Path { ref path, .. } = &config.database {
|
||||
info!("DB path: {}", path.display());
|
||||
}
|
||||
let from = self.from.as_ref().and_then(|f| f.parse().ok()).unwrap_or(1);
|
||||
let to = self.to.as_ref().and_then(|t| t.parse().ok());
|
||||
|
||||
let json = self.json;
|
||||
|
||||
let file: Box<dyn io::Write> = match &self.output {
|
||||
Some(filename) => Box::new(fs::File::create(filename)?),
|
||||
None => Box::new(io::stdout()),
|
||||
};
|
||||
|
||||
run_until_exit(config, |config| {
|
||||
Ok(builder(config)?.export_blocks(file, from.into(), to, json))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Merge all CLI parameters of `L` and `R` into the same level.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MergeParameters<L, R> {
|
||||
/// The left side parameters.
|
||||
pub left: L,
|
||||
/// The right side parameters.
|
||||
pub right: R,
|
||||
/// Internal trait used to cast to a dynamic type that implements Read and Seek.
|
||||
trait ReadPlusSeek: Read + Seek {}
|
||||
|
||||
impl<T: Read + Seek> ReadPlusSeek for T {}
|
||||
|
||||
impl ImportBlocksCmd {
|
||||
/// Run the import-blocks command
|
||||
pub fn run<G, E, B, BC, BB>(
|
||||
self,
|
||||
mut config: Configuration<G, E>,
|
||||
builder: B,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
B: FnOnce(Configuration<G, E>) -> Result<BC, sc_service::error::Error>,
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
BC: ServiceBuilderCommand<Block = BB> + Unpin,
|
||||
BB: sp_runtime::traits::Block + Debug,
|
||||
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
<BB as BlockT>::Hash: std::str::FromStr,
|
||||
{
|
||||
crate::fill_import_params(
|
||||
&mut config,
|
||||
&self.import_params,
|
||||
sc_service::Roles::FULL,
|
||||
self.shared_params.dev,
|
||||
)?;
|
||||
|
||||
let file: Box<dyn ReadPlusSeek + Send> = match &self.input {
|
||||
Some(filename) => Box::new(fs::File::open(filename)?),
|
||||
None => {
|
||||
let mut buffer = Vec::new();
|
||||
io::stdin().read_to_end(&mut buffer)?;
|
||||
Box::new(io::Cursor::new(buffer))
|
||||
},
|
||||
};
|
||||
|
||||
run_until_exit(config, |config| {
|
||||
Ok(builder(config)?.import_blocks(file, false))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, R> StructOpt for MergeParameters<L, R> where L: StructOpt + StructOptInternal, R: StructOpt {
|
||||
fn clap<'a, 'b>() -> App<'a, 'b> {
|
||||
L::augment_clap(R::clap())
|
||||
}
|
||||
impl CheckBlockCmd {
|
||||
/// Run the check-block command
|
||||
pub fn run<G, E, B, BC, BB>(
|
||||
self,
|
||||
mut config: Configuration<G, E>,
|
||||
builder: B,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
B: FnOnce(Configuration<G, E>) -> Result<BC, sc_service::error::Error>,
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
BC: ServiceBuilderCommand<Block = BB> + Unpin,
|
||||
BB: sp_runtime::traits::Block + Debug,
|
||||
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
<BB as BlockT>::Hash: std::str::FromStr,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
fn from_clap(matches: &::structopt::clap::ArgMatches) -> Self {
|
||||
MergeParameters {
|
||||
left: L::from_clap(matches),
|
||||
right: R::from_clap(matches),
|
||||
crate::fill_import_params(
|
||||
&mut config,
|
||||
&self.import_params,
|
||||
sc_service::Roles::FULL,
|
||||
self.shared_params.dev,
|
||||
)?;
|
||||
crate::fill_config_keystore_in_memory(&mut config)?;
|
||||
|
||||
let input = if self.input.starts_with("0x") { &self.input[2..] } else { &self.input[..] };
|
||||
let block_id = match FromStr::from_str(input) {
|
||||
Ok(hash) => BlockId::hash(hash),
|
||||
Err(_) => match self.input.parse::<u32>() {
|
||||
Ok(n) => BlockId::number((n as u32).into()),
|
||||
Err(_) => return Err(error::Error::Input("Invalid hash or number specified".into())),
|
||||
}
|
||||
};
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
run_until_exit(config, |config| {
|
||||
Ok(builder(config)?.check_block(block_id))
|
||||
})?;
|
||||
println!("Completed in {} ms.", start.elapsed().as_millis());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PurgeChainCmd {
|
||||
/// Run the purge command
|
||||
pub fn run<G, E>(
|
||||
self,
|
||||
mut config: Configuration<G, E>,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
crate::fill_config_keystore_in_memory(&mut config)?;
|
||||
|
||||
let db_path = match config.database {
|
||||
DatabaseConfig::Path { path, .. } => path,
|
||||
_ => {
|
||||
eprintln!("Cannot purge custom database implementation");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
if !self.yes {
|
||||
print!("Are you sure to remove {:?}? [y/N]: ", &db_path);
|
||||
io::stdout().flush().expect("failed to flush stdout");
|
||||
|
||||
let mut input = String::new();
|
||||
io::stdin().read_line(&mut input)?;
|
||||
let input = input.trim();
|
||||
|
||||
match input.chars().nth(0) {
|
||||
Some('y') | Some('Y') => {},
|
||||
_ => {
|
||||
println!("Aborted");
|
||||
return Ok(());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match fs::remove_dir_all(&db_path) {
|
||||
Ok(_) => {
|
||||
println!("{:?} removed.", &db_path);
|
||||
Ok(())
|
||||
},
|
||||
Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
|
||||
eprintln!("{:?} did not exist.", &db_path);
|
||||
Ok(())
|
||||
},
|
||||
Err(err) => Result::Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RevertCmd {
|
||||
/// Run the revert command
|
||||
pub fn run<G, E, B, BC, BB>(
|
||||
self,
|
||||
mut config: Configuration<G, E>,
|
||||
builder: B,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
B: FnOnce(Configuration<G, E>) -> Result<BC, sc_service::error::Error>,
|
||||
G: RuntimeGenesis,
|
||||
E: ChainSpecExtension,
|
||||
BC: ServiceBuilderCommand<Block = BB> + Unpin,
|
||||
BB: sp_runtime::traits::Block + Debug,
|
||||
<<<BB as BlockT>::Header as HeaderT>::Number as std::str::FromStr>::Err: std::fmt::Debug,
|
||||
<BB as BlockT>::Hash: std::str::FromStr,
|
||||
{
|
||||
assert!(config.chain_spec.is_some(), "chain_spec must be present before continuing");
|
||||
|
||||
crate::fill_config_keystore_in_memory(&mut config)?;
|
||||
|
||||
let blocks = self.num.parse()?;
|
||||
builder(config)?.revert_chain(blocks)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright 2017-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/>.
|
||||
|
||||
use futures::{Future, future, future::FutureExt};
|
||||
use futures::select;
|
||||
use futures::pin_mut;
|
||||
use sc_service::{AbstractService, Configuration};
|
||||
use crate::error;
|
||||
use crate::informant;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
async fn main<F, E>(func: F) -> Result<(), Box<dyn std::error::Error>>
|
||||
where
|
||||
F: Future<Output = Result<(), E>> + future::FusedFuture,
|
||||
E: 'static + std::error::Error,
|
||||
{
|
||||
use tokio::signal::unix::{signal, SignalKind};
|
||||
|
||||
let mut stream_int = signal(SignalKind::interrupt())?;
|
||||
let mut stream_term = signal(SignalKind::terminate())?;
|
||||
|
||||
let t1 = stream_int.recv().fuse();
|
||||
let t2 = stream_term.recv().fuse();
|
||||
let t3 = func;
|
||||
|
||||
pin_mut!(t1, t2, t3);
|
||||
|
||||
select! {
|
||||
_ = t1 => {},
|
||||
_ = t2 => {},
|
||||
res = t3 => res?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn main<F, E>(func: F) -> Result<(), Box<dyn std::error::Error>>
|
||||
where
|
||||
F: Future<Output = Result<(), E>> + future::FusedFuture,
|
||||
E: 'static + std::error::Error,
|
||||
{
|
||||
use tokio::signal::ctrl_c;
|
||||
|
||||
let t1 = ctrl_c().fuse();
|
||||
let t2 = func;
|
||||
|
||||
pin_mut!(t1, t2);
|
||||
|
||||
select! {
|
||||
_ = t1 => {},
|
||||
res = t2 => res?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_runtime() -> Result<tokio::runtime::Runtime, std::io::Error> {
|
||||
tokio::runtime::Builder::new()
|
||||
.thread_name("main-tokio-")
|
||||
.threaded_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
}
|
||||
|
||||
/// A helper function that runs a future with tokio and stops if the process receives the signal
|
||||
/// SIGTERM or SIGINT
|
||||
pub fn run_until_exit<FUT, ERR, G, E, F>(
|
||||
mut config: Configuration<G, E>,
|
||||
future_builder: F,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
F: FnOnce(Configuration<G, E>) -> error::Result<FUT>,
|
||||
FUT: Future<Output = Result<(), ERR>> + future::Future,
|
||||
ERR: 'static + std::error::Error,
|
||||
{
|
||||
let mut runtime = build_runtime()?;
|
||||
|
||||
config.task_executor = {
|
||||
let runtime_handle = runtime.handle().clone();
|
||||
Some(Box::new(move |fut| { runtime_handle.spawn(fut); }))
|
||||
};
|
||||
|
||||
let f = future_builder(config)?;
|
||||
let f = f.fuse();
|
||||
pin_mut!(f);
|
||||
|
||||
runtime.block_on(main(f)).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A helper function that runs an `AbstractService` with tokio and stops if the process receives
|
||||
/// the signal SIGTERM or SIGINT
|
||||
pub fn run_service_until_exit<T, G, E, F>(
|
||||
mut config: Configuration<G, E>,
|
||||
service_builder: F,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
F: FnOnce(Configuration<G, E>) -> Result<T, sc_service::error::Error>,
|
||||
T: AbstractService + Unpin,
|
||||
{
|
||||
let mut runtime = build_runtime()?;
|
||||
|
||||
config.task_executor = {
|
||||
let runtime_handle = runtime.handle().clone();
|
||||
Some(Box::new(move |fut| { runtime_handle.spawn(fut); }))
|
||||
};
|
||||
|
||||
let service = service_builder(config)?;
|
||||
|
||||
let informant_future = informant::build(&service);
|
||||
let _informant_handle = runtime.spawn(informant_future);
|
||||
|
||||
// we eagerly drop the service so that the internal exit future is fired,
|
||||
// but we need to keep holding a reference to the global telemetry guard
|
||||
let _telemetry = service.telemetry();
|
||||
|
||||
let f = service.fuse();
|
||||
pin_mut!(f);
|
||||
|
||||
runtime.block_on(main(f)).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -72,10 +72,10 @@ use grafana_data_source::{self, record_metrics};
|
||||
/// The order in which the `with_*` methods are called doesn't matter, as the correct binding of
|
||||
/// generics is done when you call `build`.
|
||||
///
|
||||
pub struct ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
pub struct ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend>
|
||||
{
|
||||
config: Configuration<TCfg, TGen, TCSExt>,
|
||||
config: Configuration<TGen, TCSExt>,
|
||||
pub (crate) client: Arc<TCl>,
|
||||
backend: Arc<Backend>,
|
||||
keystore: Arc<RwLock<Keystore>>,
|
||||
@@ -144,8 +144,8 @@ type TFullParts<TBl, TRtApi, TExecDisp> = (
|
||||
);
|
||||
|
||||
/// Creates a new full client for the given config.
|
||||
pub fn new_full_client<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
|
||||
config: &Configuration<TCfg, TGen, TCSExt>,
|
||||
pub fn new_full_client<TBl, TRtApi, TExecDisp, TGen, TCSExt>(
|
||||
config: &Configuration<TGen, TCSExt>,
|
||||
) -> Result<TFullClient<TBl, TRtApi, TExecDisp>, Error> where
|
||||
TBl: BlockT,
|
||||
TExecDisp: NativeExecutionDispatch + 'static,
|
||||
@@ -155,8 +155,8 @@ pub fn new_full_client<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
|
||||
new_full_parts(config).map(|parts| parts.0)
|
||||
}
|
||||
|
||||
fn new_full_parts<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
|
||||
config: &Configuration<TCfg, TGen, TCSExt>,
|
||||
fn new_full_parts<TBl, TRtApi, TExecDisp, TGen, TCSExt>(
|
||||
config: &Configuration<TGen, TCSExt>,
|
||||
) -> Result<TFullParts<TBl, TRtApi, TExecDisp>, Error> where
|
||||
TBl: BlockT,
|
||||
TExecDisp: NativeExecutionDispatch + 'static,
|
||||
@@ -177,13 +177,14 @@ fn new_full_parts<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
|
||||
config.default_heap_pages,
|
||||
);
|
||||
|
||||
let fork_blocks = config.chain_spec
|
||||
let chain_spec = config.expect_chain_spec();
|
||||
let fork_blocks = chain_spec
|
||||
.extensions()
|
||||
.get::<sc_client::ForkBlocks<TBl>>()
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let bad_blocks = config.chain_spec
|
||||
let bad_blocks = chain_spec
|
||||
.extensions()
|
||||
.get::<sc_client::BadBlocks<TBl>>()
|
||||
.cloned()
|
||||
@@ -214,7 +215,7 @@ fn new_full_parts<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
|
||||
sc_client_db::new_client(
|
||||
db_config,
|
||||
executor,
|
||||
&config.chain_spec,
|
||||
config.expect_chain_spec(),
|
||||
fork_blocks,
|
||||
bad_blocks,
|
||||
extensions,
|
||||
@@ -224,15 +225,14 @@ fn new_full_parts<TBl, TRtApi, TExecDisp, TCfg, TGen, TCSExt>(
|
||||
Ok((client, backend, keystore))
|
||||
}
|
||||
|
||||
impl<TCfg, TGen, TCSExt> ServiceBuilder<(), (), TCfg, TGen, TCSExt, (), (), (), (), (), (), (), (), (), ()>
|
||||
impl<TGen, TCSExt> ServiceBuilder<(), (), TGen, TCSExt, (), (), (), (), (), (), (), (), (), ()>
|
||||
where TGen: RuntimeGenesis, TCSExt: Extension {
|
||||
/// Start the service builder with a configuration.
|
||||
pub fn new_full<TBl: BlockT, TRtApi, TExecDisp: NativeExecutionDispatch + 'static>(
|
||||
config: Configuration<TCfg, TGen, TCSExt>
|
||||
config: Configuration<TGen, TCSExt>
|
||||
) -> Result<ServiceBuilder<
|
||||
TBl,
|
||||
TRtApi,
|
||||
TCfg,
|
||||
TGen,
|
||||
TCSExt,
|
||||
TFullClient<TBl, TRtApi, TExecDisp>,
|
||||
@@ -270,11 +270,10 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
|
||||
|
||||
/// Start the service builder with a configuration.
|
||||
pub fn new_light<TBl: BlockT, TRtApi, TExecDisp: NativeExecutionDispatch + 'static>(
|
||||
config: Configuration<TCfg, TGen, TCSExt>
|
||||
config: Configuration<TGen, TCSExt>
|
||||
) -> Result<ServiceBuilder<
|
||||
TBl,
|
||||
TRtApi,
|
||||
TCfg,
|
||||
TGen,
|
||||
TCSExt,
|
||||
TLightClient<TBl, TRtApi, TExecDisp>,
|
||||
@@ -332,7 +331,7 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
|
||||
let remote_blockchain = backend.remote_blockchain();
|
||||
let client = Arc::new(sc_client::light::new_light(
|
||||
backend.clone(),
|
||||
&config.chain_spec,
|
||||
config.expect_chain_spec(),
|
||||
executor,
|
||||
)?);
|
||||
|
||||
@@ -355,8 +354,8 @@ where TGen: RuntimeGenesis, TCSExt: Extension {
|
||||
}
|
||||
}
|
||||
|
||||
impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, Backend>
|
||||
ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
impl<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, Backend>
|
||||
ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend> {
|
||||
|
||||
/// Returns a reference to the client that was stored in this builder.
|
||||
@@ -378,9 +377,9 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
pub fn with_opt_select_chain<USc>(
|
||||
self,
|
||||
select_chain_builder: impl FnOnce(
|
||||
&Configuration<TCfg, TGen, TCSExt>, &Arc<Backend>
|
||||
&Configuration<TGen, TCSExt>, &Arc<Backend>
|
||||
) -> Result<Option<USc>, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, USc, TImpQu, TFprb, TFpp,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, USc, TImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend>, Error> {
|
||||
let select_chain = select_chain_builder(&self.config, &self.backend)?;
|
||||
|
||||
@@ -405,8 +404,8 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
/// Defines which head-of-chain strategy to use.
|
||||
pub fn with_select_chain<USc>(
|
||||
self,
|
||||
builder: impl FnOnce(&Configuration<TCfg, TGen, TCSExt>, &Arc<Backend>) -> Result<USc, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, USc, TImpQu, TFprb, TFpp,
|
||||
builder: impl FnOnce(&Configuration<TGen, TCSExt>, &Arc<Backend>) -> Result<USc, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, USc, TImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend>, Error> {
|
||||
self.with_opt_select_chain(|cfg, b| builder(cfg, b).map(Option::Some))
|
||||
}
|
||||
@@ -414,9 +413,9 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
/// Defines which import queue to use.
|
||||
pub fn with_import_queue<UImpQu>(
|
||||
self,
|
||||
builder: impl FnOnce(&Configuration<TCfg, TGen, TCSExt>, Arc<TCl>, Option<TSc>, Arc<TExPool>)
|
||||
builder: impl FnOnce(&Configuration<TGen, TCSExt>, Arc<TCl>, Option<TSc>, Arc<TExPool>)
|
||||
-> Result<UImpQu, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, UImpQu, TFprb, TFpp,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, UImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend>, Error>
|
||||
where TSc: Clone {
|
||||
let import_queue = builder(
|
||||
@@ -447,8 +446,8 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
/// Defines which network specialization protocol to use.
|
||||
pub fn with_network_protocol<UNetP>(
|
||||
self,
|
||||
network_protocol_builder: impl FnOnce(&Configuration<TCfg, TGen, TCSExt>) -> Result<UNetP, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
network_protocol_builder: impl FnOnce(&Configuration<TGen, TCSExt>) -> Result<UNetP, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
UNetP, TExPool, TRpc, Backend>, Error> {
|
||||
let network_protocol = network_protocol_builder(&self.config)?;
|
||||
|
||||
@@ -477,7 +476,6 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
) -> Result<ServiceBuilder<
|
||||
TBl,
|
||||
TRtApi,
|
||||
TCfg,
|
||||
TGen,
|
||||
TCSExt,
|
||||
TCl,
|
||||
@@ -518,7 +516,6 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
) -> Result<ServiceBuilder<
|
||||
TBl,
|
||||
TRtApi,
|
||||
TCfg,
|
||||
TGen,
|
||||
TCSExt,
|
||||
TCl,
|
||||
@@ -539,14 +536,14 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
pub fn with_import_queue_and_opt_fprb<UImpQu, UFprb>(
|
||||
self,
|
||||
builder: impl FnOnce(
|
||||
&Configuration<TCfg, TGen, TCSExt>,
|
||||
&Configuration<TGen, TCSExt>,
|
||||
Arc<TCl>,
|
||||
Arc<Backend>,
|
||||
Option<TFchr>,
|
||||
Option<TSc>,
|
||||
Arc<TExPool>,
|
||||
) -> Result<(UImpQu, Option<UFprb>), Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, UImpQu, UFprb, TFpp,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, UImpQu, UFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend>, Error>
|
||||
where TSc: Clone, TFchr: Clone {
|
||||
let (import_queue, fprb) = builder(
|
||||
@@ -580,14 +577,14 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
pub fn with_import_queue_and_fprb<UImpQu, UFprb>(
|
||||
self,
|
||||
builder: impl FnOnce(
|
||||
&Configuration<TCfg, TGen, TCSExt>,
|
||||
&Configuration<TGen, TCSExt>,
|
||||
Arc<TCl>,
|
||||
Arc<Backend>,
|
||||
Option<TFchr>,
|
||||
Option<TSc>,
|
||||
Arc<TExPool>,
|
||||
) -> Result<(UImpQu, UFprb), Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, UImpQu, UFprb, TFpp,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, UImpQu, UFprb, TFpp,
|
||||
TNetP, TExPool, TRpc, Backend>, Error>
|
||||
where TSc: Clone, TFchr: Clone {
|
||||
self.with_import_queue_and_opt_fprb(|cfg, cl, b, f, sc, tx|
|
||||
@@ -604,7 +601,7 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
Arc<TCl>,
|
||||
Option<TFchr>,
|
||||
) -> Result<UExPool, Error>
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
TNetP, UExPool, TRpc, Backend>, Error>
|
||||
where TSc: Clone, TFchr: Clone {
|
||||
let transaction_pool = transaction_pool_builder(
|
||||
@@ -641,7 +638,7 @@ impl<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp, TNet
|
||||
Option<TFchr>,
|
||||
Option<Arc<dyn RemoteBlockchain<TBl>>>,
|
||||
) -> Result<URpc, Error>,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TCfg, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
) -> Result<ServiceBuilder<TBl, TRtApi, TGen, TCSExt, TCl, TFchr, TSc, TImpQu, TFprb, TFpp,
|
||||
TNetP, TExPool, URpc, Backend>, Error>
|
||||
where TSc: Clone, TFchr: Clone {
|
||||
let rpc_extensions = rpc_ext_builder(
|
||||
@@ -705,11 +702,10 @@ pub trait ServiceBuilderCommand {
|
||||
) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send>>;
|
||||
}
|
||||
|
||||
impl<TBl, TRtApi, TCfg, TGen, TCSExt, TBackend, TExec, TSc, TImpQu, TNetP, TExPool, TRpc>
|
||||
impl<TBl, TRtApi, TGen, TCSExt, TBackend, TExec, TSc, TImpQu, TNetP, TExPool, TRpc>
|
||||
ServiceBuilder<
|
||||
TBl,
|
||||
TRtApi,
|
||||
TCfg,
|
||||
TGen,
|
||||
TCSExt,
|
||||
Client<TBackend, TExec, TBl, TRtApi>,
|
||||
@@ -733,7 +729,6 @@ ServiceBuilder<
|
||||
sp_api::ApiExt<TBl, StateBackend = TBackend::State>,
|
||||
TBl: BlockT,
|
||||
TRtApi: 'static + Send + Sync,
|
||||
TCfg: Default,
|
||||
TGen: RuntimeGenesis,
|
||||
TCSExt: Extension,
|
||||
TBackend: 'static + sc_client_api::backend::Backend<TBl> + Send,
|
||||
@@ -799,6 +794,7 @@ ServiceBuilder<
|
||||
|
||||
let import_queue = Box::new(import_queue);
|
||||
let chain_info = client.chain_info();
|
||||
let chain_spec = config.expect_chain_spec();
|
||||
|
||||
let version = config.full_version();
|
||||
info!("Highest known block at #{}", chain_info.best_number);
|
||||
@@ -821,7 +817,7 @@ ServiceBuilder<
|
||||
});
|
||||
|
||||
let protocol_id = {
|
||||
let protocol_id_full = match config.chain_spec.protocol_id() {
|
||||
let protocol_id_full = match chain_spec.protocol_id() {
|
||||
Some(pid) => pid,
|
||||
None => {
|
||||
warn!("Using default protocol ID {:?} because none is configured in the \
|
||||
@@ -1030,10 +1026,10 @@ ServiceBuilder<
|
||||
use sc_rpc::{chain, state, author, system, offchain};
|
||||
|
||||
let system_info = sc_rpc::system::SystemInfo {
|
||||
chain_name: config.chain_spec.name().into(),
|
||||
chain_name: chain_spec.name().into(),
|
||||
impl_name: config.impl_name.into(),
|
||||
impl_version: config.impl_version.into(),
|
||||
properties: config.chain_spec.properties().clone(),
|
||||
properties: chain_spec.properties().clone(),
|
||||
};
|
||||
|
||||
let subscriptions = sc_rpc::Subscriptions::new(Arc::new(SpawnTaskHandle {
|
||||
@@ -1119,7 +1115,7 @@ ServiceBuilder<
|
||||
let name = config.name.clone();
|
||||
let impl_name = config.impl_name.to_owned();
|
||||
let version = version.clone();
|
||||
let chain_name = config.chain_spec.name().to_owned();
|
||||
let chain_name = config.expect_chain_spec().name().to_owned();
|
||||
let telemetry_connection_sinks_ = telemetry_connection_sinks.clone();
|
||||
let telemetry = sc_telemetry::init_telemetry(sc_telemetry::TelemetryConfig {
|
||||
endpoints,
|
||||
@@ -1188,7 +1184,7 @@ ServiceBuilder<
|
||||
essential_failed_rx,
|
||||
to_spawn_tx,
|
||||
to_spawn_rx,
|
||||
tasks_executor: if let Some(exec) = config.tasks_executor {
|
||||
task_executor: if let Some(exec) = config.task_executor {
|
||||
exec
|
||||
} else {
|
||||
return Err(Error::TasksExecutorRequired);
|
||||
|
||||
@@ -44,11 +44,11 @@ pub fn build_spec<G, E>(spec: ChainSpec<G, E>, raw: bool) -> error::Result<Strin
|
||||
}
|
||||
|
||||
impl<
|
||||
TBl, TRtApi, TCfg, TGen, TCSExt, TBackend,
|
||||
TBl, TRtApi, TGen, TCSExt, TBackend,
|
||||
TExec, TFchr, TSc, TImpQu, TFprb, TFpp, TNetP,
|
||||
TExPool, TRpc, Backend
|
||||
> ServiceBuilderCommand for ServiceBuilder<
|
||||
TBl, TRtApi, TCfg, TGen, TCSExt, Client<TBackend, TExec, TBl, TRtApi>,
|
||||
TBl, TRtApi, TGen, TCSExt, Client<TBackend, TExec, TBl, TRtApi>,
|
||||
TFchr, TSc, TImpQu, TFprb, TFpp, TNetP, TExPool, TRpc, Backend
|
||||
> where
|
||||
TBl: BlockT,
|
||||
|
||||
@@ -23,13 +23,13 @@ pub use sc_executor::WasmExecutionMethod;
|
||||
|
||||
use std::{future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc};
|
||||
pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions;
|
||||
use sc_chain_spec::{ChainSpec, RuntimeGenesis, Extension, NoExtension};
|
||||
use sc_chain_spec::{ChainSpec, NoExtension};
|
||||
use sp_core::crypto::Protected;
|
||||
use target_info::Target;
|
||||
use sc_telemetry::TelemetryEndpoints;
|
||||
|
||||
/// Service configuration.
|
||||
pub struct Configuration<C, G, E = NoExtension> {
|
||||
pub struct Configuration<G, E = NoExtension> {
|
||||
/// Implementation name
|
||||
pub impl_name: &'static str,
|
||||
/// Implementation version
|
||||
@@ -39,7 +39,7 @@ pub struct Configuration<C, G, E = NoExtension> {
|
||||
/// Node roles.
|
||||
pub roles: Roles,
|
||||
/// How to spawn background tasks. Mandatory, otherwise creating a `Service` will error.
|
||||
pub tasks_executor: Option<Box<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send>>,
|
||||
pub task_executor: Option<Box<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send>>,
|
||||
/// Extrinsic pool configuration.
|
||||
pub transaction_pool: TransactionPoolOptions,
|
||||
/// Network configuration.
|
||||
@@ -57,9 +57,7 @@ pub struct Configuration<C, G, E = NoExtension> {
|
||||
/// Pruning settings.
|
||||
pub pruning: PruningMode,
|
||||
/// Chain configuration.
|
||||
pub chain_spec: ChainSpec<G, E>,
|
||||
/// Custom configuration.
|
||||
pub custom: C,
|
||||
pub chain_spec: Option<ChainSpec<G, E>>,
|
||||
/// Node name.
|
||||
pub name: String,
|
||||
/// Wasm execution method.
|
||||
@@ -146,22 +144,18 @@ pub enum DatabaseConfig {
|
||||
Custom(Arc<dyn KeyValueDB>),
|
||||
}
|
||||
|
||||
impl<C, G, E> Configuration<C, G, E> where
|
||||
C: Default,
|
||||
G: RuntimeGenesis,
|
||||
E: Extension,
|
||||
{
|
||||
/// Create a default config for given chain spec and path to configuration dir
|
||||
pub fn default_with_spec_and_base_path(chain_spec: ChainSpec<G, E>, config_dir: Option<PathBuf>) -> Self {
|
||||
let mut configuration = Configuration {
|
||||
impl<G, E> Default for Configuration<G, E> {
|
||||
/// Create a default config
|
||||
fn default() -> Self {
|
||||
let configuration = Configuration {
|
||||
impl_name: "parity-substrate",
|
||||
impl_version: "0.0.0",
|
||||
impl_commit: "",
|
||||
chain_spec,
|
||||
config_dir: config_dir.clone(),
|
||||
chain_spec: None,
|
||||
config_dir: None,
|
||||
name: Default::default(),
|
||||
roles: Roles::FULL,
|
||||
tasks_executor: None,
|
||||
task_executor: None,
|
||||
transaction_pool: Default::default(),
|
||||
network: Default::default(),
|
||||
keystore: KeystoreConfig::None,
|
||||
@@ -171,7 +165,6 @@ impl<C, G, E> Configuration<C, G, E> where
|
||||
},
|
||||
state_cache_size: Default::default(),
|
||||
state_cache_child_ratio: Default::default(),
|
||||
custom: Default::default(),
|
||||
pruning: PruningMode::default(),
|
||||
wasm_method: WasmExecutionMethod::Interpreted,
|
||||
execution_strategies: Default::default(),
|
||||
@@ -191,16 +184,13 @@ impl<C, G, E> Configuration<C, G, E> where
|
||||
tracing_targets: Default::default(),
|
||||
tracing_receiver: Default::default(),
|
||||
};
|
||||
configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec();
|
||||
|
||||
configuration.telemetry_endpoints = configuration.chain_spec.telemetry_endpoints().clone();
|
||||
|
||||
configuration
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl<C, G, E> Configuration<C, G, E> {
|
||||
impl<G, E> Configuration<G, E> {
|
||||
/// Returns full version string of this configuration.
|
||||
pub fn full_version(&self) -> String {
|
||||
full_version_from_strs(self.impl_version, self.impl_commit)
|
||||
@@ -216,11 +206,20 @@ impl<C, G, E> Configuration<C, G, E> {
|
||||
pub fn in_chain_config_dir(&self, sub: &str) -> Option<PathBuf> {
|
||||
self.config_dir.clone().map(|mut path| {
|
||||
path.push("chains");
|
||||
path.push(self.chain_spec.id());
|
||||
path.push(self.expect_chain_spec().id());
|
||||
path.push(sub);
|
||||
path
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a reference to the `ChainSpec` of this `Configuration`.
|
||||
///
|
||||
/// ### Panics
|
||||
///
|
||||
/// This method panic if the `chain_spec` is `None`
|
||||
pub fn expect_chain_spec(&self) -> &ChainSpec<G, E> {
|
||||
self.chain_spec.as_ref().expect("chain_spec must be specified")
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns platform info
|
||||
|
||||
@@ -96,7 +96,7 @@ pub struct Service<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> {
|
||||
/// Receiver for futures that must be spawned as background tasks.
|
||||
to_spawn_rx: mpsc::UnboundedReceiver<(Pin<Box<dyn Future<Output = ()> + Send>>, Cow<'static, str>)>,
|
||||
/// How to spawn background tasks.
|
||||
tasks_executor: Box<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send>,
|
||||
task_executor: Box<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send>,
|
||||
rpc_handlers: sc_rpc_server::RpcHandler<sc_rpc::Metadata>,
|
||||
_rpc: Box<dyn std::any::Any + Send + Sync>,
|
||||
_telemetry: Option<sc_telemetry::Telemetry>,
|
||||
@@ -340,7 +340,7 @@ impl<TBl: Unpin, TCl, TSc: Unpin, TNetStatus, TNet, TTxPool, TOc> Future for
|
||||
}
|
||||
|
||||
while let Poll::Ready(Some((task_to_spawn, name))) = Pin::new(&mut this.to_spawn_rx).poll_next(cx) {
|
||||
(this.tasks_executor)(Box::pin(futures_diagnose::diagnose(name, task_to_spawn)));
|
||||
(this.task_executor)(Box::pin(futures_diagnose::diagnose(name, task_to_spawn)));
|
||||
}
|
||||
|
||||
// The service future never ends.
|
||||
@@ -523,8 +523,8 @@ impl<TBl, TCl, TSc, TNetStatus, TNet, TTxPool, TOc> Drop for
|
||||
|
||||
/// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them alive.
|
||||
#[cfg(not(target_os = "unknown"))]
|
||||
fn start_rpc_servers<C, G, E, H: FnMut() -> sc_rpc_server::RpcHandler<sc_rpc::Metadata>>(
|
||||
config: &Configuration<C, G, E>,
|
||||
fn start_rpc_servers<G, E, H: FnMut() -> sc_rpc_server::RpcHandler<sc_rpc::Metadata>>(
|
||||
config: &Configuration<G, E>,
|
||||
mut gen_handler: H
|
||||
) -> Result<Box<dyn std::any::Any + Send + Sync>, error::Error> {
|
||||
fn maybe_start_server<T, F>(address: Option<SocketAddr>, mut start: F) -> Result<Option<T>, io::Error>
|
||||
@@ -564,8 +564,8 @@ fn start_rpc_servers<C, G, E, H: FnMut() -> sc_rpc_server::RpcHandler<sc_rpc::Me
|
||||
|
||||
/// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them alive.
|
||||
#[cfg(target_os = "unknown")]
|
||||
fn start_rpc_servers<C, G, E, H: FnMut() -> sc_rpc_server::RpcHandler<sc_rpc::Metadata>>(
|
||||
_: &Configuration<C, G, E>,
|
||||
fn start_rpc_servers<G, E, H: FnMut() -> sc_rpc_server::RpcHandler<sc_rpc::Metadata>>(
|
||||
_: &Configuration<G, E>,
|
||||
_: H
|
||||
) -> Result<Box<dyn std::any::Any + Send + Sync>, error::Error> {
|
||||
Ok(Box::new(()))
|
||||
|
||||
@@ -133,11 +133,11 @@ fn node_config<G, E: Clone> (
|
||||
index: usize,
|
||||
spec: &ChainSpec<G, E>,
|
||||
role: Roles,
|
||||
tasks_executor: Box<dyn Fn(Pin<Box<dyn futures::Future<Output = ()> + Send>>) + Send>,
|
||||
task_executor: Box<dyn Fn(Pin<Box<dyn futures::Future<Output = ()> + Send>>) + Send>,
|
||||
key_seed: Option<String>,
|
||||
base_port: u16,
|
||||
root: &TempDir,
|
||||
) -> Configuration<(), G, E>
|
||||
) -> Configuration<G, E>
|
||||
{
|
||||
let root = root.path().join(format!("node-{}", index));
|
||||
|
||||
@@ -175,7 +175,7 @@ fn node_config<G, E: Clone> (
|
||||
impl_version: "0.1",
|
||||
impl_commit: "",
|
||||
roles: role,
|
||||
tasks_executor: Some(tasks_executor),
|
||||
task_executor: Some(task_executor),
|
||||
transaction_pool: Default::default(),
|
||||
network: network_config,
|
||||
keystore: KeystoreConfig::Path {
|
||||
@@ -190,8 +190,7 @@ fn node_config<G, E: Clone> (
|
||||
state_cache_size: 16777216,
|
||||
state_cache_child_ratio: None,
|
||||
pruning: Default::default(),
|
||||
chain_spec: (*spec).clone(),
|
||||
custom: Default::default(),
|
||||
chain_spec: Some((*spec).clone()),
|
||||
name: format!("Node {}", index),
|
||||
wasm_method: sc_service::config::WasmExecutionMethod::Interpreted,
|
||||
execution_strategies: Default::default(),
|
||||
@@ -221,11 +220,11 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
|
||||
fn new(
|
||||
temp: &TempDir,
|
||||
spec: ChainSpec<G, E>,
|
||||
full: impl Iterator<Item = impl FnOnce(Configuration<(), G, E>) -> Result<(F, U), Error>>,
|
||||
light: impl Iterator<Item = impl FnOnce(Configuration<(), G, E>) -> Result<L, Error>>,
|
||||
full: impl Iterator<Item = impl FnOnce(Configuration<G, E>) -> Result<(F, U), Error>>,
|
||||
light: impl Iterator<Item = impl FnOnce(Configuration<G, E>) -> Result<L, Error>>,
|
||||
authorities: impl Iterator<Item = (
|
||||
String,
|
||||
impl FnOnce(Configuration<(), G, E>) -> Result<(F, U), Error>
|
||||
impl FnOnce(Configuration<G, E>) -> Result<(F, U), Error>
|
||||
)>,
|
||||
base_port: u16
|
||||
) -> TestNet<G, E, F, L, U> {
|
||||
@@ -248,14 +247,14 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
|
||||
fn insert_nodes(
|
||||
&mut self,
|
||||
temp: &TempDir,
|
||||
full: impl Iterator<Item = impl FnOnce(Configuration<(), G, E>) -> Result<(F, U), Error>>,
|
||||
light: impl Iterator<Item = impl FnOnce(Configuration<(), G, E>) -> Result<L, Error>>,
|
||||
authorities: impl Iterator<Item = (String, impl FnOnce(Configuration<(), G, E>) -> Result<(F, U), Error>)>
|
||||
full: impl Iterator<Item = impl FnOnce(Configuration<G, E>) -> Result<(F, U), Error>>,
|
||||
light: impl Iterator<Item = impl FnOnce(Configuration<G, E>) -> Result<L, Error>>,
|
||||
authorities: impl Iterator<Item = (String, impl FnOnce(Configuration<G, E>) -> Result<(F, U), Error>)>
|
||||
) {
|
||||
let executor = self.runtime.executor();
|
||||
|
||||
for (key, authority) in authorities {
|
||||
let tasks_executor = {
|
||||
let task_executor = {
|
||||
let executor = executor.clone();
|
||||
Box::new(move |fut: Pin<Box<dyn futures::Future<Output = ()> + Send>>| executor.spawn(fut.unit_error().compat()))
|
||||
};
|
||||
@@ -263,7 +262,7 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
|
||||
self.nodes,
|
||||
&self.chain_spec,
|
||||
Roles::AUTHORITY,
|
||||
tasks_executor,
|
||||
task_executor,
|
||||
Some(key),
|
||||
self.base_port,
|
||||
&temp,
|
||||
@@ -279,11 +278,11 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
|
||||
}
|
||||
|
||||
for full in full {
|
||||
let tasks_executor = {
|
||||
let task_executor = {
|
||||
let executor = executor.clone();
|
||||
Box::new(move |fut: Pin<Box<dyn futures::Future<Output = ()> + Send>>| executor.spawn(fut.unit_error().compat()))
|
||||
};
|
||||
let node_config = node_config(self.nodes, &self.chain_spec, Roles::FULL, tasks_executor, None, self.base_port, &temp);
|
||||
let node_config = node_config(self.nodes, &self.chain_spec, Roles::FULL, task_executor, None, self.base_port, &temp);
|
||||
let addr = node_config.network.listen_addresses.iter().next().unwrap().clone();
|
||||
let (service, user_data) = full(node_config).expect("Error creating test node service");
|
||||
let service = SyncService::from(service);
|
||||
@@ -295,11 +294,11 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
|
||||
}
|
||||
|
||||
for light in light {
|
||||
let tasks_executor = {
|
||||
let task_executor = {
|
||||
let executor = executor.clone();
|
||||
Box::new(move |fut: Pin<Box<dyn futures::Future<Output = ()> + Send>>| executor.spawn(fut.unit_error().compat()))
|
||||
};
|
||||
let node_config = node_config(self.nodes, &self.chain_spec, Roles::LIGHT, tasks_executor, None, self.base_port, &temp);
|
||||
let node_config = node_config(self.nodes, &self.chain_spec, Roles::LIGHT, task_executor, None, self.base_port, &temp);
|
||||
let addr = node_config.network.listen_addresses.iter().next().unwrap().clone();
|
||||
let service = SyncService::from(light(node_config).expect("Error creating test node service"));
|
||||
|
||||
@@ -321,9 +320,9 @@ pub fn connectivity<G, E, Fb, F, Lb, L>(
|
||||
light_builder: Lb,
|
||||
) where
|
||||
E: Clone,
|
||||
Fb: Fn(Configuration<(), G, E>) -> Result<F, Error>,
|
||||
Fb: Fn(Configuration<G, E>) -> Result<F, Error>,
|
||||
F: AbstractService,
|
||||
Lb: Fn(Configuration<(), G, E>) -> Result<L, Error>,
|
||||
Lb: Fn(Configuration<G, E>) -> Result<L, Error>,
|
||||
L: AbstractService,
|
||||
{
|
||||
const NUM_FULL_NODES: usize = 5;
|
||||
@@ -420,9 +419,9 @@ pub fn sync<G, E, Fb, F, Lb, L, B, ExF, U>(
|
||||
mut make_block_and_import: B,
|
||||
mut extrinsic_factory: ExF
|
||||
) where
|
||||
Fb: Fn(Configuration<(), G, E>) -> Result<(F, U), Error>,
|
||||
Fb: Fn(Configuration<G, E>) -> Result<(F, U), Error>,
|
||||
F: AbstractService,
|
||||
Lb: Fn(Configuration<(), G, E>) -> Result<L, Error>,
|
||||
Lb: Fn(Configuration<G, E>) -> Result<L, Error>,
|
||||
L: AbstractService,
|
||||
B: FnMut(&F, &mut U),
|
||||
ExF: FnMut(&F, &U) -> <F::Block as BlockT>::Extrinsic,
|
||||
@@ -489,9 +488,9 @@ pub fn consensus<G, E, Fb, F, Lb, L>(
|
||||
light_builder: Lb,
|
||||
authorities: impl IntoIterator<Item = String>
|
||||
) where
|
||||
Fb: Fn(Configuration<(), G, E>) -> Result<F, Error>,
|
||||
Fb: Fn(Configuration<G, E>) -> Result<F, Error>,
|
||||
F: AbstractService,
|
||||
Lb: Fn(Configuration<(), G, E>) -> Result<L, Error>,
|
||||
Lb: Fn(Configuration<G, E>) -> Result<L, Error>,
|
||||
L: AbstractService,
|
||||
E: Clone,
|
||||
{
|
||||
|
||||
@@ -113,6 +113,33 @@ impl From<Keyring> for sp_runtime::MultiSigner {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseKeyringError;
|
||||
|
||||
impl std::fmt::Display for ParseKeyringError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ParseKeyringError")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Keyring {
|
||||
type Err = ParseKeyringError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, <Self as std::str::FromStr>::Err> {
|
||||
match s {
|
||||
"alice" => Ok(Keyring::Alice),
|
||||
"bob" => Ok(Keyring::Bob),
|
||||
"charlie" => Ok(Keyring::Charlie),
|
||||
"dave" => Ok(Keyring::Dave),
|
||||
"eve" => Ok(Keyring::Eve),
|
||||
"ferdie" => Ok(Keyring::Ferdie),
|
||||
"one" => Ok(Keyring::One),
|
||||
"two" => Ok(Keyring::Two),
|
||||
_ => Err(ParseKeyringError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref PRIVATE_KEYS: HashMap<Keyring, Pair> = {
|
||||
Keyring::iter().map(|i| (i, i.pair())).collect()
|
||||
|
||||
@@ -34,25 +34,24 @@ pub use console_log::init_with_level as init_console_log;
|
||||
/// Create a service configuration from a chain spec and the websocket transport.
|
||||
///
|
||||
/// This configuration contains good defaults for a browser light client.
|
||||
pub async fn browser_configuration<C, G, E>(
|
||||
pub async fn browser_configuration<G, E>(
|
||||
transport: Transport,
|
||||
chain_spec: ChainSpec<G, E>,
|
||||
) -> Result<Configuration<C, G, E>, Box<dyn std::error::Error>>
|
||||
) -> Result<Configuration<G, E>, Box<dyn std::error::Error>>
|
||||
where
|
||||
C: Default,
|
||||
G: RuntimeGenesis,
|
||||
E: Extension,
|
||||
{
|
||||
let name = chain_spec.name().to_string();
|
||||
|
||||
let transport = ExtTransport::new(transport);
|
||||
let mut config = Configuration::default_with_spec_and_base_path(chain_spec, None);
|
||||
let mut config = Configuration::default();
|
||||
config.network.transport = network::config::TransportConfig::Normal {
|
||||
wasm_external_transport: Some(transport.clone()),
|
||||
allow_private_ipv4: true,
|
||||
enable_mdns: false,
|
||||
};
|
||||
config.tasks_executor = Some(Box::new(move |fut| {
|
||||
config.task_executor = Some(Box::new(move |fut| {
|
||||
wasm_bindgen_futures::spawn_local(fut)
|
||||
}));
|
||||
config.telemetry_external_transport = Some(transport);
|
||||
|
||||
Reference in New Issue
Block a user