// 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 chain_spec::ChainSpec;
use futures::{Future, FutureExt, TryFutureExt, future::select, channel::oneshot, compat::Future01CompatExt};
pub use tokio::runtime::Runtime;
use std::sync::Arc;
use log::{info, error};
use structopt::StructOpt;
pub use service::{
AbstractService, CustomConfiguration,
ProvideRuntimeApi, CoreApi, ParachainHost,
};
pub use cli::{VersionInfo, IntoExit, NoCustom};
pub use cli::{display_role, error};
type BoxedFuture = Box + Send>;
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 + Unpin + 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: &Runtime) -> Self::Work
where S: AbstractService,
SC: service::SelectChain + 'static,
B: service::Backend + 'static,
CE: service::CallExecutor + Clone + Send + Sync + 'static;
}
#[derive(Debug, StructOpt, Clone)]
enum PolkadotSubCommands {
#[structopt(name = "validation-worker", setting = structopt::clap::AppSettings::Hidden)]
ValidationWorker(ValidationWorkerCommand),
}
impl cli::GetLogFilter for PolkadotSubCommands {
fn get_log_filter(&self) -> Option { None }
}
#[derive(Debug, StructOpt, Clone)]
struct ValidationWorkerCommand {
#[structopt()]
pub mem_id: String,
}
#[derive(Debug, StructOpt, Clone)]
struct PolkadotSubParams {
#[structopt(long = "enable-authority-discovery")]
pub authority_discovery_enabled: bool,
}
cli::impl_augment_clap!(PolkadotSubParams);
/// Parses polkadot specific CLI arguments and run the service.
pub fn run(worker: W, version: cli::VersionInfo) -> error::Result<()> where
W: Worker,
{
match cli::parse_and_prepare::(
&version,
"parity-polkadot",
std::env::args(),
) {
cli::ParseAndPrepare::Run(cmd) => cmd.run(load_spec, 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());
if config.chain_spec.name().starts_with("Kusama") {
info!("----------------------------");
info!("This chain is not in any way");
info!(" endorsed by the ");
info!(" KUSAMA FOUNDATION ");
info!("----------------------------");
}
info!("Node name: {}", config.name);
info!("Roles: {}", display_role(&config));
config.custom = worker.configuration();
config.custom.authority_discovery_enabled = custom_args.authority_discovery_enabled;
let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
match config.roles {
service::Roles::LIGHT =>
run_until_exit(
runtime,
service::new_light(config).map_err(|e| format!("{:?}", e))?,
worker
),
_ => run_until_exit(
runtime,
service::new_full(config).map_err(|e| format!("{:?}", e))?,
worker
),
}.map_err(|e| format!("{:?}", e))
}),
cli::ParseAndPrepare::BuildSpec(cmd) => cmd.run::(load_spec),
cli::ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder::<(), _, _, _, _, _, _>(|config|
Ok(service::new_chain_ops(config)?), load_spec, worker),
cli::ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder::<(), _, _, _, _, _, _>(|config|
Ok(service::new_chain_ops(config)?), load_spec, worker),
cli::ParseAndPrepare::CheckBlock(cmd) => cmd.run_with_builder::<(), _, _, _, _, _, _>(|config|
Ok(service::new_chain_ops(config)?), load_spec, worker),
cli::ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
cli::ParseAndPrepare::RevertChain(cmd) => cmd.run_with_builder::<(), _, _, _, _, _>(|config|
Ok(service::new_chain_ops(config)?), load_spec),
cli::ParseAndPrepare::CustomCommand(PolkadotSubCommands::ValidationWorker(args)) => {
#[cfg(not(target_os = "unknown"))]
service::run_validation_worker(&args.mem_id)?;
Ok(())
}
}
}
fn run_until_exit(
mut runtime: Runtime,
service: T,
worker: W,
) -> error::Result<()>
where
T: AbstractService,
SC: service::SelectChain + 'static,
B: service::Backend + 'static,
CE: service::CallExecutor + Clone + Send + Sync + 'static,
W: Worker,
{
let (exit_send, exit) = oneshot::channel();
let informant = cli::informant::build(&service);
let future = select(exit, informant);
let informant_handle = runtime.spawn(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 work = worker.work(&service, &runtime);
let service = service
.map_err(|err| error!("Error while running Service: {}", err))
.compat();
let future = select(service, work);;
let _ = runtime.block_on(future);
let _ = exit_send.send(());
let _ = runtime.block_on(informant_handle);
Ok(())
}