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::{account_key::GenerateAccountKey, EthApi, FrontierBackendType, Sealing};
24
use moonbeam_service::chain_spec;
25
use sc_cli::{Error as CliError, SubstrateCli};
26
use std::path::PathBuf;
27
use std::time::Duration;
28
use url::Url;
29

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

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

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

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

            
54
	/// Export the genesis wasm of the parachain.
55
	#[clap(name = "export-genesis-wasm")]
56
	ExportGenesisWasm(ExportGenesisWasmCommand),
57

            
58
	/// Build a chain specification.
59
	BuildSpec(BuildSpecCommand),
60

            
61
	/// Validate blocks.
62
	CheckBlock(sc_cli::CheckBlockCmd),
63

            
64
	/// Export blocks.
65
	ExportBlocks(sc_cli::ExportBlocksCmd),
66

            
67
	/// Export the state of a given block into a chain spec.
68
	ExportState(sc_cli::ExportStateCmd),
69

            
70
	/// Import blocks.
71
	ImportBlocks(sc_cli::ImportBlocksCmd),
72

            
73
	/// Remove the whole chain.
74
	PurgeChain(cumulus_client_cli::PurgeChainCmd),
75

            
76
	/// Revert the chain to a previous state.
77
	Revert(sc_cli::RevertCmd),
78

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

            
84
	/// Try some command against runtime state.
85
	TryRuntime,
86

            
87
	/// Key management cli utilities
88
	#[clap(subcommand)]
89
	Key(KeyCmd),
90

            
91
	/// Precompile the WASM runtime into native code
92
	PrecompileWasm(sc_cli::PrecompileWasmCmd),
