// 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 . //! Polkadot CLI library. #![warn(missing_docs)] #![warn(unused_extern_crates)] mod chain_spec; use std::ops::Deref; use chain_spec::ChainSpec; use futures::Future; use tokio::runtime::Runtime; use service::{Service as BareService, Error as ServiceError}; use std::sync::Arc; use log::{info, error}; use structopt::StructOpt; pub use service::{ Components as ServiceComponents, PolkadotService, CustomConfiguration, ServiceFactory, Factory, ProvideRuntimeApi, CoreApi, ParachainHost, }; pub use cli::{VersionInfo, IntoExit, NoCustom}; pub use cli::error; /// Abstraction over an executor that lets you spawn tasks in the background. pub type TaskExecutor = Arc + Send>> + Send + Sync>; 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. /// /// This will be invoked with the service and spawn a future that resolves /// when complete. pub trait Worker: IntoExit { /// A future that resolves when the work is done or the node should exit. /// This will be run on a tokio runtime. type Work: Future + Send + 'static; /// Return configuration for the polkadot node. // TODO: make this the full configuration, so embedded nodes don't need // string CLI args (https://github.com/paritytech/polkadot/issues/111) fn configuration(&self) -> service::CustomConfiguration { Default::default() } /// Do work and schedule exit. fn work(self, service: &S, executor: TaskExecutor) -> Self::Work; } #[derive(Debug, StructOpt, Clone)] enum PolkadotSubCommands { #[structopt(name = "validation-worker", raw(setting = "structopt::clap::AppSettings::Hidden"))] ValidationWorker(ValidationWokerCommand), } impl cli::GetLogFilter for PolkadotSubCommands { fn get_log_filter(&self) -> Option { None } } #[derive(Debug, StructOpt, Clone)] struct ValidationWokerCommand { #[structopt()] pub mem_id: String, } /// Parses polkadot specific CLI arguments and run the service. pub fn run(worker: W, version: cli::VersionInfo) -> error::Result<()> where W: Worker, { let command = cli::parse_and_execute::( load_spec, &version, "parity-polkadot", std::env::args(), worker, |worker, _cli_args, _custom_args, mut config| { info!("{}", version.name); info!(" version {}", config.full_version()); info!(" by {}, 2017-2019", version.author); info!("Chain specification: {}", config.chain_spec.name()); info!("Node name: {}", config.name); info!("Roles: {:?}", config.roles); config.custom = worker.configuration(); let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?; match config.roles { service::Roles::LIGHT => run_until_exit( runtime, Factory::new_light(config).map_err(|e| format!("{:?}", e))?, worker ), _ => run_until_exit( runtime, Factory::new_full(config).map_err(|e| format!("{:?}", e))?, worker ), }.map_err(|e| format!("{:?}", e)) } )?; match command { Some(PolkadotSubCommands::ValidationWorker(args)) => { service::run_validation_worker(&args.mem_id).map_err(Into::into) } _ => Ok(()) } } fn run_until_exit( mut runtime: Runtime, service: T, worker: W, ) -> error::Result<()> where T: Deref> + Future + Send + 'static, C: service::Components, BareService: PolkadotService, W: Worker, { let (exit_send, exit) = exit_future::signal(); let executor = runtime.executor(); let informant = cli::informant::build(&service); executor.spawn(exit.until(informant).map(|_| ())); // 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 work = worker.work(&*service, Arc::new(executor)); let service = service.map_err(|err| error!("Error while running Service: {}", err)); let _ = runtime.block_on(service.select(work)); exit_send.fire(); Ok(()) }