command.rs 14.4 KB
Newer Older
Gavin Wood's avatar
Gavin Wood committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Copyright 2017-2020 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 <http://www.gnu.org/licenses/>.

use log::info;
Seun Lanlege's avatar
Seun Lanlege committed
18
use service::{IdentifyVariant, self};
19
use sc_cli::{SubstrateCli, RuntimeVersion, Role};
Gavin Wood's avatar
Gavin Wood committed
20
use crate::cli::{Cli, Subcommand};
21
use futures::future::TryFutureExt;
22

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#[derive(thiserror::Error, Debug)]
pub enum Error {
	#[error(transparent)]
	PolkadotService(#[from] service::Error),

	#[error(transparent)]
	SubstrateCli(#[from] sc_cli::Error),

	#[error(transparent)]
	SubstrateService(#[from] sc_service::Error),

	#[error("Other: {0}")]
	Other(String),
}

impl std::convert::From<String> for Error {
	fn from(s: String) -> Self {
		Self::Other(s)
	}
}

type Result<T> = std::result::Result<T, Error>;

Gavin Wood's avatar
Gavin Wood committed
46
47
48
49
50
51
52
fn get_exec_name() -> Option<String> {
	std::env::current_exe()
		.ok()
		.and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
		.and_then(|s| s.into_string().ok())
}

53
impl SubstrateCli for Cli {
54
	fn impl_name() -> String { "Parity Polkadot".into() }
55

56
	fn impl_version() -> String { env!("SUBSTRATE_CLI_IMPL_VERSION").into() }
57

58
	fn description() -> String { env!("CARGO_PKG_DESCRIPTION").into() }
59

60
	fn author() -> String { env!("CARGO_PKG_AUTHORS").into() }
61

62
	fn support_url() -> String { "https://github.com/paritytech/polkadot/issues/new".into() }
63
64
65

	fn copyright_start_year() -> i32 { 2017 }

66
	fn executable_name() -> String { "polkadot".into() }
67
68

	fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
Gavin Wood's avatar
Gavin Wood committed
69
70
		let id = if id == "" {
			let n = get_exec_name().unwrap_or_default();
71
			["polkadot", "kusama", "westend", "rococo"].iter()
Gavin Wood's avatar
Gavin Wood committed
72
73
74
75
				.cloned()
				.find(|&chain| n.starts_with(chain))
				.unwrap_or("polkadot")
		} else { id };
76
		Ok(match id {
77
			"kusama" => Box::new(service::chain_spec::kusama_config()?),
78
			#[cfg(feature = "kusama-native")]
Wei Tang's avatar
Wei Tang committed
79
			"kusama-dev" => Box::new(service::chain_spec::kusama_development_config()?),
80
			#[cfg(feature = "kusama-native")]
Wei Tang's avatar
Wei Tang committed
81
			"kusama-local" => Box::new(service::chain_spec::kusama_local_testnet_config()?),
82
			#[cfg(feature = "kusama-native")]
Wei Tang's avatar
Wei Tang committed
83
			"kusama-staging" => Box::new(service::chain_spec::kusama_staging_testnet_config()?),
84
			#[cfg(not(feature = "kusama-native"))]
85
			name if name.starts_with("kusama-") && !name.ends_with(".json") =>
86
				Err(format!("`{}` only supported with `kusama-native` feature enabled.", name))?,
Gavin Wood's avatar
Gavin Wood committed
87
			"polkadot" => Box::new(service::chain_spec::polkadot_config()?),
88
89
90
91
			"polkadot-dev" | "dev" => Box::new(service::chain_spec::polkadot_development_config()?),
			"polkadot-local" => Box::new(service::chain_spec::polkadot_local_testnet_config()?),
			"polkadot-staging" => Box::new(service::chain_spec::polkadot_staging_testnet_config()?),
			"rococo" => Box::new(service::chain_spec::rococo_config()?),
92
			#[cfg(feature = "rococo-native")]
93
			"rococo-dev" => Box::new(service::chain_spec::rococo_development_config()?),
94
			#[cfg(feature = "rococo-native")]
95
			"rococo-local" => Box::new(service::chain_spec::rococo_local_testnet_config()?),
96
			#[cfg(feature = "rococo-native")]
97
			"rococo-staging" => Box::new(service::chain_spec::rococo_staging_testnet_config()?),
98
99
			#[cfg(not(feature = "rococo-native"))]
			name if name.starts_with("rococo-") && !name.ends_with(".json") =>
100
				Err(format!("`{}` only supported with `rococo-native` feature enabled.", name))?,
101
			"westend" => Box::new(service::chain_spec::westend_config()?),
102
			#[cfg(feature = "westend-native")]
Wei Tang's avatar
Wei Tang committed
103
			"westend-dev" => Box::new(service::chain_spec::westend_development_config()?),
104
			#[cfg(feature = "westend-native")]
Wei Tang's avatar
Wei Tang committed
105
			"westend-local" => Box::new(service::chain_spec::westend_local_testnet_config()?),
106
			#[cfg(feature = "westend-native")]
Wei Tang's avatar
Wei Tang committed
107
			"westend-staging" => Box::new(service::chain_spec::westend_staging_testnet_config()?),
108
			#[cfg(not(feature = "westend-native"))]
109
			name if name.starts_with("westend-") && !name.ends_with(".json") =>
110
				Err(format!("`{}` only supported with `westend-native` feature enabled.", name))?,
111
			"wococo" => Box::new(service::chain_spec::wococo_config()?),
112
			#[cfg(feature = "rococo-native")]
113
			"wococo-dev" => Box::new(service::chain_spec::wococo_development_config()?),
114
115
116
			#[cfg(not(feature = "rococo-native"))]
			name if name.starts_with("wococo-") =>
				Err(format!("`{}` only supported with `rococo-native` feature enabled.", name))?,
117
118
119
			path => {
				let path = std::path::PathBuf::from(path);

120
				let chain_spec = Box::new(service::PolkadotChainSpec::from_json_file(path.clone())?) as Box<dyn service::ChainSpec>;
121
122
123

				// When `force_*` is given or the file name starts with the name of one of the known chains,
				// we use the chain spec for the specific chain.
124
				if self.run.force_rococo || chain_spec.is_rococo() || chain_spec.is_wococo() {
125
					Box::new(service::RococoChainSpec::from_json_file(path)?)
126
				} else if self.run.force_kusama || chain_spec.is_kusama() {
127
					Box::new(service::KusamaChainSpec::from_json_file(path)?)
128
				} else if self.run.force_westend || chain_spec.is_westend() {
129
130
					Box::new(service::WestendChainSpec::from_json_file(path)?)
				} else {
131
					chain_spec
132
				}
133
134
135
			},
		})
	}
136
137

	fn native_runtime_version(spec: &Box<dyn service::ChainSpec>) -> &'static RuntimeVersion {
138
		#[cfg(feature = "kusama-native")]
139
		if spec.is_kusama() {
140
			return &service::kusama_runtime::VERSION
141
		}
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

		#[cfg(feature = "westend-native")]
		if spec.is_westend() {
			return &service::westend_runtime::VERSION
		}

		#[cfg(feature = "rococo-native")]
		if spec.is_rococo() || spec.is_wococo() {
			return &service::rococo_runtime::VERSION
		}

		#[cfg(not(all(feature = "rococo-native", feature = "westend-native", feature = "kusama-native")))]
		let _ = spec;

		&service::polkadot_runtime::VERSION
157
	}
158
}
Gavin Wood's avatar
Gavin Wood committed
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
fn set_default_ss58_version(spec: &Box<dyn service::ChainSpec>) {
	use sp_core::crypto::Ss58AddressFormat;

	let ss58_version = if spec.is_kusama() {
		Ss58AddressFormat::KusamaAccount
	} else if spec.is_westend() {
		Ss58AddressFormat::SubstrateAccount
	} else {
		Ss58AddressFormat::PolkadotAccount
	};

	sp_core::crypto::set_default_ss58_version(ss58_version);
}

174
const DEV_ONLY_ERROR_PATTERN: &'static str =
175
	"can only use subcommand with --chain [polkadot-dev, kusama-dev, westend-dev, rococo-dev, wococo-dev], got ";
176
177

fn ensure_dev(spec: &Box<dyn service::ChainSpec>) -> std::result::Result<(), String> {
Kian Paimani's avatar
Kian Paimani committed
178
179
180
181
182
	if spec.is_dev() {
		Ok(())
	} else {
		Err(format!("{}{}", DEV_ONLY_ERROR_PATTERN, spec.id()))
	}
183
184
}

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/// Launch a node, accepting arguments just like a regular node,
/// accepts an alternative overseer generator, to adjust behavior
/// for integration tests as needed.
#[cfg(feature = "malus")]
pub fn run_node(cli: Cli, overseer_gen: impl service::OverseerGen) -> Result<()> {
	run_node_inner(cli, overseer_gen)
}

fn run_node_inner(cli: Cli, overseer_gen: impl service::OverseerGen) -> Result<()> {
	let runner = cli.create_runner(&cli.run.base)
		.map_err(Error::from)?;
	let chain_spec = &runner.config().chain_spec;

	set_default_ss58_version(chain_spec);

	let grandpa_pause = if cli.run.grandpa_pause.is_empty() {
		None
	} else {
		Some((cli.run.grandpa_pause[0], cli.run.grandpa_pause[1]))
	};

	if chain_spec.is_kusama() {
		info!("----------------------------");
		info!("This chain is not in any way");
		info!("      endorsed by the       ");
		info!("     KUSAMA FOUNDATION      ");
		info!("----------------------------");
	}

	let jaeger_agent = cli.run.jaeger_agent;

	runner.run_node_until_exit(move |config| async move {
		let role = config.role.clone();

		match role {
			#[cfg(feature = "browser")]
			Role::Light => service::build_light(config).map(|(task_manager, _)| task_manager).map_err(Into::into),
			#[cfg(not(feature = "browser"))]
			Role::Light => Err(Error::Other("Light client not enabled".into())),
			_ => service::build_full(
				config,
				service::IsCollator::No,
				grandpa_pause,
				cli.run.no_beefy,
				jaeger_agent,
				None,
				overseer_gen,
			).map(|full| full.task_manager).map_err(Into::into)
		}
	})
}

Gavin Wood's avatar
Gavin Wood committed
237
/// Parses polkadot specific CLI arguments and run the service.
238
239
240
241
pub fn run() -> Result<()> {
	let cli = Cli::from_args();

	match &cli.subcommand {
242
		None => run_node_inner(cli, service::RealOverseerGen),
243
244
		Some(Subcommand::BuildSpec(cmd)) => {
			let runner = cli.create_runner(cmd)?;
245
246
247
			Ok(runner.sync_run(|config| {
				cmd.run(config.chain_spec, config.network)
			})?)
248
249
		},
		Some(Subcommand::CheckBlock(cmd)) => {
250
251
			let runner = cli.create_runner(cmd)
				.map_err(Error::SubstrateCli)?;
252
253
254
255
256
			let chain_spec = &runner.config().chain_spec;

			set_default_ss58_version(chain_spec);

			runner.async_run(|mut config| {
257
258
				let (client, _, import_queue, task_manager) = service::new_chain_ops(&mut config, None)?;
				Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager))
259
260
261
262
			})
		},
		Some(Subcommand::ExportBlocks(cmd)) => {
			let runner = cli.create_runner(cmd)?;
263
			let chain_spec = &runner.config().chain_spec;
264
265

			set_default_ss58_version(chain_spec);
Gavin Wood's avatar
Gavin Wood committed
266

267
			Ok(runner.async_run(|mut config| {
268
				let (client, _, _, task_manager) = service::new_chain_ops(&mut config, None)
269
270
271
					.map_err(Error::PolkadotService)?;
				Ok((cmd.run(client, config.database).map_err(Error::SubstrateCli), task_manager))
			})?)
272
273
274
275
276
277
278
		},
		Some(Subcommand::ExportState(cmd)) => {
			let runner = cli.create_runner(cmd)?;
			let chain_spec = &runner.config().chain_spec;

			set_default_ss58_version(chain_spec);

279
280
281
282
			Ok(runner.async_run(|mut config| {
				let (client, _, _, task_manager) = service::new_chain_ops(&mut config, None)?;
				Ok((cmd.run(client, config.chain_spec).map_err(Error::SubstrateCli), task_manager))
			})?)
283
284
285
286
287
288
289
		},
		Some(Subcommand::ImportBlocks(cmd)) => {
			let runner = cli.create_runner(cmd)?;
			let chain_spec = &runner.config().chain_spec;

			set_default_ss58_version(chain_spec);

290
291
292
293
			Ok(runner.async_run(|mut config| {
				let (client, _, import_queue, task_manager) = service::new_chain_ops(&mut config, None)?;
				Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager))
			})?)