93
}
94

            
95
#[derive(Debug, Parser)]
96
pub struct BuildSpecCommand {
97
	#[clap(flatten)]
98
	pub base: sc_cli::BuildSpecCmd,
99

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

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

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

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

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

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

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

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

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

            
147
#[derive(Debug, Parser)]
148
#[group(skip)]
149
pub struct RunCmd {
150
	#[clap(flatten)]
151
	pub base: cumulus_client_cli::RunCmd,
152

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

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

            
162
	/// Enable the legacy block import strategy
163
	#[clap(long)]
164
	pub legacy_block_import_strategy: bool,
165

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

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

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

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

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

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

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

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

            
241
	/// Enable EVM tracing module on a non-authority node.
242
	#[clap(long, value_delimiter = ',')]
243
930
	pub ethapi: Vec<EthApi>,
244

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

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

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

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

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

            
268
	/// Sets the frontier backend type (KeyValue or Sql)
269
936
	#[arg(long, value_enum, ignore_case = true, default_value_t = FrontierBackendType::default())]
270
	pub frontier_backend_type: FrontierBackendType,
271

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

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

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

            
284
	/// Sets the SQL backend's query timeout in number of VM ops.
285
	/// Default value is 200MB.
286
	#[arg(long, default_value = "209715200")]
287
	pub frontier_sql_backend_cache_size: u64,
288

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

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

            
298
	/// Force using Moonbase native runtime.
299
	#[clap(long = "force-moonbase")]
300
	pub force_moonbase: bool,
301

            
302
	/// Force using Moonriver native runtime.
303
	#[clap(long = "force-moonriver")]
304
	pub force_moonriver: bool,
305

            
306
	/// Id of the parachain this collator collates for.
307
	#[clap(long)]
308
	pub parachain_id: Option<u32>,
309

            
310
	/// Maximum fee history cache size.
311
	#[clap(long, default_value = "2048")]
312
	pub fee_history_limit: u64,
313

            
314
	/// Disable automatic hardware benchmarks.
315
	///
316
	/// By default these benchmarks are automatically ran at startup and measure
317
	/// the CPU speed, the memory bandwidth and the disk speed.
318
	///
319
	/// The results are then printed out in the logs, and also sent as part of
320
	/// telemetry, if telemetry is enabled.
321
	#[clap(long)]
322
	pub no_hardware_benchmarks: bool,
323

            
324
	/// Removes moonbeam prefix from Prometheus metrics
325
	#[clap(long)]
326
	pub no_prometheus_prefix: bool,
327

            
328
	/// Maximum duration in milliseconds to produce a block
329
	#[clap(long, default_value = "2000", value_parser=block_authoring_duration_parser)]
330
	pub block_authoring_duration: Duration,
331

            
332
	/// Enable full proof-of-validation mode for Nimbus
333
	#[clap(long)]
334
	pub nimbus_full_pov: bool,
335
}
336

            
337
936
fn block_authoring_duration_parser(s: &str) -> Result<Duration, String> {
338
936
	Ok(Duration::from_millis(clap_num::number_range(
339
936
		s, 250, 2_000,
340
936
	)?))
341
936
}
342

            
343
impl RunCmd {
344
932
	pub fn new_rpc_config(&self) -> moonbeam_cli_opt::RpcConfig {
345
932
		moonbeam_cli_opt::RpcConfig {
346
932
			ethapi: self.ethapi.clone(),
347
932
			ethapi_max_permits: self.ethapi_max_permits,
348
932
			ethapi_trace_max_count: self.ethapi_trace_max_count,
349
932
			ethapi_trace_cache_duration: self.ethapi_trace_cache_duration,
350
932
			eth_log_block_cache: self.eth_log_block_cache,
351
932
			eth_statuses_cache: self.eth_statuses_cache,
352
932
			fee_history_limit: self.fee_history_limit,
353
932
			max_past_logs: self.max_past_logs,
354
932
			relay_chain_rpc_urls: self.base.relay_chain_rpc_urls.clone(),
355
932
			tracing_raw_max_memory_usage: self.tracing_raw_max_memory_usage,
356
932
			frontier_backend_config: match self.frontier_backend_type {
357
932
				FrontierBackendType::KeyValue => moonbeam_cli_opt::FrontierBackendConfig::KeyValue,
358
				FrontierBackendType::Sql => moonbeam_cli_opt::FrontierBackendConfig::Sql {
359
					pool_size: self.frontier_sql_backend_pool_size,
360
					num_ops_timeout: self.frontier_sql_backend_num_ops_timeout,
361
					thread_count: self.frontier_sql_backend_thread_count,
362
					cache_size: self.frontier_sql_backend_cache_size,
363
				},
364
			},
365
932
			no_prometheus_prefix: self.no_prometheus_prefix,
366
932
		}
367
932
	}
368
}
369

            
370
impl std::ops::Deref for RunCmd {
371
	type Target = cumulus_client_cli::RunCmd;
372

            
373
1860
	fn deref(&self) -> &Self::Target {
374
1860
		&self.base
375
1860
	}
376
}
377

            
378
#[derive(Debug, clap::Subcommand)]
379
pub enum KeyCmd {
380
	#[clap(flatten)]
381
	BaseCli(sc_cli::KeySubcommand),
382
	/// Generate an Ethereum account.
383
	GenerateAccountKey(GenerateAccountKey),
384
}
385

            
386
impl KeyCmd {
387
	/// run the key subcommands
388
	pub fn run<C: SubstrateCli>(&self, cli: &C) -> Result<(), CliError> {
389
		match self {
390
			KeyCmd::BaseCli(cmd) => cmd.run(cli),
391
			KeyCmd::GenerateAccountKey(cmd) => {
392
				cmd.run();
393
				Ok(())
394
			}
395
		}
396
	}
397
}
398

            
399
#[derive(Debug, Parser)]
400
#[clap(
401
	propagate_version = true,
402
	args_conflicts_with_subcommands = true,
403
	subcommand_negates_reqs = true
404
)]
405
pub struct Cli {
406
	#[clap(subcommand)]
407
	pub subcommand: Option<Subcommand>,
408

            
409
	#[clap(flatten)]
410
	pub run: RunCmd,
411

            
412
	/// Relaychain arguments
413
	#[clap(raw = true)]
414
	pub relaychain_args: Vec<String>,
415
}
416

            
417
#[derive(Debug)]
418
pub struct RelayChainCli {
419
	/// The actual relay chain cli object.
420
	pub base: polkadot_cli::RunCmd,
421

            
422
	/// Optional chain id that should be passed to the relay chain.
423
	pub chain_id: Option<String>,
424

            
425
	/// The base path that should be used by the relay chain.
426
	pub base_path: PathBuf,
427
}
428

            
429
impl RelayChainCli {
430
	/// Parse the relay chain CLI parameters using the para chain `Configuration`.
431
	pub fn new<'a>(
432
		para_config: &sc_service::Configuration,
433
		relay_chain_args: impl Iterator<Item = &'a String>,
434
	) -> Self {
435
		let extension = chain_spec::Extensions::try_get(&*para_config.chain_spec);
436
		let chain_id = extension.map(|e| e.relay_chain.clone());
437
		let base_path = para_config.base_path.path().join("polkadot");
438
		Self {
439
			base_path,
440
			chain_id,
441
			base: polkadot_cli::RunCmd::parse_from(relay_chain_args),
442
		}
443
	}
444
}