diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 276855c887ff..cc2e87190e6d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -3,30 +3,18 @@ name = "polkadot-cli" version = "0.2.0" authors = ["Parity Technologies "] description = "Polkadot node implementation in Rust." -build = "build.rs" [dependencies] clap = { version = "~2.32", features = ["yaml"] } -backtrace = "0.3" -env_logger = "0.4" error-chain = "0.12" log = "0.3" -atty = "0.2" -regex = "1" -time = "0.1" slog = "^2" -ansi_term = "0.10" lazy_static = "1.0" -triehash = "0.1" -ed25519 = { path = "../../substrate/ed25519" } -app_dirs = "1.2" tokio = "0.1.7" futures = "0.1.17" -fdlimit = "0.1" parking_lot = "0.4" -serde_json = "1.0" -serde = "1.0" exit-future = "0.1" +substrate-cli = { path = "../../substrate/cli" } substrate-client = { path = "../../substrate/client" } substrate-codec = { path = "../../substrate/codec" } substrate-extrinsic-pool = { path = "../../substrate/extrinsic-pool" } @@ -42,7 +30,4 @@ polkadot-primitives = { path = "../primitives" } polkadot-runtime = { path = "../runtime" } polkadot-service = { path = "../service" } polkadot-transaction-pool = { path = "../transaction-pool" } -names = "0.11.0" -[build-dependencies] -clap = "~2.32" diff --git a/cli/README.adoc b/cli/README.adoc deleted file mode 100644 index 54fa5f3f32f7..000000000000 --- a/cli/README.adoc +++ /dev/null @@ -1,11 +0,0 @@ - -= Polkadot CLI - -== Summary - -[source, toml] ----- -include::Cargo.toml[lines=2..5] ----- - -include::doc/shell-completion.adoc[] diff --git a/cli/build.rs b/cli/build.rs deleted file mode 100644 index 9b878725eef7..000000000000 --- a/cli/build.rs +++ /dev/null @@ -1,43 +0,0 @@ -#[macro_use] -extern crate clap; - -use std::fs; -use std::env; -use clap::Shell; -use std::path::Path; - -fn main() { - build_shell_completion(); -} - -/// 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() { - let shells = [Shell::Bash, Shell::Fish, Shell::Zsh, Shell::Elvish, Shell::PowerShell]; - for shell in shells.iter() { - build_completion(shell); - } -} - -/// Build the shell auto-completion for a given Shell -fn build_completion(shell: &Shell) { - let yml = load_yaml!("src/cli.yml"); - - 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(); - - let mut app = clap::App::from_yaml(&yml); - app.gen_completions( - "polkadot", - *shell, - &path); -} diff --git a/cli/doc/shell-completion.adoc b/cli/doc/shell-completion.adoc deleted file mode 100644 index 2486bed09e79..000000000000 --- a/cli/doc/shell-completion.adoc +++ /dev/null @@ -1,41 +0,0 @@ - -== Shell completion - -The Polkadot cli command supports shell auto-completion. For this to work, you will need to run the completion script matching you build and system. - -Assuming you built a release version using `cargo build --release` and use `bash` run the following: - -[source, shell] -source target/release/completion-scripts/polkadot.bash - -You can find completion scripts for: -- bash -- fish -- zsh -- elvish -- powershell - -To make this change persistent, you can proceed as follow: - -.First install - -[source, shell] ----- -COMPL_DIR=$HOME/.completion -mkdir -p $COMPL_DIR -cp -f target/release/completion-scripts/polkadot.bash $COMPL_DIR/ -echo "source $COMPL_DIR/polkadot.bash" >> $HOME/.bash_profile -source $HOME/.bash_profile ----- - -.Update - -When you build a new version of Polkadot, the following will ensure you auto-completion script matches the current binary: - -[source, shell] ----- -COMPL_DIR=$HOME/.completion -mkdir -p $COMPL_DIR -cp -f target/release/completion-scripts/polkadot.bash $COMPL_DIR/ -source $HOME/.bash_profile ----- diff --git a/cli/src/chain_spec.rs b/cli/src/chain_spec.rs index 0224e24bdc75..7298a4cdff98 100644 --- a/cli/src/chain_spec.rs +++ b/cli/src/chain_spec.rs @@ -17,7 +17,6 @@ //! Predefined chains. use service; -use std::path::PathBuf; /// The chain specification (this should eventually be replaced by a more general JSON-based chain /// specification). @@ -31,8 +30,6 @@ pub enum ChainSpec { KrummeLanke, /// Whatever the current runtime is with the "global testnet" defaults. StagingTestnet, - /// Custom Genesis file. - Custom(String), } /// Get a chain config from a spec setting. @@ -43,32 +40,18 @@ impl ChainSpec { ChainSpec::Development => service::chain_spec::development_config(), ChainSpec::LocalTestnet => service::chain_spec::local_testnet_config(), ChainSpec::StagingTestnet => service::chain_spec::staging_testnet_config(), - ChainSpec::Custom(f) => service::ChainSpec::from_json_file(PathBuf::from(f))?, }) } -} -impl<'a> From<&'a str> for ChainSpec { - fn from(s: &'a str) -> Self { + pub(crate) fn from(s: &str) -> Option { match s { - "dev" => ChainSpec::Development, - "local" => ChainSpec::LocalTestnet, - "poc-1" => ChainSpec::KrummeLanke, - "krummelanke" => ChainSpec::KrummeLanke, - "staging" => ChainSpec::StagingTestnet, - s => ChainSpec::Custom(s.into()), + "dev" => Some(ChainSpec::Development), + "local" => Some(ChainSpec::LocalTestnet), + "poc-1" => Some(ChainSpec::KrummeLanke), + "" | "krummelanke" => Some(ChainSpec::KrummeLanke), + "staging" => Some(ChainSpec::StagingTestnet), + _ => None, } } } -impl From for String { - fn from(s: ChainSpec) -> String { - match s { - ChainSpec::Development => "dev".into(), - ChainSpec::LocalTestnet => "local".into(), - ChainSpec::KrummeLanke => "krummelanke".into(), - ChainSpec::StagingTestnet => "staging".into(), - ChainSpec::Custom(f) => format!("custom ({})", f), - } - } -} diff --git a/cli/src/cli.yml b/cli/src/cli.yml deleted file mode 100644 index d8e279797033..000000000000 --- a/cli/src/cli.yml +++ /dev/null @@ -1,197 +0,0 @@ -name: polkadot -author: "Parity Team " -about: Polkadot Node Rust Implementation -args: - - log: - short: l - long: log - value_name: LOG_PATTERN - help: Sets a custom logging filter - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path - takes_value: true - - keystore-path: - long: keystore-path - value_name: PATH - help: Specify custom keystore path - takes_value: true - - key: - long: key - value_name: STRING - help: Specify additional key seed - takes_value: true - - node-key: - long: node-key - value_name: KEY - help: Specify node secret key (64-character hex string) - takes_value: true - - validator: - long: validator - help: Enable validator mode - takes_value: false - - light: - long: light - help: Run in light client mode - takes_value: false - - dev: - long: dev - help: Run in development mode; implies --chain=dev --validator --key Alice - takes_value: false - - port: - long: port - value_name: PORT - help: Specify p2p protocol TCP port - takes_value: true - - rpc-port: - long: rpc-port - value_name: PORT - help: Specify HTTP RPC server TCP port - takes_value: true - - ws-port: - long: ws-port - value_name: PORT - help: Specify WebSockets RPC server TCP port - takes_value: true - - bootnodes: - long: bootnodes - value_name: URL - help: Specify a list of bootnodes - takes_value: true - multiple: true - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification (one of krummelanke, dev, local or staging) - takes_value: true - - pruning: - long: pruning - value_name: PRUNING_MODE - help: Specify the pruning mode, a number of blocks to keep or "archive". Default is 256. - takes_value: true - - name: - long: name - value_name: NAME - help: The human-readable name for this node, as reported to the telemetry server, if enabled - takes_value: true - - telemetry: - short: t - long: telemetry - help: Should connect to the Polkadot telemetry server (telemetry is off by default on local chains) - takes_value: false - - no-telemetry: - long: no-telemetry - help: Should not connect to the Polkadot telemetry server (telemetry is on by default on global chains) - takes_value: false - - telemetry-url: - long: telemetry-url - value_name: TELEMETRY_URL - help: The URL of the telemetry server. Implies --telemetry - takes_value: true - - execution: - long: execution - value_name: STRATEGY - help: The means of execution used when calling into the runtime. Can be either wasm, native or both. - - min-heap-pages: - long: min-heap-pages - value_name: COUNT - help: The number of 64KB pages to allocate for Wasm execution initially. - - max-heap-pages: - long: max-heap-pages - value_name: COUNT - help: The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing. -subcommands: - - build-spec: - about: Build a spec.json file, outputing to stdout - args: - - raw: - long: raw - help: Force raw genesis storage output. - takes_value: false - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification (one of krummelanke, dev, local or staging) - takes_value: true - - export-blocks: - about: Export blocks to a file - args: - - OUTPUT: - index: 1 - help: Output file name or stdout if unspecified. - required: false - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true - - from: - long: from - value_name: BLOCK - help: Specify starting block number. 1 by default. - takes_value: true - - to: - long: to - value_name: BLOCK - help: Specify last block number. Best block by default. - takes_value: true - - json: - long: json - help: Use JSON output rather than binary. - takes_value: false - - import-blocks: - about: Import blocks from file. - args: - - INPUT: - index: 1 - help: Input file or stdin if unspecified. - required: false - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true - - execution: - long: execution - value_name: STRATEGY - help: The means of execution used when calling into the runtime. Can be either wasm, native or both. - - min-heap-pages: - long: min-heap-pages - value_name: COUNT - help: The number of 64KB pages to allocate for Wasm execution initially. - - max-heap-pages: - long: max-heap-pages - value_name: COUNT - help: The maximum number of 64KB pages to ever allocate for Wasm execution. Don't alter this unless you know what you're doing. - - revert: - about: Revert chain to the previous state - args: - - NUM: - index: 1 - help: Number of blocks to revert. Default is 256. - - chain: - long: chain - value_name: CHAIN_SPEC - help: Specify the chain specification. - takes_value: true - - base-path: - long: base-path - short: d - value_name: PATH - help: Specify custom base path. - takes_value: true diff --git a/cli/src/error.rs b/cli/src/error.rs deleted file mode 100644 index d7c690276ca8..000000000000 --- a/cli/src/error.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Initialization errors. - -use client; - -error_chain! { - foreign_links { - Io(::std::io::Error) #[doc="IO error"]; - Cli(::clap::Error) #[doc="CLI error"]; - Service(::service::Error) #[doc="Polkadot service error"]; - } - links { - Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"]; - } - errors { - /// Input error. - Input(m: String) { - description("Invalid input"), - display("{}", m), - } - } -} diff --git a/cli/src/informant.rs b/cli/src/informant.rs deleted file mode 100644 index a624bce6263f..000000000000 --- a/cli/src/informant.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Console informant. Prints sync progress and block events. Runs on the calling thread. - -use std::time::{Duration, Instant}; -use futures::{Future, Stream}; -use service::{Service, Components}; -use tokio::runtime::TaskExecutor; -use tokio::timer::Interval; -use network::{SyncState, SyncProvider}; -use client::BlockchainEvents; -use runtime_primitives::traits::{Header, As}; -use substrate_extrinsic_pool::api::ExtrinsicPool; - -const TIMER_INTERVAL_MS: u64 = 5000; - -/// Spawn informant on the event loop -pub fn start(service: &Service, exit: ::exit_future::Exit, handle: TaskExecutor) - where - C: Components, -{ - let interval = Interval::new(Instant::now(), Duration::from_millis(TIMER_INTERVAL_MS)); - - let network = service.network(); - let client = service.client(); - let txpool = service.extrinsic_pool(); - - let display_notifications = interval.map_err(|e| debug!("Timer error: {:?}", e)).for_each(move |_| { - let sync_status = network.status(); - - if let Ok(best_block) = client.best_block_header() { - let hash = best_block.hash(); - let num_peers = sync_status.num_peers; - let status = match (sync_status.sync.state, sync_status.sync.best_seen_block) { - (SyncState::Idle, _) => "Idle".into(), - (SyncState::Downloading, None) => "Syncing".into(), - (SyncState::Downloading, Some(n)) => format!("Syncing, target=#{}", n), - }; - let txpool_status = txpool.light_status(); - let best_number: u64 = best_block.number().as_(); - info!(target: "polkadot", "{} ({} peers), best: #{} ({})", status, sync_status.num_peers, best_number, hash); - telemetry!("system.interval"; "status" => status, "peers" => num_peers, "height" => best_number, "best" => ?hash, "txcount" => txpool_status.transaction_count); - } else { - warn!("Error getting best block information"); - } - Ok(()) - }); - - let client = service.client(); - let display_block_import = client.import_notification_stream().for_each(|n| { - info!(target: "polkadot", "Imported #{} ({})", n.header.number, n.hash); - Ok(()) - }); - - let txpool = service.extrinsic_pool(); - let display_txpool_import = txpool.import_notification_stream().for_each(move |_| { - let status = txpool.light_status(); - telemetry!("txpool.import"; "mem_usage" => status.mem_usage, "count" => status.transaction_count, "sender" => status.senders); - Ok(()) - }); - - let informant_work = display_notifications.join3(display_block_import, display_txpool_import); - handle.spawn(exit.until(informant_work).map(|_| ())); -} - diff --git a/cli/src/lib.rs b/cli/src/lib.rs index de8872472f44..ba29a39eccec 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -17,119 +17,34 @@ //! Polkadot CLI library. #![warn(missing_docs)] +#![warn(unused_extern_crates)] -extern crate app_dirs; -extern crate env_logger; -extern crate atty; -extern crate ansi_term; -extern crate regex; -extern crate time; -extern crate fdlimit; extern crate futures; extern crate tokio; -extern crate ed25519; -extern crate triehash; -extern crate parking_lot; -extern crate serde; -extern crate serde_json; -extern crate names; -extern crate backtrace; -extern crate substrate_client as client; -extern crate substrate_network as network; -extern crate substrate_codec as codec; -extern crate substrate_primitives; -extern crate substrate_rpc; -extern crate substrate_rpc_servers as rpc; -extern crate substrate_runtime_primitives as runtime_primitives; -extern crate substrate_state_machine as state_machine; -extern crate substrate_extrinsic_pool; -extern crate substrate_service; -extern crate polkadot_primitives; -extern crate polkadot_runtime; +extern crate substrate_cli as cli; extern crate polkadot_service as service; -#[macro_use] -extern crate slog; // needed until we can reexport `slog_info` from `substrate_telemetry` -#[macro_use] -extern crate substrate_telemetry; -extern crate polkadot_transaction_pool as txpool; extern crate exit_future; -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate clap; -#[macro_use] -extern crate error_chain; #[macro_use] extern crate log; -pub mod error; -mod informant; mod chain_spec; -mod panic_hook; -pub use chain_spec::ChainSpec; -pub use client::error::Error as ClientError; -pub use client::backend::Backend as ClientBackend; -pub use state_machine::Backend as StateMachineBackend; -pub use polkadot_primitives::Block as PolkadotBlock; -pub use service::{Components as ServiceComponents, Service, CustomConfiguration}; +pub use cli::error; -use std::io::{self, Write, Read, stdin, stdout}; -use std::fs::File; -use std::net::SocketAddr; -use std::path::{Path, PathBuf}; -use substrate_telemetry::{init_telemetry, TelemetryConfig}; -use polkadot_primitives::BlockId; -use codec::{Decode, Encode}; -use client::BlockOrigin; -use runtime_primitives::generic::SignedBlock; -use names::{Generator, Name}; -use regex::Regex; +use chain_spec::ChainSpec; use futures::Future; use tokio::runtime::Runtime; -use service::PruningMode; - -const DEFAULT_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; - -#[derive(Clone)] -struct SystemConfiguration { - chain_name: String, -} - -impl substrate_rpc::system::SystemApi for SystemConfiguration { - fn system_name(&self) -> substrate_rpc::system::error::Result { - Ok("parity-polkadot".into()) - } - - fn system_version(&self) -> substrate_rpc::system::error::Result { - Ok(crate_version!().into()) - } - - fn system_chain(&self) -> substrate_rpc::system::error::Result { - Ok(self.chain_name.clone()) - } -} - -fn load_spec(matches: &clap::ArgMatches) -> Result<(service::ChainSpec, bool), String> { - let chain_spec = matches.value_of("chain") - .map(ChainSpec::from) - .unwrap_or_else(|| if matches.is_present("dev") { ChainSpec::Development } else { ChainSpec::KrummeLanke }); - let is_global = match chain_spec { - ChainSpec::KrummeLanke => true, - _ => false, - }; - let spec = chain_spec.load()?; - info!("Chain specification: {}", spec.name()); - Ok((spec, is_global)) -} +pub use service::{Components as ServiceComponents, Service, CustomConfiguration}; +pub use cli::VersionInfo; -fn base_path(matches: &clap::ArgMatches) -> PathBuf { - matches.value_of("base-path") - .map(|x| Path::new(x).to_owned()) - .unwrap_or_else(default_base_path) +fn load_spec(id: &str) -> Result, String> { + Ok(match ChainSpec::from(id) { + Some(spec) => Some(spec.load()?), + None => None, + }) } /// Additional worker making use of the node, to run asynchronously before shutdown. @@ -147,39 +62,16 @@ pub trait Worker { /// Return configuration for the polkadot node. // TODO: make this the full configuration, so embedded nodes don't need // string CLI args - fn configuration(&self) -> CustomConfiguration { Default::default() } + fn configuration(&self) -> service::CustomConfiguration { Default::default() } /// Don't work, but schedule an exit. - fn exit_only(self) -> Self::Exit; + fn exit_only(&self) -> Self::Exit; /// Do work and schedule exit. - fn work(self, service: &Service) -> Self::Work; + fn work(self, service: &service::Service) -> Self::Work; } -/// 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; - let name = _name.to_string(); - if name.chars().count() >= MAX_NODE_NAME_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(()) -} - -/// Parse command line arguments and start the node. +/// Parse command line arguments into service configuration. /// /// IANA unassigned port ranges that we could use: /// 6717-6766 Unassigned @@ -187,342 +79,37 @@ fn is_node_name_valid(_name: &str) -> Result<(), &str> { /// 9556-9591 Unassigned /// 9803-9874 Unassigned /// 9926-9949 Unassigned -pub fn run(args: I, worker: W) -> error::Result<()> where +pub fn run(args: I, worker: W, version: cli::VersionInfo) -> error::Result<()> where I: IntoIterator, T: Into + Clone, W: Worker, { - panic_hook::set(); - - let yaml = load_yaml!("./cli.yml"); - let matches = match clap::App::from_yaml(yaml) - .version(&(crate_version!().to_owned() + "\n")[..]) - .get_matches_from_safe(args) { - Ok(m) => m, - Err(e) => e.exit(), - }; - - // TODO [ToDr] Split parameters parsing from actual execution. - let log_pattern = matches.value_of("log").unwrap_or(""); - init_logger(log_pattern); - fdlimit::raise_fd_limit(); - - info!("Parity ·:· Polkadot"); - info!(" version {}", crate_version!()); - info!(" by Parity Technologies, 2017, 2018"); - - if let Some(matches) = matches.subcommand_matches("build-spec") { - return build_spec(matches); - } - - if let Some(matches) = matches.subcommand_matches("export-blocks") { - return export_blocks(matches, worker.exit_only()); - } - - if let Some(matches) = matches.subcommand_matches("import-blocks") { - return import_blocks(matches, worker.exit_only()); - } - - if let Some(matches) = matches.subcommand_matches("revert") { - return revert_chain(matches); - } - - let (spec, is_global) = load_spec(&matches)?; - let mut config = service::Configuration::default_with_spec(spec); - - config.name = match matches.value_of("name") { - None => Generator::with_naming(Name::Numbered).next().unwrap(), - Some(name) => name.into(), - }; - match is_node_name_valid(&config.name) { - Ok(_) => info!("Node name: {}", config.name), - Err(msg) => return Err(error::ErrorKind::Input( - format!("Invalid node name '{}'. Reason: {}. If unsure, use none.", config.name, msg)).into()) - } - - let base_path = base_path(&matches); - - config.keystore_path = matches.value_of("keystore") - .map(|x| Path::new(x).to_owned()) - .unwrap_or_else(|| keystore_path(&base_path, config.chain_spec.id())) - .to_string_lossy() - .into(); - - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - - config.pruning = match matches.value_of("pruning") { - 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()))?), - }; - - let role = - if matches.is_present("light") { - info!("Starting (light)"); - config.execution_strategy = service::ExecutionStrategy::NativeWhenPossible; - service::Roles::LIGHT - } else if matches.is_present("validator") || matches.is_present("dev") { - info!("Starting validator"); - config.execution_strategy = service::ExecutionStrategy::Both; - service::Roles::AUTHORITY - } else { - info!("Starting (heavy)"); - config.execution_strategy = service::ExecutionStrategy::NativeWhenPossible; - service::Roles::FULL - }; - - if let Some(v) = matches.value_of("min-heap-pages") { - config.min_heap_pages = v.parse().map_err(|_| "Invalid --min-heap-pages argument")?; - } - if let Some(v) = matches.value_of("max-heap-pages") { - config.max_heap_pages = v.parse().map_err(|_| "Invalid --max-heap-pages argument")?; - } - - if let Some(s) = matches.value_of("execution") { - config.execution_strategy = match s { - "both" => service::ExecutionStrategy::Both, - "native" => service::ExecutionStrategy::NativeWhenPossible, - "wasm" => service::ExecutionStrategy::AlwaysWasm, - _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), - }; - } - - config.roles = role; - { - config.network.boot_nodes.extend(matches - .values_of("bootnodes") - .map_or(Default::default(), |v| v.map(|n| n.to_owned()).collect::>())); - config.network.config_path = Some(network_path(&base_path, config.chain_spec.id()).to_string_lossy().into()); - config.network.net_config_path = config.network.config_path.clone(); - - let port = match matches.value_of("port") { - Some(port) => port.parse().map_err(|_| "Invalid p2p port value specified.")?, - None => 30333, - }; - - config.network.listen_address = Some(SocketAddr::new("0.0.0.0".parse().unwrap(), port)); - config.network.public_address = None; - config.network.client_version = format!("parity-polkadot/{}", crate_version!()); - config.network.use_secret = match matches.value_of("node-key").map(|s| s.parse()) { - Some(Ok(secret)) => Some(secret), - Some(Err(err)) => return Err(format!("Error parsing node key: {}", err).into()), - None => None, - }; - } - - config.custom = worker.configuration(); - - config.keys = matches.values_of("key").unwrap_or_default().map(str::to_owned).collect(); - if matches.is_present("dev") { - config.keys.push("Alice".into()); - } - - let sys_conf = SystemConfiguration { - chain_name: config.chain_spec.name().to_owned(), - }; - - let mut runtime = Runtime::new()?; - let executor = runtime.executor(); - - let telemetry_enabled = - matches.is_present("telemetry") - || matches.value_of("telemetry-url").is_some() - || (is_global && !matches.is_present("no-telemetry")); - let _guard = if telemetry_enabled { - let name = config.name.clone(); - let chain_name = config.chain_spec.name().to_owned(); - Some(init_telemetry(TelemetryConfig { - url: matches.value_of("telemetry-url").unwrap_or(DEFAULT_TELEMETRY_URL).into(), - on_connect: Box::new(move || { - telemetry!("system.connected"; - "name" => name.clone(), - "implementation" => "parity-polkadot", - "version" => crate_version!(), - "config" => "", - "chain" => chain_name.clone(), - ); - }), - })) - } else { - None - }; - - match role == service::Roles::LIGHT { - true => run_until_exit(&mut runtime, service::new_light(config, executor)?, &matches, sys_conf, worker)?, - false => run_until_exit(&mut runtime, service::new_full(config, executor)?, &matches, sys_conf, worker)?, - } - - // TODO: hard exit if this stalls? - runtime.shutdown_on_idle().wait().expect("failed to shut down event loop"); - Ok(()) -} - -fn build_spec(matches: &clap::ArgMatches) -> error::Result<()> { - let (spec, _) = load_spec(&matches)?; - info!("Building chain spec"); - let json = spec.to_json(matches.is_present("raw"))?; - print!("{}", json); - Ok(()) -} - -fn export_blocks(matches: &clap::ArgMatches, exit: E) -> error::Result<()> - where E: Future + Send + 'static -{ - let base_path = base_path(matches); - let (spec, _) = load_spec(&matches)?; - let mut config = service::Configuration::default_with_spec(spec); - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - info!("DB path: {}", config.database_path); - let client = service::new_client(config)?; - let (exit_send, exit_recv) = std::sync::mpsc::channel(); - ::std::thread::spawn(move || { - let _ = exit.wait(); - let _ = exit_send.send(()); - }); - let mut from_block: u32 = match matches.value_of("from") { - Some(v) => v.parse().map_err(|_| "Invalid --from argument")?, - None => 1, - }; - - if from_block < 1 { - from_block = 1; - } - let to_block = match matches.value_of("to") { - Some(v) => v.parse().map_err(|_| "Invalid --to argument")?, - None => client.info()?.chain.best_number as u32, - }; - info!("Exporting blocks from #{} to #{}", from_block, to_block); - - if to_block < from_block { - return Err("Invalid block range specified".into()); - } - - let json = matches.is_present("json"); - - let mut file: Box = match matches.value_of("OUTPUT") { - Some(filename) => Box::new(File::create(filename)?), - None => Box::new(stdout()), - }; - - if !json { - file.write(&(to_block - from_block + 1).encode())?; - } - - loop { - if exit_recv.try_recv().is_ok() { - break; - } - match client.block(&BlockId::number(from_block as u64))? { - Some(from_block) => { - if json { - serde_json::to_writer(&mut *file, &from_block).map_err(|e| format!("Eror writing JSON: {}", e))?; - } else { - file.write(&from_block.encode())?; - } - }, - None => break, - } - if from_block % 10000 == 0 { - info!("#{}", from_block); - } - if from_block == to_block { - break; - } - from_block += 1; - } - Ok(()) -} - -fn import_blocks(matches: &clap::ArgMatches, exit: E) -> error::Result<()> - where E: Future + Send + 'static -{ - let (spec, _) = load_spec(&matches)?; - let base_path = base_path(matches); - let mut config = service::Configuration::default_with_spec(spec); - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - - if let Some(v) = matches.value_of("min-heap-pages") { - config.min_heap_pages = v.parse().map_err(|_| "Invalid --min-heap-pages argument")?; - } - if let Some(v) = matches.value_of("max-heap-pages") { - config.max_heap_pages = v.parse().map_err(|_| "Invalid --max-heap-pages argument")?; - } - - if let Some(s) = matches.value_of("execution") { - config.execution_strategy = match s { - "both" => service::ExecutionStrategy::Both, - "native" => service::ExecutionStrategy::NativeWhenPossible, - "wasm" => service::ExecutionStrategy::AlwaysWasm, - _ => return Err(error::ErrorKind::Input("Invalid execution mode specified".to_owned()).into()), - }; - } - - let client = service::new_client(config)?; - let (exit_send, exit_recv) = std::sync::mpsc::channel(); - - ::std::thread::spawn(move || { - let _ = exit.wait(); - let _ = exit_send.send(()); - }); - - let mut file: Box = match matches.value_of("INPUT") { - Some(filename) => Box::new(File::open(filename)?), - None => Box::new(stdin()), - }; - - let count: u32 = Decode::decode(&mut file).ok_or("Error reading file")?; - info!("Importing {} blocks", count); - let mut block = 0; - for _ in 0 .. count { - if exit_recv.try_recv().is_ok() { - break; - } - match SignedBlock::decode(&mut file) { - Some(block) => { - let header = client.check_justification(block.block.header, block.justification.into())?; - client.import_block(BlockOrigin::File, header, Some(block.block.extrinsics))?; - }, - None => { - warn!("Error reading block data."); - break; + match cli::prepare_execution::(args, worker.exit_only(), version, load_spec, "parity-polkadot")? { + cli::Action::ExecutedInternally => (), + cli::Action::RunService(mut config) => { + info!("Parity ·:· Polkadot"); + info!(" version {}", config.full_version()); + info!(" by Parity Technologies, 2017, 2018"); + info!("Chain specification: {}", config.chain_spec.name()); + info!("Node name: {}", config.name); + info!("Roles: {:?}", config.roles); + config.custom = worker.configuration(); + let mut runtime = Runtime::new()?; + let executor = runtime.executor(); + match config.roles == service::Roles::LIGHT { + true => run_until_exit(&mut runtime, service::new_light(config, executor)?, worker)?, + false => run_until_exit(&mut runtime, service::new_full(config, executor)?, worker)?, } - } - block += 1; - if block % 1000 == 0 { - info!("#{}", block); + // TODO: hard exit if this stalls? + runtime.shutdown_on_idle().wait().expect("failed to shut down event loop"); } } - info!("Imported {} blocks. Best: #{}", block, client.info()?.chain.best_number); - Ok(()) } - -fn revert_chain(matches: &clap::ArgMatches) -> error::Result<()> { - let (spec, _) = load_spec(&matches)?; - let base_path = base_path(matches); - let mut config = service::Configuration::default_with_spec(spec); - config.database_path = db_path(&base_path, config.chain_spec.id()).to_string_lossy().into(); - - let client = service::new_client(config)?; - - let blocks = match matches.value_of("NUM") { - Some(v) => v.parse().map_err(|_| "Invalid block count specified")?, - None => 256, - }; - - let reverted = client.revert(blocks)?; - let info = client.info()?.chain; - info!("Reverted {} blocks. Best: #{} ({})", reverted, info.best_number, info.best_hash); - Ok(()) -} - fn run_until_exit( runtime: &mut Runtime, service: service::Service, - matches: &clap::ArgMatches, - sys_conf: SystemConfiguration, worker: W, ) -> error::Result<()> where @@ -532,163 +119,9 @@ fn run_until_exit( let (exit_send, exit) = exit_future::signal(); let executor = runtime.executor(); - informant::start(&service, exit.clone(), executor.clone()); - - let _rpc_servers = { - let http_address = parse_address("127.0.0.1:9933", "rpc-port", matches)?; - let ws_address = parse_address("127.0.0.1:9944", "ws-port", matches)?; - - let handler = || { - let client = substrate_service::Service::client(&service); - let chain = rpc::apis::chain::Chain::new(client.clone(), executor.clone()); - let author = rpc::apis::author::Author::new(client.clone(), service.extrinsic_pool(), executor.clone()); - rpc::rpc_handler::, _, _, _, _>( - client, - chain, - author, - sys_conf.clone(), - ) - }; - ( - start_server(http_address, |address| rpc::start_http(address, handler())), - start_server(ws_address, |address| rpc::start_ws(address, handler())), - ) - }; + cli::informant::start(&service, exit.clone(), executor.clone()); let _ = runtime.block_on(worker.work(&service)); exit_send.fire(); Ok(()) } - -fn start_server(mut address: SocketAddr, start: F) -> Result where - F: Fn(&SocketAddr) -> Result, -{ - start(&address) - .or_else(|e| match e.kind() { - io::ErrorKind::AddrInUse | - io::ErrorKind::PermissionDenied => { - warn!("Unable to bind server to {}. Trying random port.", address); - address.set_port(0); - start(&address) - }, - _ => Err(e), - }) -} - -fn parse_address(default: &str, port_param: &str, matches: &clap::ArgMatches) -> Result { - let mut address: SocketAddr = default.parse().ok().ok_or(format!("Invalid address specified for --{}.", port_param))?; - if let Some(port) = matches.value_of(port_param) { - let port: u16 = port.parse().ok().ok_or(format!("Invalid port for --{} specified.", port_param))?; - address.set_port(port); - } - - Ok(address) -} - -fn keystore_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("keystore"); - path -} - -fn db_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("db"); - path -} - -fn network_path(base_path: &Path, chain_id: &str) -> PathBuf { - let mut path = base_path.to_owned(); - path.push("chains"); - path.push(chain_id); - path.push("network"); - path -} - -fn default_base_path() -> PathBuf { - use app_dirs::{AppInfo, AppDataType}; - - let app_info = AppInfo { - name: "Polkadot", - author: "Parity Technologies", - }; - - app_dirs::get_app_root( - AppDataType::UserData, - &app_info, - ).expect("app directories exist on all supported platforms; qed") -} - -fn init_logger(pattern: &str) { - use ansi_term::Colour; - - let mut builder = env_logger::LogBuilder::new(); - // Disable info logging by default for some modules: - builder.filter(Some("ws"), log::LogLevelFilter::Warn); - builder.filter(Some("hyper"), log::LogLevelFilter::Warn); - // Enable info for others. - builder.filter(None, log::LogLevelFilter::Info); - - if let Ok(lvl) = std::env::var("RUST_LOG") { - builder.parse(&lvl); - } - - builder.parse(pattern); - let isatty = atty::is(atty::Stream::Stderr); - let enable_color = isatty; - - let format = move |record: &log::LogRecord| { - let timestamp = time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).expect("Error formatting log timestamp"); - - let mut output = if log::max_log_level() <= log::LogLevelFilter::Info { - format!("{} {}", Colour::Black.bold().paint(timestamp), record.args()) - } else { - let name = ::std::thread::current().name().map_or_else(Default::default, |x| format!("{}", Colour::Blue.bold().paint(x))); - format!("{} {} {} {} {}", Colour::Black.bold().paint(timestamp), name, record.level(), record.target(), record.args()) - }; - - if !enable_color { - output = kill_color(output.as_ref()); - } - - if !isatty && record.level() <= log::LogLevel::Info && atty::is(atty::Stream::Stdout) { - // duplicate INFO/WARN output to console - println!("{}", output); - } - output - }; - builder.format(format); - - builder.init().expect("Logger initialized only once."); -} - -fn kill_color(s: &str) -> String { - lazy_static! { - static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").expect("Error initializing color regex"); - } - RE.replace_all(s, "").to_string() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[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()); - } -} diff --git a/cli/src/panic_hook.rs b/cli/src/panic_hook.rs deleted file mode 100644 index fa48ce08cb88..000000000000 --- a/cli/src/panic_hook.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2017 Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Custom panic hook with bug report link - -use backtrace::Backtrace; -use std::io::{self, Write}; -use std::panic::{self, PanicInfo}; -use std::thread; - -/// Set the panic hook -pub fn set() { - panic::set_hook(Box::new(panic_hook)); -} - -static ABOUT_PANIC: &str = " -This is a bug. Please report it at: - - https://github.com/paritytech/polkadot/issues/new -"; - -fn panic_hook(info: &PanicInfo) { - let location = info.location(); - let file = location.as_ref().map(|l| l.file()).unwrap_or(""); - let line = location.as_ref().map(|l| l.line()).unwrap_or(0); - - let msg = match info.payload().downcast_ref::<&'static str>() { - Some(s) => *s, - None => match info.payload().downcast_ref::() { - Some(s) => &s[..], - None => "Box", - } - }; - - let thread = thread::current(); - let name = thread.name().unwrap_or(""); - - let backtrace = Backtrace::new(); - - let mut stderr = io::stderr(); - - let _ = writeln!(stderr, ""); - let _ = writeln!(stderr, "===================="); - let _ = writeln!(stderr, ""); - let _ = writeln!(stderr, "{:?}", backtrace); - let _ = writeln!(stderr, ""); - let _ = writeln!( - stderr, - "Thread '{}' panicked at '{}', {}:{}", - name, msg, file, line - ); - - let _ = writeln!(stderr, "{}", ABOUT_PANIC); -} - diff --git a/collator/src/lib.rs b/collator/src/lib.rs index 7e4b36cc48e3..0acee408b68a 100644 --- a/collator/src/lib.rs +++ b/collator/src/lib.rs @@ -68,7 +68,7 @@ use client::BlockchainEvents; use polkadot_api::PolkadotApi; use polkadot_primitives::{AccountId, BlockId, SessionKey}; use polkadot_primitives::parachain::{self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId}; -use polkadot_cli::{ServiceComponents, Service, CustomConfiguration}; +use polkadot_cli::{ServiceComponents, Service, CustomConfiguration, VersionInfo}; use polkadot_cli::Worker; use tokio::timer::Deadline; @@ -213,7 +213,7 @@ struct CollationNode { impl Worker for CollationNode where P: ParachainContext + Send + 'static, - E: Future + Send + 'static + E: Future + Send + Clone + 'static { type Work = Box + Send>; type Exit = E; @@ -227,8 +227,8 @@ impl Worker for CollationNode where config } - fn exit_only(self) -> Self::Exit { - self.exit + fn exit_only(&self) -> Self::Exit { + self.exit.clone() } fn work(self, service: &Service) -> Self::Work { @@ -323,14 +323,15 @@ pub fn run_collator( para_id: ParaId, exit: E, key: Arc, - args: Vec<::std::ffi::OsString> + args: Vec<::std::ffi::OsString>, + version: VersionInfo, ) -> polkadot_cli::error::Result<()> where P: ParachainContext + Send + 'static, E: IntoFuture, - E::Future: Send + 'static, + E::Future: Send + Clone + 'static, { let node_logic = CollationNode { parachain_context, exit: exit.into_future(), para_id, key }; - polkadot_cli::run(args, node_logic) + polkadot_cli::run(args, node_logic, version) } #[cfg(test)] diff --git a/service/res/krummelanke.json b/service/res/krummelanke.json index 756106e78bff..510c1e467c8c 100644 --- a/service/res/krummelanke.json +++ b/service/res/krummelanke.json @@ -46,5 +46,6 @@ "/ip4/104.211.48.247/tcp/30333/p2p/QmYPx99i3H8EKXrvYHTBwqz3jjFC1kBfkvmSKd2h9zwQFr", "/ip4/40.114.120.164/tcp/30333/p2p/QmWzYU5X1NpFrprD1YZF5Lcj9aE5WF4QEg5FpvQx5XGWG7", "/ip4/40.117.153.33/tcp/30333/p2p/QmSz8qCADMmi92QB8dTqMPu56JYQQKZBAHz7y8KXjvqcvW" -] +], +"telemetryUrl": "wss://telemetry.polkadot.io/submit/" } diff --git a/service/src/chain_spec.rs b/service/src/chain_spec.rs index 37b7bae35e03..0c734f5712f3 100644 --- a/service/src/chain_spec.rs +++ b/service/src/chain_spec.rs @@ -22,6 +22,8 @@ use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyC SessionConfig, StakingConfig, TimestampConfig}; use service::ChainSpec; +const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; + pub fn poc_1_testnet_config() -> Result, String> { ChainSpec::from_embedded(include_bytes!("../res/krummelanke.json")) } @@ -94,7 +96,13 @@ fn staging_testnet_config_genesis() -> GenesisConfig { /// Staging testnet config. pub fn staging_testnet_config() -> ChainSpec { let boot_nodes = vec![]; - ChainSpec::from_genesis("Staging Testnet", "staging_testnet", staging_testnet_config_genesis, boot_nodes) + ChainSpec::from_genesis( + "Staging Testnet", + "staging_testnet", + staging_testnet_config_genesis, + boot_nodes, + Some(STAGING_TELEMETRY_URL.into()), + ) } fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { @@ -169,7 +177,7 @@ fn development_config_genesis() -> GenesisConfig { /// Development config (single validator Alice) pub fn development_config() -> ChainSpec { - ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![]) + ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None) } fn local_testnet_genesis() -> GenesisConfig { @@ -181,5 +189,5 @@ fn local_testnet_genesis() -> GenesisConfig { /// Local testnet config (multivalidator Alice + Bob) pub fn local_testnet_config() -> ChainSpec { - ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![]) + ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None) } diff --git a/src/main.rs b/src/main.rs index 8780126fcddf..4e3fba054f96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,19 +25,24 @@ extern crate futures; #[macro_use] extern crate error_chain; -use cli::{ServiceComponents, Service}; +use cli::{ServiceComponents, Service, VersionInfo}; use futures::sync::oneshot; use futures::{future, Future}; use std::cell::RefCell; +mod vergen { + #![allow(unused)] + include!(concat!(env!("OUT_DIR"), "/version.rs")); +} + // the regular polkadot worker simply does nothing until ctrl-c struct Worker; impl cli::Worker for Worker { type Work = Self::Exit; type Exit = future::MapErr, fn(oneshot::Canceled) -> ()>; - fn exit_only(self) -> Self::Exit { + fn exit_only(&self) -> Self::Exit { // can't use signal directly here because CtrlC takes only `Fn`. let (exit_send, exit) = oneshot::channel(); @@ -59,5 +64,12 @@ impl cli::Worker for Worker { quick_main!(run); fn run() -> cli::error::Result<()> { - cli::run(::std::env::args(), Worker) + let version = VersionInfo { + commit: vergen::short_sha(), + version: env!("CARGO_PKG_VERSION"), + executable_name: "polkadot", + author: "Parity Team ", + description: "Polkadot Node Rust Implementation", + }; + cli::run(::std::env::args(), Worker, version) }