1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Moonbeam CLI Library. Built with clap
18
//!
19
//! This module defines the Moonbeam node's Command Line Interface (CLI)
20
//! It is built using clap and inherits behavior from Substrate's sc_cli crate.
21

            
22
use clap::Parser;
23
use moonbeam_cli_opt::{
24
	account_key::{GenerateAccountKey, Network},
25
	EthApi, FrontierBackendType, Sealing,
26
};
27
use moonbeam_service::chain_spec;
28
use sc_cli::{Error as CliError, SubstrateCli};
29
use std::path::PathBuf;
30
use std::time::Duration;
31
use url::Url;
32

            
33
#[cfg(feature = "lazy-loading")]
34
fn parse_block_hash(s: &str) -> Result<sp_core::H256, String> {
35
	use std::str::FromStr;
36
	sp_core::H256::from_str(s).map_err(|err| err.to_string())
37
}
38

            
39
fn validate_url(arg: &str) -> Result<Url, String> {
40
	let url = Url::parse(arg).map_err(|e| e.to_string())?;
41

            
42
	let scheme = url.scheme();
43
	if scheme == "http" || scheme == "https" {
44
		Ok(url)
45
	} else {
46
		Err(format!("'{}' URL scheme not supported.", url.scheme()))
47
	}
48
}
49

            
50
/// Sub-commands supported by the collator.
51
#[derive(Debug, clap::Subcommand)]
52
pub enum Subcommand {
53
	/// Export the genesis state of the parachain.
54
	#[clap(name = "export-genesis-state")]
55
	ExportGenesisHead(ExportGenesisHeadCommand),
56

            
57
	/// Export the genesis wasm of the parachain.
58
	#[clap(name = "export-genesis-wasm")]
59
	ExportGenesisWasm(ExportGenesisWasmCommand),
60

            
61
	/// Build a chain specification.
62
	BuildSpec(BuildSpecCommand),
63

            
64
	/// Validate blocks.
65
	CheckBlock(sc_cli::CheckBlockCmd),
66

            
67
	/// Export blocks.
68
	ExportBlocks(sc_cli::ExportBlocksCmd),
69

            
70
	/// Export the state of a given block into a chain spec.
71
	ExportState(sc_cli::ExportStateCmd),
72

            
73
	/// Import blocks.
74
	ImportBlocks(sc_cli::ImportBlocksCmd),
75

            
76
	/// Remove the whole chain.
77
	PurgeChain(cumulus_client_cli::PurgeChainCmd),
78

            
79
	/// Revert the chain to a previous state.
80
	Revert(sc_cli::RevertCmd),
81

            
82
	/// Sub-commands concerned with benchmarking.
83
	/// The pallet benchmarking moved to the `pallet` sub-command.
84
	#[clap(subcommand)]
85
	Benchmark(frame_benchmarking_cli::BenchmarkCmd),
86

            
87
	/// Try some command against runtime state.
88
	TryRuntime,
89

            
90
	/// Key management cli utilities
91
	#[clap(subcommand)]
92
	Key(KeyCmd),
93

            
94
	/// Precompile the WASM runtime into native code
95
	PrecompileWasm(sc_cli::PrecompileWasmCmd),
96
}
97

            
98
#[derive(Debug, Parser)]
99
pub struct BuildSpecCommand {
100
	#[clap(flatten)]
101
	pub base: sc_cli::BuildSpecCmd,
102

            
103
	/// Number of accounts to be funded in the genesis
104
	/// Warning: This flag implies a development spec and overrides any explicitly supplied spec
105
	#[clap(long, conflicts_with = "chain")]
106
	pub accounts: Option<u32>,
107

            
108
	/// Mnemonic from which we can derive funded accounts in the genesis
109
	/// Warning: This flag implies a development spec and overrides any explicitly supplied spec
110
	#[clap(long, conflicts_with = "chain")]
111
	pub mnemonic: Option<String>,
112
}
113

            
114
/// Command for exporting the genesis state of the parachain
115
#[derive(Debug, Parser)]
116
pub struct ExportGenesisHeadCommand {
117
	/// Output file name or stdout if unspecified.
118
	#[clap(value_parser)]
119
	pub output: Option<PathBuf>,
120

            
121
	/// Id of the parachain this state is for.
122
	#[clap(long)]
123
	pub parachain_id: Option<u32>,
124

            
125
	/// Write output in binary. Default is to write in hex.
126
	#[clap(short, long)]
127
	pub raw: bool,
128

            
129
	/// The name of the chain for that the genesis state should be exported.
130
	#[clap(long)]
131
	pub chain: Option<String>,
132
}
133

            
134
/// Command for exporting the genesis wasm file.
135
#[derive(Debug, Parser)]
136
pub struct ExportGenesisWasmCommand {
137
	/// Output file name or stdout if unspecified.
138
	#[clap(value_parser)]
139
	pub output: Option<PathBuf>,
140

            
141
	/// Write output in binary. Default is to write in hex.
142
	#[clap(short, long)]
143
	pub raw: bool,
144

            
145
	/// The name of the chain for that the genesis wasm file should be exported.
146
	#[clap(long)]
147
	pub chain: Option<String>,
148
}
149

            
150
#[derive(Debug, Parser)]
151
#[group(skip)]
152
pub struct RunCmd {
153
	#[clap(flatten)]
154
	pub base: cumulus_client_cli::RunCmd,
155

            
156
	/// Enable the development service to run without a backing relay chain
157
	#[clap(long)]
158
	pub dev_service: bool,
159

            
160
	/// No-op
161
	/// Deprecated in: https://github.com/moonbeam-foundation/moonbeam/pull/3204
162
	#[clap(long)]
163
	pub experimental_block_import_strategy: bool,
164

            
165
	/// Enable the legacy block import strategy
166
	#[clap(long)]
167
	pub legacy_block_import_strategy: bool,
168

            
169
	/// Specifies the URL used to fetch chain data via RPC.
170
	///
171
	/// The URL should point to the RPC endpoint of the chain being forked.
172
	/// Ensure that the RPC has sufficient rate limits to handle the expected load.
173
	#[cfg(feature = "lazy-loading")]
174
	#[clap(long)]
175
	#[arg(
176
		long,
177
		value_parser = validate_url,
178
		alias = "fork-chain-from-rpc"
179
	)]