294
295
296
		},
		Some(Subcommand::PurgeChain(cmd)) => {
			let runner = cli.create_runner(cmd)?;
297
			Ok(runner.sync_run(|config| cmd.run(config.database))?)
298
299
300
301
302
303
304
		},
		Some(Subcommand::Revert(cmd)) => {
			let runner = cli.create_runner(cmd)?;
			let chain_spec = &runner.config().chain_spec;

			set_default_ss58_version(chain_spec);

305
306
			Ok(runner.async_run(|mut config| {
				let (client, backend, _, task_manager) = service::new_chain_ops(&mut config, None)?;
Kian Paimani's avatar
Kian Paimani committed
307
				Ok((cmd.run(client, backend).map_err(Error::SubstrateCli), task_manager))
308
			})?)
Gavin Wood's avatar
Gavin Wood committed
309
		},
310
		Some(Subcommand::PvfPrepareWorker(cmd)) => {
Cecile Tonglet's avatar
Cecile Tonglet committed
311
			let mut builder = sc_cli::LoggerBuilder::new("");
312
313
			builder.with_colors(false);
			let _ = builder.init();
Gavin Wood's avatar
Gavin Wood committed
314

315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
			#[cfg(any(target_os = "android", feature = "browser"))]
			{
				return Err(
					sc_cli::Error::Input("PVF preparation workers are not supported under this platform".into()).into()
				);
			}

			#[cfg(not(any(target_os = "android", feature = "browser")))]
			{
				polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path);
				Ok(())
			}
		},
		Some(Subcommand::PvfExecuteWorker(cmd)) => {
			let mut builder = sc_cli::LoggerBuilder::new("");
			builder.with_colors(false);
			let _ = builder.init();

			#[cfg(any(target_os = "android", feature = "browser"))]
			{
				return Err(
					sc_cli::Error::Input("PVF execution workers are not supported under this platform".into()).into()
				);
			}

			#[cfg(not(any(target_os = "android", feature = "browser")))]
			{
				polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path);
Gavin Wood's avatar
Gavin Wood committed
343
344
345
				Ok(())
			}
		},
