diff --git a/substrate/core/cli/src/lib.rs b/substrate/core/cli/src/lib.rs index f6a727931f..5821eddfef 100644 --- a/substrate/core/cli/src/lib.rs +++ b/substrate/core/cli/src/lib.rs @@ -113,13 +113,17 @@ pub trait IntoExit { fn into_exit(self) -> Self::Exit; } +fn get_chain_key(matches: &clap::ArgMatches) -> String { + matches.value_of("chain").unwrap_or_else( + || if matches.is_present("dev") { "dev" } else { "" } + ).into() +} + fn load_spec(matches: &clap::ArgMatches, factory: F) -> Result, String> where G: RuntimeGenesis, F: FnOnce(&str) -> Result>, String>, { - let chain_key = matches.value_of("chain").unwrap_or_else( - || if matches.is_present("dev") { "dev" } else { "" } - ); - let spec = match factory(chain_key)? { + let chain_key = get_chain_key(matches); + let spec = match factory(&chain_key)? { Some(spec) => spec, None => ChainSpec::from_json_file(PathBuf::from(chain_key))? }; @@ -132,6 +136,10 @@ fn base_path(matches: &clap::ArgMatches) -> PathBuf { .unwrap_or_else(default_base_path) } +fn create_input_err>(msg: T) -> error::Error { + error::ErrorKind::Input(msg.into()).into() +} + /// Check whether a node name is considered as valid fn is_node_name_valid(_name: &str) -> Result<(), &str> { const MAX_NODE_NAME_LENGTH: usize = 32; @@ -202,8 +210,14 @@ where }; match is_node_name_valid(&config.name) { Ok(_) => (), - Err(msg) => return Err(error::ErrorKind::Input( - format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", config.name, msg)).into()) + Err(msg) => bail!( + create_input_err( + format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", + config.name, + msg + ) + ) + ) } let base_path = base_path(&matches); @@ -220,7 +234,7 @@ where Some("archive") => PruningMode::ArchiveAll, None => PruningMode::default(), Some(s) => PruningMode::keep_blocks(s.parse() - .map_err(|_| error::ErrorKind::Input("Invalid pruning mode specified".to_owned()))?), + .map_err(|_| create_input_err("Invalid pruning mode specified"))?), }; let role = @@ -240,7 +254,7 @@ where "both" => service::ExecutionStrategy::Both, "native" => service::ExecutionStrategy::NativeWhenPossible, "wasm" => service::ExecutionStrategy::AlwaysWasm, - _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), + _ => bail!(create_input_err("Invalid execution mode specified")), }; } @@ -280,7 +294,7 @@ where config.network.client_version = config.client_id(); config.network.use_secret = match matches.value_of("node_key").map(H256::from_str) { Some(Ok(secret)) => Some(secret.into()), - Some(Err(err)) => return Err(format!("Error parsing node key: {}", err).into()), + Some(Err(err)) => bail!(create_input_err(format!("Error parsing node key: {}", err))), None => None, }; @@ -318,6 +332,47 @@ where Ok((spec, config)) } +fn get_db_path_for_subcommand( + main_cmd: &clap::ArgMatches, + sub_cmd: &clap::ArgMatches +) -> error::Result { + if main_cmd.is_present("chain") && sub_cmd.is_present("chain") { + bail!(create_input_err("`--chain` option is present two times")); + } + + fn check_contradicting_chain_dev_flags( + m0: &clap::ArgMatches, + m1: &clap::ArgMatches + ) -> error::Result<()> { + if m0.is_present("dev") && m1.is_present("chain") { + bail!(create_input_err("`--dev` and `--chain` given on different levels")); + } + + Ok(()) + } + + check_contradicting_chain_dev_flags(main_cmd, sub_cmd)?; + check_contradicting_chain_dev_flags(sub_cmd, main_cmd)?; + + let spec_id = if sub_cmd.is_present("chain") || sub_cmd.is_present("dev") { + get_chain_key(sub_cmd) + } else { + get_chain_key(main_cmd) + }; + + if main_cmd.is_present("base_path") && sub_cmd.is_present("base_path") { + bail!(create_input_err("`--base_path` option is present two times")); + } + + let base_path = if sub_cmd.is_present("base_path") { + base_path(sub_cmd) + } else { + base_path(main_cmd) + }; + + Ok(db_path(&base_path, &spec_id)) +} + // // IANA unassigned port ranges that we could use: // 6717-6766 Unassigned @@ -337,30 +392,40 @@ where E: IntoExit, F: ServiceFactory, { - panic_hook::set(); let log_pattern = matches.value_of("log").unwrap_or(""); init_logger(log_pattern); fdlimit::raise_fd_limit(); - let base_path = base_path(matches); - let db_path = db_path(&base_path, spec.id()); - if let Some(matches) = matches.subcommand_matches("build-spec") { build_spec::(matches, spec, config)?; return Ok(Action::ExecutedInternally); - } else if let Some(matches) = matches.subcommand_matches("export-blocks") { - export_blocks::(db_path, matches, spec, exit.into_exit())?; + } else if let Some(sub_matches) = matches.subcommand_matches("export-blocks") { + export_blocks::( + get_db_path_for_subcommand(matches, sub_matches)?, + matches, + spec, + exit.into_exit() + )?; return Ok(Action::ExecutedInternally); - } else if let Some(matches) = matches.subcommand_matches("import-blocks") { - import_blocks::(db_path, matches, spec, exit.into_exit())?; + } else if let Some(sub_matches) = matches.subcommand_matches("import-blocks") { + import_blocks::( + get_db_path_for_subcommand(matches, sub_matches)?, + matches, + spec, + exit.into_exit() + )?; return Ok(Action::ExecutedInternally); - } else if let Some(matches) = matches.subcommand_matches("revert") { - revert_chain::(db_path, matches, spec)?; + } else if let Some(sub_matches) = matches.subcommand_matches("revert") { + revert_chain::( + get_db_path_for_subcommand(matches, sub_matches)?, + matches, + spec + )?; return Ok(Action::ExecutedInternally); - } else if let Some(matches) = matches.subcommand_matches("purge-chain") { - purge_chain::(db_path)?; + } else if let Some(sub_matches) = matches.subcommand_matches("purge-chain") { + purge_chain::(get_db_path_for_subcommand(matches, sub_matches)?)?; return Ok(Action::ExecutedInternally); } @@ -514,10 +579,18 @@ fn purge_chain( Ok(()) } -fn parse_address(default: &str, port_param: &str, matches: &clap::ArgMatches) -> Result { - let mut address: SocketAddr = default.parse().ok().ok_or_else(|| format!("Invalid address specified for --{}.", port_param))?; +fn parse_address( + default: &str, + port_param: &str, + matches: &clap::ArgMatches +) -> Result { + let mut address: SocketAddr = default.parse().ok().ok_or_else( + || format!("Invalid address specified for --{}.", port_param) + )?; if let Some(port) = matches.value_of(port_param) { - let port: u16 = port.parse().ok().ok_or_else(|| format!("Invalid port for --{} specified.", port_param))?; + let port: u16 = port.parse().ok().ok_or_else( + || format!("Invalid port for --{} specified.", port_param) + )?; address.set_port(port); } diff --git a/substrate/core/cli/src/params.rs b/substrate/core/cli/src/params.rs index d88e204691..c5293b821b 100644 --- a/substrate/core/cli/src/params.rs +++ b/substrate/core/cli/src/params.rs @@ -21,199 +21,223 @@ use structopt::StructOpt; #[derive(Debug, StructOpt)] #[structopt(name = "Substrate")] pub struct CoreParams { - ///Sets a custom logging filter - #[structopt(short = "l", long = "log", value_name = "LOG_PATTERN")] - log: Option, + ///Sets a custom logging filter + #[structopt(short = "l", long = "log", value_name = "LOG_PATTERN")] + log: Option, - /// Specify custom base path - #[structopt(long = "base-path", short = "d", value_name = "PATH", parse(from_os_str))] - base_path: Option, + /// Specify custom keystore path + #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] + keystore_path: Option, - /// Specify custom keystore path - #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] - keystore_path: Option, + /// Specify additional key seed + #[structopt(long = "key", value_name = "STRING")] + key: Option, - /// Specify additional key seed - #[structopt(long = "key", value_name = "STRING")] - key: Option, + /// Specify node secret key (64-character hex string) + #[structopt(long = "node-key", value_name = "KEY")] + node_key: Option, - /// Specify node secret key (64-character hex string) - #[structopt(long = "node-key", value_name = "KEY")] - node_key: Option, + /// Enable validator mode + #[structopt(long = "validator")] + validator: bool, - /// Enable validator mode - #[structopt(long = "validator")] - validator: bool, + /// Run in light client mode + #[structopt(long = "light")] + light: bool, - /// Run in light client mode - #[structopt(long = "light")] - light: bool, + /// Listen on this multiaddress + #[structopt(long = "listen-addr", value_name = "LISTEN_ADDR")] + listen_addr: Vec, - /// Run in development mode; implies --chain=dev --validator --key Alice - #[structopt(long = "dev")] - dev: bool, + /// Specify p2p protocol TCP port. Only used if --listen-addr is not specified. + #[structopt(long = "port", value_name = "PORT")] + port: Option, - /// Listen on this multiaddress - #[structopt(long = "listen-addr", value_name = "LISTEN_ADDR")] - listen_addr: Vec, + /// Listen to all RPC interfaces (default is local) + #[structopt(long = "rpc-external")] + rpc_external: bool, - /// Specify p2p protocol TCP port. Only used if --listen-addr is not specified. - #[structopt(long = "port", value_name = "PORT")] - port: Option, + /// Listen to all Websocket interfaces (default is local) + #[structopt(long = "ws-external")] + ws_external: bool, - /// Listen to all RPC interfaces (default is local) - #[structopt(long = "rpc-external")] - rpc_external: bool, + /// Specify HTTP RPC server TCP port + #[structopt(long = "rpc-port", value_name = "PORT")] + rpc_port: Option, - /// Listen to all Websocket interfaces (default is local) - #[structopt(long = "ws-external")] - ws_external: bool, + /// Specify WebSockets RPC server TCP port + #[structopt(long = "ws-port", value_name = "PORT")] + ws_port: Option, - /// Specify HTTP RPC server TCP port - #[structopt(long = "rpc-port", value_name = "PORT")] - rpc_port: Option, + /// Specify a list of bootnodes + #[structopt(long = "bootnodes", value_name = "URL")] + bootnodes: Vec, - /// Specify WebSockets RPC server TCP port - #[structopt(long = "ws-port", value_name = "PORT")] - ws_port: Option, + /// Specify a list of reserved node addresses + #[structopt(long = "reserved-nodes", value_name = "URL")] + reserved_nodes: Vec, - /// Specify a list of bootnodes - #[structopt(long = "bootnodes", value_name = "URL")] - bootnodes: Vec, + /// Specify the number of outgoing connections we're trying to maintain + #[structopt(long = "out-peers", value_name = "OUT_PEERS")] + out_peers: Option, - /// Specify a list of reserved node addresses - #[structopt(long = "reserved-nodes", value_name = "URL")] - reserved_nodes: Vec, + /// Specify the maximum number of incoming connections we're accepting + #[structopt(long = "in-peers", value_name = "IN_PEERS")] + in_peers: Option, - /// Specify the number of outgoing connections we're trying to maintain - #[structopt(long = "out-peers", value_name = "OUT_PEERS")] - out_peers: Option, + /// Specify the pruning mode, a number of blocks to keep or 'archive'. Default is 256. + #[structopt(long = "pruning", value_name = "PRUNING_MODE")] + pruning: Option, - /// Specify the maximum number of incoming connections we're accepting - #[structopt(long = "in-peers", value_name = "IN_PEERS")] - in_peers: Option, + /// The human-readable name for this node, as reported to the telemetry server, if enabled + #[structopt(long = "name", value_name = "NAME")] + name: Option, - /// Specify the chain specification (one of dev, local or staging) - #[structopt(long = "chain", value_name = "CHAIN_SPEC")] - chain: Option, + /// Should connect to the Substrate telemetry server (telemetry is off by default on local chains) + #[structopt(short = "t", long = "telemetry")] + telemetry: bool, - /// Specify the pruning mode, a number of blocks to keep or 'archive'. Default is 256. - #[structopt(long = "pruning", value_name = "PRUNING_MODE")] - pruning: Option, + /// Should not connect to the Substrate telemetry server (telemetry is on by default on global chains) + #[structopt(long = "no-telemetry")] + no_telemetry: bool, - /// The human-readable name for this node, as reported to the telemetry server, if enabled - #[structopt(long = "name", value_name = "NAME")] - name: Option, + /// The URL of the telemetry server. Implies --telemetry + #[structopt(long = "telemetry-url", value_name = "TELEMETRY_URL")] + telemetry_url: Option, - /// Should connect to the Substrate telemetry server (telemetry is off by default on local chains) - #[structopt(short = "t", long = "telemetry")] - telemetry: bool, + /// The means of execution used when calling into the runtime. Can be either wasm, native or both. + #[structopt(long = "execution", value_name = "STRATEGY")] + execution: Option, - /// Should not connect to the Substrate telemetry server (telemetry is on by default on global chains) - #[structopt(long = "no-telemetry")] - no_telemetry: bool, + #[allow(missing_docs)] + #[structopt(flatten)] + shared_flags: SharedFlags, - /// The URL of the telemetry server. Implies --telemetry - #[structopt(long = "telemetry-url", value_name = "TELEMETRY_URL")] - telemetry_url: Option, - - /// The means of execution used when calling into the runtime. Can be either wasm, native or both. - #[structopt(long = "execution", value_name = "STRATEGY")] - execution: Option, - - #[structopt(subcommand)] - cmds: Option, + #[structopt(subcommand)] + cmds: Option, } /// How to execute blocks #[derive(Debug, StructOpt)] pub enum ExecutionStrategy { - /// Execute native only - Native, - /// Execute wasm only - Wasm, - /// Execute natively when possible, wasm otherwise - Both, + /// Execute native only + Native, + /// Execute wasm only + Wasm, + /// Execute natively when possible, wasm otherwise + Both, } impl Default for ExecutionStrategy { - fn default() -> Self { - ExecutionStrategy::Both - } + fn default() -> Self { + ExecutionStrategy::Both + } } impl std::str::FromStr for ExecutionStrategy { - type Err = String; - fn from_str(input: &str) -> Result { - match input { - "native" => Ok(ExecutionStrategy::Native), - "wasm" | "webassembly" => Ok(ExecutionStrategy::Wasm), - "both" => Ok(ExecutionStrategy::Both), - _ => Err("Please specify either 'native', 'wasm' or 'both".to_owned()) + type Err = String; + fn from_str(input: &str) -> Result { + match input { + "native" => Ok(ExecutionStrategy::Native), + "wasm" | "webassembly" => Ok(ExecutionStrategy::Wasm), + "both" => Ok(ExecutionStrategy::Both), + _ => Err("Please specify either 'native', 'wasm' or 'both".to_owned()) - } - } + } + } +} + +/// Flags used by `CoreParams` and almost all `CoreCommands`. +#[derive(Debug, StructOpt)] +pub struct SharedFlags { + /// Specify the chain specification (one of dev, local or staging) + #[structopt(long = "chain", value_name = "CHAIN_SPEC")] + chain: Option, + + /// Specify the development chain + #[structopt(long = "dev")] + dev: bool, + + /// Specify custom base path. + #[structopt(long = "base-path", short = "d", value_name = "PATH")] + base_path: Option, } /// Subcommands provided by Default #[derive(Debug, StructOpt)] pub enum CoreCommands { - /// Build a spec.json file, outputing to stdout - #[structopt(name = "build-spec")] - BuildSpec { - /// Force raw genesis storage output. - #[structopt(long = "raw")] - raw: bool, - }, + /// Build a spec.json file, outputing to stdout + #[structopt(name = "build-spec")] + BuildSpec { + /// Force raw genesis storage output. + #[structopt(long = "raw")] + raw: bool, + }, - /// Export blocks to a file - #[structopt(name = "export-blocks")] - ExportBlocks { - /// Output file name or stdout if unspecified. - #[structopt(parse(from_os_str))] - output: Option, + /// Export blocks to a file + #[structopt(name = "export-blocks")] + ExportBlocks { + /// Output file name or stdout if unspecified. + #[structopt(parse(from_os_str))] + output: Option, - /// Specify starting block number. 1 by default. - #[structopt(long = "from", value_name = "BLOCK")] - from: Option, + /// Specify starting block number. 1 by default. + #[structopt(long = "from", value_name = "BLOCK")] + from: Option, - /// Specify last block number. Best block by default. - #[structopt(long = "to", value_name = "BLOCK")] - to: Option, + /// Specify last block number. Best block by default. + #[structopt(long = "to", value_name = "BLOCK")] + to: Option, - /// Use JSON output rather than binary. - #[structopt(long = "json")] - json: bool, - }, + /// Use JSON output rather than binary. + #[structopt(long = "json")] + json: bool, - /// Import blocks from file. - #[structopt(name = "import-blocks")] - ImportBlocks { - /// Input file or stdin if unspecified. - #[structopt(parse(from_os_str))] - input: Option, + #[allow(missing_docs)] + #[structopt(flatten)] + shared_flags: SharedFlags, + }, - /// The means of execution used when executing blocks. Can be either wasm, native or both. - #[structopt(long = "execution", value_name = "STRATEGY")] - execution: ExecutionStrategy, + /// Import blocks from file. + #[structopt(name = "import-blocks")] + ImportBlocks { + /// Input file or stdin if unspecified. + #[structopt(parse(from_os_str))] + input: Option, - /// The means of execution used when calling into the runtime. Can be either wasm, native or both. - #[structopt(long = "api-execution", value_name = "STRATEGY")] - api_execution: ExecutionStrategy, + /// The means of execution used when executing blocks. Can be either wasm, native or both. + #[structopt(long = "execution", value_name = "STRATEGY")] + execution: ExecutionStrategy, - /// The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing. - #[structopt(long = "max-heap-pages", value_name = "COUNT")] - max_heap_pages: Option, - }, + /// The means of execution used when calling into the runtime. Can be either wasm, native or both. + #[structopt(long = "api-execution", value_name = "STRATEGY")] + api_execution: ExecutionStrategy, - ///Revert chain to the previous state - #[structopt(name = "revert")] - Revert { - /// Number of blocks to revert. Default is 256. - num: Option, - }, + /// The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing. + #[structopt(long = "max-heap-pages", value_name = "COUNT")] + max_heap_pages: Option, - /// Remove the whole chain data. - #[structopt(name = "purge-chain")] - PurgeChain {}, + #[allow(missing_docs)] + #[structopt(flatten)] + shared_flags: SharedFlags, + }, + + ///Revert chain to the previous state + #[structopt(name = "revert")] + Revert { + /// Number of blocks to revert. Default is 256. + num: Option, + + #[allow(missing_docs)] + #[structopt(flatten)] + shared_flags: SharedFlags, + }, + + /// Remove the whole chain data. + #[structopt(name = "purge-chain")] + PurgeChain { + #[allow(missing_docs)] + #[structopt(flatten)] + shared_flags: SharedFlags, + }, }