180
	pub lazy_loading_remote_rpc: Option<Url>,
181

            
182
	/// Optional parameter to specify the block hash for lazy loading.
183
	///
184
	/// This parameter allows the user to specify a block hash from which to start loading data.
185
	///
186
	/// If not provided, the latest block will be used.
187
	#[cfg(feature = "lazy-loading")]
188
	#[arg(
189
		long,
190
		value_name = "BLOCK",
191
		value_parser = parse_block_hash,
192
		alias = "block"
193
	)]
194
	pub lazy_loading_block: Option<sp_core::H256>,
195

            
196
	/// Optional parameter to specify state overrides during lazy loading.
197
	///
198
	/// This parameter allows the user to provide a path to a file containing state overrides.
199
	/// The file can contain any custom state modifications that should be applied.
200
	#[cfg(feature = "lazy-loading")]
201
	#[clap(
202
		long,
203
		value_name = "PATH",
204
		value_parser,
205
		alias = "fork-state-overrides"
206
	)]
207
	pub lazy_loading_state_overrides: Option<PathBuf>,
208

            
209
	/// Optional parameter to specify a runtime override when starting the lazy loading.
210
	///
211
	/// If not provided, it will fetch the runtime from the block being forked.
212
	#[cfg(feature = "lazy-loading")]
213
	#[clap(long, value_name = "PATH", value_parser, alias = "runtime-override")]
214
	pub lazy_loading_runtime_override: Option<PathBuf>,