346
		Some(Subcommand::Benchmark(cmd)) => {
347
348
			let runner = cli.create_runner(cmd)?;
			let chain_spec = &runner.config().chain_spec;
349
			set_default_ss58_version(chain_spec);
350

351
			ensure_dev(chain_spec).map_err(Error::Other)?;
352
353

			#[cfg(feature = "kusama-native")]
Kian Paimani's avatar
Kian Paimani committed
354
			if chain_spec.is_kusama() {
355
				return Ok(runner.sync_run(|config| {
356
357
358
					cmd.run::<service::kusama_runtime::Block, service::KusamaExecutor>(config)
						.map_err(|e| Error::SubstrateCli(e))
				})?)
359
360
361
362
363
			}

			#[cfg(feature = "westend-native")]
			if chain_spec.is_westend() {
				return Ok(runner.sync_run(|config| {
364
365
366
367
					cmd.run::<service::westend_runtime::Block, service::WestendExecutor>(config)
						.map_err(|e| Error::SubstrateCli(e))
				})?)
			}
368
369
370
371
372
373

			// else we assume it is polkadot.
			Ok(runner.sync_run(|config| {
				cmd.run::<service::polkadot_runtime::Block, service::PolkadotExecutor>(config)
					.map_err(|e| Error::SubstrateCli(e))
			})?)
374
		},