215

            
216
	/// The delay (in milliseconds) between RPC requests when using lazy loading.
217
	///
218
	/// This parameter controls the amount of time (in milliseconds) to wait between consecutive
219
	/// RPC requests. This can help manage request rate and avoid overwhelming the server.
220
	///
221
	/// The default value is 100 milliseconds.
222
	#[cfg(feature = "lazy-loading")]
223
	#[clap(long, default_value = "100")]
224
	pub lazy_loading_delay_between_requests: u32,
225

            
226
	/// The maximum number of retries for an RPC request when using lazy loading.
227
	///
228
	/// The default value is 10 retries.
229
	#[cfg(feature = "lazy-loading")]
230
	#[clap(long, default_value = "10")]
231
	pub lazy_loading_max_retries_per_request: u32,
232

            
233
	/// When blocks should be sealed in the dev service.
234
	///
235
	/// Options are "instant", "manual", or timer interval in milliseconds
236
	#[clap(long, default_value = "instant")]
237
	pub sealing: Sealing,
238

            
239
	/// Public authoring identity to be inserted in the author inherent
240
	/// This is not currently used, but we may want a way to use it in the dev service.
241
	// #[clap(long)]
242
	// pub author_id: Option<NimbusId>,
243

            
244
	/// Enable EVM tracing module on a non-authority node.
245
	#[clap(long, value_delimiter = ',')]
246
940
	pub ethapi: Vec<EthApi>,
247

            
248
	/// Number of concurrent tracing tasks. Meant to be shared by both "debug" and "trace" modules.
249
	#[clap(long, default_value = "10")]
250
	pub ethapi_max_permits: u32,
251

            
252
	/// Maximum number of trace entries a single request of `trace_filter` is allowed to return.
253
	/// A request asking for more or an unbounded one going over this limit will both return an
254
	/// error.
255
	#[clap(long, default_value = "500")]
256
	pub ethapi_trace_max_count: u32,
257

            
258
	/// Duration (in seconds) after which the cache of `trace_filter` for a given block will be
259
	/// discarded.
260
	#[clap(long, default_value = "300")]
261
	pub ethapi_trace_cache_duration: u64,
262

            
263
	/// Size in bytes of the LRU cache for block data.
264
	#[clap(long, default_value = "300000000")]
265
	pub eth_log_block_cache: usize,
266

            
267
	/// Size in bytes of the LRU cache for transactions statuses data.
268
	#[clap(long, default_value = "300000000")]
269
	pub eth_statuses_cache: usize,
270

            
271
	/// Sets the frontier backend type (KeyValue or Sql)
272
946
	#[arg(long, value_enum, ignore_case = true, default_value_t = FrontierBackendType::default())]
273
	pub frontier_backend_type: FrontierBackendType,
274

            
275
	// Sets the SQL backend's pool size.
276
	#[arg(long, default_value = "100")]
277
	pub frontier_sql_backend_pool_size: u32,
278

            
279
	/// Sets the SQL backend's query timeout in number of VM ops.
280
	#[arg(long, default_value = "10000000")]
281
	pub frontier_sql_backend_num_ops_timeout: u32,
282

            
283
	/// Sets the SQL backend's auxiliary thread limit.
284
	#[arg(long, default_value = "4")]
285
	pub frontier_sql_backend_thread_count: u32,
286

            
287
	/// Sets the SQL backend's cache size in bytes.
288
	/// Default value is 200MB.
289
	#[arg(long, default_value = "209715200")]
290
	pub frontier_sql_backend_cache_size: u64,
291

            
292
	/// Size in bytes of data a raw tracing request is allowed to use.
293
	/// Bound the size of memory, stack and storage data.
294
	#[clap(long, default_value = "20000000")]
295
	pub tracing_raw_max_memory_usage: usize,
296

            
297
	/// Maximum number of logs in a query.
298
	#[clap(long, default_value = "10000")]
299
	pub max_past_logs: u32,
300

            
301
	/// Maximum block range to query logs from.
302
	#[clap(long, default_value = "1024")]
303
	pub max_block_range: u32,
304

            
305
	/// Force using Moonbase native runtime.
306
	#[clap(long = "force-moonbase")]
307
	pub force_moonbase: bool,
308

            
309
	/// Force using Moonriver native runtime.
310
	#[clap(long = "force-moonriver")]
311
	pub force_moonriver: bool,
312

            
313
	/// Id of the parachain this collator collates for.
314
	#[clap(long)]
315
	pub parachain_id: Option<u32>,
316

            
317
	/// Maximum fee history cache size.
318
	#[clap(long, default_value = "2048")]
319
	pub fee_history_limit: u64,
320

            
321
	/// Disable automatic hardware benchmarks.
322
	///
323
	/// By default these benchmarks are automatically ran at startup and measure
324
	/// the CPU speed, the memory bandwidth and the disk speed.
325
	///
326
	/// The results are then printed out in the logs, and also sent as part of
327
	/// telemetry, if telemetry is enabled.
328
	#[clap(long)]
329
	pub no_hardware_benchmarks: bool,
330

            
331
	/// Removes moonbeam prefix from Prometheus metrics
332
	#[clap(long)]
333
	pub no_prometheus_prefix: bool,
334

            
335
	/// Maximum duration in milliseconds to produce a block
336
	#[clap(long, default_value = "2000", value_parser=block_authoring_duration_parser)]
337
	pub block_authoring_duration: Duration,
338

            
339
	/// Enable full proof-of-validation mode for Nimbus (deprecated, use --max-pov-percentage instead)
340
	#[clap(long, hide = true)]
341
	pub nimbus_full_pov: bool,
342

            
343
	/// Maximum percentage of POV size to use (0-100)
344
	#[arg(
345
		long,
346
		conflicts_with = "nimbus_full_pov",
347
		default_value = "50",
348
		default_value_if("nimbus_full_pov", "true", "100")
349
	)]
350
	pub max_pov_percentage: u8,