375
		Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
Kian Paimani's avatar
Kian Paimani committed
376
377
378
379
380
381
		#[cfg(feature = "try-runtime")]
		Some(Subcommand::TryRuntime(cmd)) => {
			let runner = cli.create_runner(cmd)?;
			let chain_spec = &runner.config().chain_spec;
			set_default_ss58_version(chain_spec);

382
383
384
385
386
387
388
389
			use sc_service::TaskManager;
			let registry = &runner.config().prometheus_config.as_ref().map(|cfg| &cfg.registry);
			let task_manager = TaskManager::new(
				runner.config().task_executor.clone(),
				*registry,
			).map_err(|e| Error::SubstrateService(sc_service::Error::Prometheus(e)))?;

			ensure_dev(chain_spec).map_err(Error::Other)?;
390
391

			#[cfg(feature = "kusama-native")]
Kian Paimani's avatar
Kian Paimani committed
392
			if chain_spec.is_kusama() {
393
				return runner.async_run(|config| {
394
					Ok((cmd.run::<
Kian Paimani's avatar
Kian Paimani committed
395
396
						service::kusama_runtime::Block,
						service::KusamaExecutor,
397
398
					>(config).map_err(Error::SubstrateCli), task_manager))
				})
399
400
401
402
403
			}

			#[cfg(feature = "westend-native")]
			if chain_spec.is_westend() {
				return runner.async_run(|config| {
404
405
406
407
408
409
					Ok((cmd.run::<
						service::westend_runtime::Block,
						service::WestendExecutor,
					>(config).map_err(Error::SubstrateCli), task_manager))
				})
			}
410
411
412
413
414
415
416
			// else we assume it is polkadot.
			runner.async_run(|config| {
				Ok((cmd.run::<
					service::polkadot_runtime::Block,
					service::PolkadotExecutor,
				>(config).map_err(Error::SubstrateCli), task_manager))
			})
Zeke Mostov's avatar
Zeke Mostov committed
417
418
419
420
421
422
		},
		#[cfg(not(feature = "try-runtime"))]
		Some(Subcommand::TryRuntime) => {
			Err(Error::Other("TryRuntime wasn't enabled when building the node. \
				You can enable it with `--features try-runtime`.".into()).into())
		},
423
424
	}?;
	Ok(())
Gavin Wood's avatar
Gavin Wood committed
425
}