351
}
352

            
353
946
fn block_authoring_duration_parser(s: &str) -> Result<Duration, String> {
354
946
	Ok(Duration::from_millis(clap_num::number_range(
355
946
		s, 250, 2_000,
356
946
	)?))
357
946
}
358

            
359
impl RunCmd {
360
942
	pub fn new_rpc_config(&self) -> moonbeam_cli_opt::RpcConfig {
361
942
		moonbeam_cli_opt::RpcConfig {
362
942
			ethapi: self.ethapi.clone(),
363
942
			ethapi_max_permits: self.ethapi_max_permits,
364
942
			ethapi_trace_max_count: self.ethapi_trace_max_count,
365
942
			ethapi_trace_cache_duration: self.ethapi_trace_cache_duration,
366
942
			eth_log_block_cache: self.eth_log_block_cache,
367
942
			eth_statuses_cache: self.eth_statuses_cache,
368
942
			fee_history_limit: self.fee_history_limit,
369
942
			max_past_logs: self.max_past_logs,
370
942
			max_block_range: self.max_block_range,
371
942
			relay_chain_rpc_urls: self.base.relay_chain_rpc_urls.clone(),
372
942
			tracing_raw_max_memory_usage: self.tracing_raw_max_memory_usage,
373
942
			frontier_backend_config: match self.frontier_backend_type {
374
942
				FrontierBackendType::KeyValue => moonbeam_cli_opt::FrontierBackendConfig::KeyValue,
375
				FrontierBackendType::Sql => moonbeam_cli_opt::FrontierBackendConfig::Sql {
376
					pool_size: self.frontier_sql_backend_pool_size,
377
					num_ops_timeout: self.frontier_sql_backend_num_ops_timeout,
378
					thread_count: self.frontier_sql_backend_thread_count,
379
					cache_size: self.frontier_sql_backend_cache_size,
380
				},
381
			},
382
942
			no_prometheus_prefix: self.no_prometheus_prefix,
383
942
		}
384
942
	}
385
}
386

            
387
impl std::ops::Deref for RunCmd {
388
	type Target = cumulus_client_cli::RunCmd;
389

            
390
1880
	fn deref(&self) -> &Self::Target {
391
1880
		&self.base
392
1880
	}
393
}
394

            
395
#[derive(Debug, clap::Subcommand)]
396
pub enum KeyCmd {
397
	#[clap(flatten)]
398
	BaseCli(sc_cli::KeySubcommand),
399
	/// Generate an Ethereum account.
400
	#[clap(about = "This command is deprecated, please use `generate-moonbeam-key` instead.")]
401
	GenerateAccountKey(GenerateAccountKey),
402
	/// Generate a Moonbeam account.
403
	GenerateMoonbeamKey(GenerateAccountKey),
404
}
405

            
406
impl KeyCmd {
407
	/// run the key subcommands
408
	pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), CliError> {
409
		match self {
410
			KeyCmd::BaseCli(cmd) => cmd.run(cli),
411
			KeyCmd::GenerateAccountKey(cmd) => {
412
				let deprecation_msg = r#"
413

            
414
				Warning: This command is deprecated, please use `generate-moonbeam-key` instead.
415

            
416
				The `generate-account-key` command used Ethereum's derivation path (m/44'/60'/0'/0/n)
417
				while `generate-moonbeam-key` uses Moonbeam's derivation path (m/44'/1284'/0'/0/n).
418
				Furthermore, it supports derivation paths for Moonriver, Moonbase, and Ethereum.
419

            
420
				For more information, see: https://github.com/moonbeam-foundation/moonbeam/pull/3090
421

            
422
				"#;
423
				eprintln!("{}", ansi_term::Colour::Yellow.paint(deprecation_msg));
424

            
425
				// Force ethereum network for deprecated command
426
				let mut cmd = cmd.clone();
427
				cmd.network = Network::Ethereum;
428
				cmd.run();
429
				Ok(())
430
			}
431
			KeyCmd::GenerateMoonbeamKey(cmd) => {
432
				cmd.run();
433
				Ok(())
434
			}
435
		}
436
	}
437
}
438

            
439
#[derive(Debug, Parser)]
440
#[clap(
441
	propagate_version = true,
442
	args_conflicts_with_subcommands = true,
443
	subcommand_negates_reqs = true
444
)]
445
pub struct Cli {
446
	#[clap(subcommand)]
447
	pub subcommand: Option<Subcommand>,
448

            
449
	#[clap(flatten)]
450
	pub run: RunCmd,
451

            
452
	/// Relaychain arguments
453
	#[clap(raw = true)]
454
	pub relaychain_args: Vec<String>,
455
}
456

            
457
#[derive(Debug)]
458
pub struct RelayChainCli {
459
	/// The actual relay chain cli object.
460
	pub base: polkadot_cli::RunCmd,
461

            
462
	/// Optional chain id that should be passed to the relay chain.
463
	pub chain_id: Option<String>,
464

            
465
	/// The base path that should be used by the relay chain.
466
	pub base_path: PathBuf,
467
}
468

            
469
impl RelayChainCli {
470
	/// Parse the relay chain CLI parameters using the para chain `Configuration`.
471
	pub fn new<'a>(
472
		para_config: &sc_service::Configuration,
473
		relay_chain_args: impl Iterator<Item = &'a String>,
474
	) -> Self {
475
		let extension = chain_spec::Extensions::try_get(&*para_config.chain_spec);
476
		let chain_id = extension.map(|e| e.relay_chain.clone());
477
		let base_path = para_config.base_path.path().join("polkadot");
478
		Self {
479
			base_path,
480
			chain_id,
481
			base: polkadot_cli::RunCmd::parse_from(relay_chain_args),
482
		}
483
	}
484
}