1use {
2 crate::{commands, commands::run::args::pub_sub_config},
3 agave_snapshots::{
4 DEFAULT_ARCHIVE_COMPRESSION, SnapshotVersion,
5 snapshot_config::{
6 DEFAULT_FULL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS,
7 DEFAULT_INCREMENTAL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS,
8 DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
9 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN,
10 },
11 },
12 clap::{App, AppSettings, Arg, ArgMatches, SubCommand, crate_description, crate_name},
13 log::warn,
14 solana_accounts_db::accounts_db::{
15 DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE, DEFAULT_ACCOUNTS_SHRINK_RATIO,
16 },
17 solana_clap_utils::{
18 hidden_unless_forced,
19 input_validators::{
20 is_parsable, is_pubkey, is_pubkey_or_keypair, is_slot, is_url_or_moniker,
21 validate_cpu_ranges,
22 },
23 },
24 solana_clock::Slot,
25 solana_core::banking_trace::BANKING_TRACE_DIR_DEFAULT_BYTE_LIMIT,
26 solana_epoch_schedule::MINIMUM_SLOTS_PER_EPOCH,
27 solana_faucet::faucet::{self, FAUCET_PORT},
28 solana_hash::Hash,
29 solana_net_utils::{MINIMUM_VALIDATOR_PORT_RANGE_WIDTH, VALIDATOR_PORT_RANGE},
30 solana_send_transaction_service::send_transaction_service::{self},
31 solana_streamer::quic::{
32 DEFAULT_MAX_CONNECTIONS_PER_IPADDR_PER_MINUTE,
33 DEFAULT_MAX_QUIC_CONNECTIONS_PER_STAKED_PEER,
34 DEFAULT_MAX_QUIC_CONNECTIONS_PER_UNSTAKED_PEER, DEFAULT_MAX_STAKED_CONNECTIONS,
35 DEFAULT_MAX_STREAMS_PER_MS, DEFAULT_MAX_UNSTAKED_CONNECTIONS, DEFAULT_QUIC_ENDPOINTS,
36 },
37 solana_tpu_client::tpu_client::DEFAULT_VOTE_USE_QUIC,
38 std::{cmp::Ordering, path::PathBuf, str::FromStr},
39};
40
41pub mod thread_args;
42use {
43 solana_core::banking_stage::BankingStage,
44 thread_args::{DefaultThreadArgs, thread_args},
45};
46
47const DEFAULT_MIN_SNAPSHOT_DOWNLOAD_SPEED: u64 = 10485760;
49const MAX_SNAPSHOT_DOWNLOAD_ABORT: u32 = 5;
51const MINIMUM_TICKS_PER_SLOT: u64 = 2;
54
55pub fn app<'a>(version: &'a str, default_args: &'a DefaultArgs) -> App<'a, 'a> {
56 let app = App::new(crate_name!())
57 .about(crate_description!())
58 .version(version)
59 .global_setting(AppSettings::ColoredHelp)
60 .global_setting(AppSettings::InferSubcommands)
61 .global_setting(AppSettings::UnifiedHelpMessage)
62 .global_setting(AppSettings::VersionlessSubcommands)
63 .subcommand(commands::exit::command())
64 .subcommand(commands::authorized_voter::command())
65 .subcommand(commands::contact_info::command())
66 .subcommand(commands::repair_shred_from_peer::command())
67 .subcommand(commands::repair_whitelist::command())
68 .subcommand(
69 SubCommand::with_name("init").about("Initialize the ledger directory then exit"),
70 )
71 .subcommand(commands::monitor::command())
72 .subcommand(SubCommand::with_name("run").about("Run the validator"))
73 .subcommand(commands::plugin::command())
74 .subcommand(commands::set_identity::command())
75 .subcommand(commands::set_log_filter::command())
76 .subcommand(commands::staked_nodes_overrides::command())
77 .subcommand(commands::wait_for_restart_window::command())
78 .subcommand(commands::set_public_address::command())
79 .subcommand(commands::manage_block_production::command(default_args))
80 .subcommand(commands::blockstore::command());
81
82 commands::run::add_args(app, default_args)
83 .args(&thread_args(&default_args.thread_args))
84 .args(&get_deprecated_arguments())
85 .after_help("The default subcommand is run")
86}
87
88struct DeprecatedArg {
91 arg: Arg<'static, 'static>,
96
97 replaced_by: Option<&'static str>,
101
102 usage_warning: Option<&'static str>,
106}
107
108fn deprecated_arguments() -> Vec<DeprecatedArg> {
109 let mut res = vec![];
110
111 macro_rules! add_arg {
113 (
114 $arg:expr
115 $( , replaced_by: $replaced_by:expr )?
116 $( , usage_warning: $usage_warning:expr )?
117 $(,)?
118 ) => {
119 let replaced_by = add_arg!(@into-option $( $replaced_by )?);
120 let usage_warning = add_arg!(@into-option $( $usage_warning )?);
121 res.push(DeprecatedArg {
122 arg: $arg,
123 replaced_by,
124 usage_warning,
125 });
126 };
127
128 (@into-option) => { None };
129 (@into-option $v:expr) => { Some($v) };
130 }
131
132 add_arg!(
133 Arg::with_name("account_shrink_path")
135 .long("account-shrink-path")
136 .value_name("PATH")
137 .takes_value(true)
138 .multiple(true)
139 .help("Path to accounts shrink path which can hold a compacted account set."),
140 );
141 add_arg!(
142 Arg::with_name("enable_accounts_disk_index")
144 .long("enable-accounts-disk-index")
145 .help("Enables the disk-based accounts index")
146 .conflicts_with("accounts_index_limit"),
147 replaced_by: "accounts-index-limit",
148 );
149 add_arg!(
150 Arg::with_name("experimental_retransmit_xdp_cpu_cores")
152 .long("experimental-retransmit-xdp-cpu-cores")
153 .takes_value(true)
154 .value_name("CPU_LIST")
155 .conflicts_with("xdp_cpu_cores")
156 .validator(|value| {
157 validate_cpu_ranges(value, "--experimental-retransmit-xdp-cpu-cores")
158 })
159 .help(
160 "Enable XDP retransmit on the specified CPU cores. Use --xdp-cpu-cores instead",
161 ),
162 replaced_by: "xdp-cpu-cores",
163 );
164 add_arg!(
165 Arg::with_name("experimental_retransmit_xdp_interface")
167 .long("experimental-retransmit-xdp-interface")
168 .takes_value(true)
169 .value_name("INTERFACE")
170 .conflicts_with("xdp_interface")
171 .requires("experimental_retransmit_xdp_cpu_cores")
172 .help("Network interface to use for XDP retransmit. Use --xdp-interface instead"),
173 replaced_by: "xdp-interface",
174 );
175 add_arg!(
176 Arg::with_name("experimental_retransmit_xdp_zero_copy")
178 .long("experimental-retransmit-xdp-zero-copy")
179 .takes_value(false)
180 .conflicts_with("xdp_zero_copy")
181 .requires("experimental_retransmit_xdp_cpu_cores")
182 .help("Enable XDP zero copy. Use --xdp-zero-copy instead"),
183 replaced_by: "xdp-zero-copy",
184 );
185 add_arg!(
186 Arg::with_name("tpu_connection_pool_size")
188 .long("tpu-connection-pool-size")
189 .takes_value(true)
190 .validator(is_parsable::<usize>)
191 .help("Controls the TPU connection pool size per remote address"),
192 usage_warning:"This parameter is misleading, avoid setting it",
193 );
194 res
195}
196
197fn get_deprecated_arguments() -> Vec<Arg<'static, 'static>> {
200 deprecated_arguments()
201 .into_iter()
202 .map(|info| {
203 let arg = info.arg;
204 arg.hidden(hidden_unless_forced())
206 })
207 .collect()
208}
209
210pub fn warn_for_deprecated_arguments(matches: &ArgMatches) {
211 for DeprecatedArg {
212 arg,
213 replaced_by,
214 usage_warning,
215 } in deprecated_arguments().into_iter()
216 {
217 if matches.is_present(arg.b.name) {
218 let mut msg = format!("--{} is deprecated", arg.b.name.replace('_', "-"));
219 if let Some(replaced_by) = replaced_by {
220 msg.push_str(&format!(", please use --{replaced_by}"));
221 }
222 msg.push('.');
223 if let Some(usage_warning) = usage_warning {
224 msg.push_str(&format!(" {usage_warning}"));
225 if !msg.ends_with('.') {
226 msg.push('.');
227 }
228 }
229 warn!("{msg}");
230 }
231 }
232}
233
234pub struct DefaultArgs {
235 pub bind_address: String,
236 pub dynamic_port_range: String,
237 pub ledger_path: String,
238
239 pub tower_storage: String,
240 pub send_transaction_service_config: send_transaction_service::Config,
241
242 pub maximum_local_snapshot_age: String,
243 pub maximum_full_snapshot_archives_to_retain: String,
244 pub maximum_incremental_snapshot_archives_to_retain: String,
245 pub snapshot_packager_niceness_adjustment: String,
246 pub full_snapshot_archive_interval_slots: String,
247 pub incremental_snapshot_archive_interval_slots: String,
248 pub min_snapshot_download_speed: String,
249 pub max_snapshot_download_abort: String,
250
251 pub contact_debug_interval: String,
252
253 pub snapshot_version: SnapshotVersion,
254 pub snapshot_archive_format: String,
255 pub snapshot_zstd_compression_level: String,
256
257 pub accounts_shrink_optimize_total_space: String,
258 pub accounts_shrink_ratio: String,
259
260 pub tpu_max_connections_per_unstaked_peer: String,
261 pub tpu_max_connections_per_staked_peer: String,
262 pub tpu_max_connections_per_ipaddr_per_minute: String,
263 pub tpu_max_staked_connections: String,
264 pub tpu_max_unstaked_connections: String,
265 pub tpu_max_fwd_staked_connections: String,
266 pub tpu_max_fwd_unstaked_connections: String,
267 pub tpu_max_streams_per_ms: String,
268
269 pub num_quic_endpoints: String,
270 pub vote_use_quic: String,
271
272 pub banking_trace_dir_byte_limit: String,
273 pub block_production_pacing_fill_time_millis: String,
274
275 pub thread_args: DefaultThreadArgs,
276}
277
278impl DefaultArgs {
279 pub fn new() -> Self {
280 DefaultArgs {
281 bind_address: "0.0.0.0".to_string(),
282 ledger_path: "ledger".to_string(),
283 dynamic_port_range: format!("{}-{}", VALIDATOR_PORT_RANGE.0, VALIDATOR_PORT_RANGE.1),
284 maximum_local_snapshot_age: "2500".to_string(),
285 tower_storage: "file".to_string(),
286 send_transaction_service_config: send_transaction_service::Config::default(),
287 maximum_full_snapshot_archives_to_retain: DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN
288 .to_string(),
289 maximum_incremental_snapshot_archives_to_retain:
290 DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN.to_string(),
291 snapshot_packager_niceness_adjustment: "0".to_string(),
292 full_snapshot_archive_interval_slots: DEFAULT_FULL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS
293 .get()
294 .to_string(),
295 incremental_snapshot_archive_interval_slots:
296 DEFAULT_INCREMENTAL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS
297 .get()
298 .to_string(),
299 min_snapshot_download_speed: DEFAULT_MIN_SNAPSHOT_DOWNLOAD_SPEED.to_string(),
300 max_snapshot_download_abort: MAX_SNAPSHOT_DOWNLOAD_ABORT.to_string(),
301 snapshot_archive_format: DEFAULT_ARCHIVE_COMPRESSION.to_string(),
302 snapshot_zstd_compression_level: "1".to_string(), contact_debug_interval: "120000".to_string(),
304 snapshot_version: SnapshotVersion::default(),
305 accounts_shrink_optimize_total_space: DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE
306 .to_string(),
307 accounts_shrink_ratio: DEFAULT_ACCOUNTS_SHRINK_RATIO.to_string(),
308 tpu_max_connections_per_ipaddr_per_minute:
309 DEFAULT_MAX_CONNECTIONS_PER_IPADDR_PER_MINUTE.to_string(),
310 vote_use_quic: DEFAULT_VOTE_USE_QUIC.to_string(),
311 tpu_max_connections_per_unstaked_peer: DEFAULT_MAX_QUIC_CONNECTIONS_PER_UNSTAKED_PEER
312 .to_string(),
313 tpu_max_connections_per_staked_peer: DEFAULT_MAX_QUIC_CONNECTIONS_PER_STAKED_PEER
314 .to_string(),
315 tpu_max_staked_connections: DEFAULT_MAX_STAKED_CONNECTIONS.to_string(),
316 tpu_max_unstaked_connections: DEFAULT_MAX_UNSTAKED_CONNECTIONS.to_string(),
317 tpu_max_fwd_staked_connections: DEFAULT_MAX_STAKED_CONNECTIONS
318 .saturating_add(DEFAULT_MAX_UNSTAKED_CONNECTIONS)
319 .to_string(),
320 tpu_max_fwd_unstaked_connections: 0.to_string(),
321 tpu_max_streams_per_ms: DEFAULT_MAX_STREAMS_PER_MS.to_string(),
322 num_quic_endpoints: DEFAULT_QUIC_ENDPOINTS.to_string(),
323 banking_trace_dir_byte_limit: BANKING_TRACE_DIR_DEFAULT_BYTE_LIMIT.to_string(),
324 block_production_pacing_fill_time_millis: BankingStage::default_fill_time_millis()
325 .to_string(),
326 thread_args: DefaultThreadArgs::default(),
327 }
328 }
329}
330
331impl Default for DefaultArgs {
332 fn default() -> Self {
333 Self::new()
334 }
335}
336
337pub fn port_validator(port: String) -> Result<(), String> {
338 port.parse::<u16>()
339 .map(|_| ())
340 .map_err(|e| format!("{e:?}"))
341}
342
343pub fn port_range_validator(port_range: String) -> Result<(), String> {
344 if let Some((start, end)) = solana_net_utils::parse_port_range(&port_range) {
345 if end - start < MINIMUM_VALIDATOR_PORT_RANGE_WIDTH {
346 Err(format!(
347 "Port range is too small. Try --dynamic-port-range {}-{}",
348 start,
349 start + MINIMUM_VALIDATOR_PORT_RANGE_WIDTH
350 ))
351 } else {
352 Ok(())
353 }
354 } else {
355 Err("Invalid port range".to_string())
356 }
357}
358
359pub(crate) fn hash_validator(hash: String) -> Result<(), String> {
360 Hash::from_str(&hash)
361 .map(|_| ())
362 .map_err(|e| format!("{e:?}"))
363}
364
365pub fn test_app<'a>(version: &'a str, default_args: &'a DefaultTestArgs) -> App<'a, 'a> {
367 App::new("solana-test-validator")
368 .about("Test Validator")
369 .version(version)
370 .arg({
371 let arg = Arg::with_name("config_file")
372 .short("C")
373 .long("config")
374 .value_name("PATH")
375 .takes_value(true)
376 .help("Configuration file to use");
377 if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
378 arg.default_value(config_file)
379 } else {
380 arg
381 }
382 })
383 .arg(
384 Arg::with_name("json_rpc_url")
385 .short("u")
386 .long("url")
387 .value_name("URL_OR_MONIKER")
388 .takes_value(true)
389 .validator(is_url_or_moniker)
390 .help(
391 "URL for Solana's JSON RPC or moniker (or their first letter): [mainnet-beta, \
392 testnet, devnet, localhost]",
393 ),
394 )
395 .arg(
396 Arg::with_name("mint_address")
397 .long("mint")
398 .value_name("PUBKEY")
399 .validator(is_pubkey)
400 .takes_value(true)
401 .help(
402 "Address of the mint account that will receive tokens created at genesis. If \
403 the ledger already exists then this parameter is silently ignored [default: \
404 client keypair]",
405 ),
406 )
407 .arg(
408 Arg::with_name("ledger_path")
409 .short("l")
410 .long("ledger")
411 .value_name("DIR")
412 .takes_value(true)
413 .required(true)
414 .default_value("test-ledger")
415 .help("Use DIR as ledger location"),
416 )
417 .arg(
418 Arg::with_name("reset")
419 .short("r")
420 .long("reset")
421 .takes_value(false)
422 .help(
423 "Reset the ledger to genesis if it exists. By default the validator will \
424 resume an existing ledger (if present)",
425 ),
426 )
427 .arg(
428 Arg::with_name("quiet")
429 .short("q")
430 .long("quiet")
431 .takes_value(false)
432 .conflicts_with("log")
433 .help("Quiet mode: suppress normal output"),
434 )
435 .arg(
436 Arg::with_name("log")
437 .long("log")
438 .takes_value(false)
439 .conflicts_with("quiet")
440 .help("Log mode: stream the validator log"),
441 )
442 .arg(
443 Arg::with_name("account_indexes")
444 .long("account-index")
445 .takes_value(true)
446 .multiple(true)
447 .possible_values(&["program-id", "spl-token-owner", "spl-token-mint"])
448 .value_name("INDEX")
449 .help("Enable an accounts index, indexed by the selected account field"),
450 )
451 .arg(
452 Arg::with_name("faucet_port")
453 .long("faucet-port")
454 .value_name("PORT")
455 .takes_value(true)
456 .default_value(&default_args.faucet_port)
457 .validator(port_validator)
458 .help("Enable the faucet on this port"),
459 )
460 .arg(
461 Arg::with_name("rpc_port")
462 .long("rpc-port")
463 .value_name("PORT")
464 .takes_value(true)
465 .default_value(&default_args.rpc_port)
466 .validator(port_validator)
467 .help("Enable JSON RPC on this port, and the next port for the RPC websocket"),
468 )
469 .arg(
470 Arg::with_name("enable_rpc_bigtable_ledger_storage")
471 .long("enable-rpc-bigtable-ledger-storage")
472 .takes_value(false)
473 .hidden(hidden_unless_forced())
474 .help(
475 "Fetch historical transaction info from a BigTable instance as a fallback to \
476 local ledger data",
477 ),
478 )
479 .arg(
480 Arg::with_name("enable_bigtable_ledger_upload")
481 .long("enable-bigtable-ledger-upload")
482 .takes_value(false)
483 .hidden(hidden_unless_forced())
484 .help("Upload new confirmed blocks into a BigTable instance"),
485 )
486 .arg(
487 Arg::with_name("rpc_bigtable_instance")
488 .long("rpc-bigtable-instance")
489 .value_name("INSTANCE_NAME")
490 .takes_value(true)
491 .hidden(hidden_unless_forced())
492 .default_value("solana-ledger")
493 .help("Name of BigTable instance to target"),
494 )
495 .arg(
496 Arg::with_name("rpc_bigtable_app_profile_id")
497 .long("rpc-bigtable-app-profile-id")
498 .value_name("APP_PROFILE_ID")
499 .takes_value(true)
500 .hidden(hidden_unless_forced())
501 .default_value(solana_storage_bigtable::DEFAULT_APP_PROFILE_ID)
502 .help("Application profile id to use in Bigtable requests"),
503 )
504 .arg(
505 Arg::with_name("bpf_program")
506 .long("bpf-program")
507 .value_names(&["ADDRESS_OR_KEYPAIR", "SBF_PROGRAM.SO"])
508 .takes_value(true)
509 .number_of_values(2)
510 .multiple(true)
511 .help(
512 "Add a SBF program to the genesis configuration with upgrades disabled. If \
513 the ledger already exists then this parameter is silently ignored. The first \
514 argument can be a pubkey string or path to a keypair",
515 ),
516 )
517 .arg(
518 Arg::with_name("upgradeable_program")
519 .long("upgradeable-program")
520 .value_names(&["ADDRESS_OR_KEYPAIR", "SBF_PROGRAM.SO", "UPGRADE_AUTHORITY"])
521 .takes_value(true)
522 .number_of_values(3)
523 .multiple(true)
524 .help(
525 "Add an upgradeable SBF program to the genesis configuration. If the ledger \
526 already exists then this parameter is silently ignored. First and third \
527 arguments can be a pubkey string or path to a keypair. Upgrade authority set \
528 to \"none\" disables upgrades",
529 ),
530 )
531 .arg(
532 Arg::with_name("account")
533 .long("account")
534 .value_names(&["ADDRESS", "DUMP.JSON"])
535 .takes_value(true)
536 .number_of_values(2)
537 .allow_hyphen_values(true)
538 .multiple(true)
539 .help(
540 "Load an account from the provided JSON file (see `solana account --help` on \
541 how to dump an account to file). Files are searched for relatively to CWD \
542 and tests/fixtures. If ADDRESS is omitted via the `-` placeholder, the one \
543 in the file will be used. If the ledger already exists then this parameter \
544 is silently ignored",
545 ),
546 )
547 .arg(
548 Arg::with_name("account_dir")
549 .long("account-dir")
550 .value_name("DIRECTORY")
551 .validator(|value| {
552 value
553 .parse::<PathBuf>()
554 .map_err(|err| format!("error parsing '{value}': {err}"))
555 .and_then(|path| {
556 if path.exists() && path.is_dir() {
557 Ok(())
558 } else {
559 Err(format!(
560 "path does not exist or is not a directory: {value}"
561 ))
562 }
563 })
564 })
565 .takes_value(true)
566 .multiple(true)
567 .help(
568 "Load all the accounts from the JSON files found in the specified DIRECTORY \
569 (see also the `--account` flag). If the ledger already exists then this \
570 parameter is silently ignored",
571 ),
572 )
573 .arg(
574 Arg::with_name("ticks_per_slot")
575 .long("ticks-per-slot")
576 .value_name("TICKS")
577 .validator(|value| {
578 value
579 .parse::<u64>()
580 .map_err(|err| format!("error parsing '{value}': {err}"))
581 .and_then(|ticks| {
582 if ticks < MINIMUM_TICKS_PER_SLOT {
583 Err(format!("value must be >= {MINIMUM_TICKS_PER_SLOT}"))
584 } else {
585 Ok(())
586 }
587 })
588 })
589 .takes_value(true)
590 .help("The number of ticks in a slot"),
591 )
592 .arg(
593 Arg::with_name("slots_per_epoch")
594 .long("slots-per-epoch")
595 .value_name("SLOTS")
596 .validator(|value| {
597 value
598 .parse::<Slot>()
599 .map_err(|err| format!("error parsing '{value}': {err}"))
600 .and_then(|slot| {
601 if slot < MINIMUM_SLOTS_PER_EPOCH {
602 Err(format!("value must be >= {MINIMUM_SLOTS_PER_EPOCH}"))
603 } else {
604 Ok(())
605 }
606 })
607 })
608 .takes_value(true)
609 .help(
610 "Override the number of slots in an epoch. If the ledger already exists then \
611 this parameter is silently ignored",
612 ),
613 )
614 .arg(
615 Arg::with_name("inflation_fixed")
616 .long("inflation-fixed")
617 .value_name("RATE")
618 .validator(|value| {
619 value
620 .parse::<f64>()
621 .map_err(|err| format!("error parsing '{value}': {err}"))
622 .and_then(|rate| match rate.partial_cmp(&0.0) {
623 Some(Ordering::Greater) | Some(Ordering::Equal) => Ok(()),
624 Some(Ordering::Less) | None => Err(String::from("value must be >= 0")),
625 })
626 })
627 .takes_value(true)
628 .allow_hyphen_values(true)
629 .help(
630 "Override default inflation with fixed rate. If the ledger already exists \
631 then this parameter is silently ignored",
632 ),
633 )
634 .arg(
635 Arg::with_name("gossip_port")
636 .long("gossip-port")
637 .value_name("PORT")
638 .takes_value(true)
639 .help("Gossip port number for the validator"),
640 )
641 .arg(
642 Arg::with_name("dynamic_port_range")
643 .long("dynamic-port-range")
644 .value_name("MIN_PORT-MAX_PORT")
645 .takes_value(true)
646 .validator(port_range_validator)
647 .help("Range to use for dynamically assigned ports [default: 1024-65535]"),
648 )
649 .arg(
650 Arg::with_name("bind_address")
651 .long("bind-address")
652 .value_name("HOST")
653 .takes_value(true)
654 .validator(solana_net_utils::is_host)
655 .default_value("127.0.0.1")
656 .help(
657 "IPv4 address to bind the validator ports. Can be repeated. The first \
658 --bind-address MUST be your public internet address. ALL protocols (gossip, \
659 repair, IP echo, TVU, TPU, etc.) bind to this address on startup. Additional \
660 --bind-address values enable multihoming for Gossip/TVU/TPU - these \
661 protocols bind to ALL interfaces on startup. Gossip reads/sends from one \
662 interface at a time. TVU/TPU read from ALL interfaces simultaneously but \
663 send from only one interface at a time. When switching interfaces via \
664 AdminRPC: Gossip switches to send/receive from the new interface, while \
665 TVU/TPU continue receiving from ALL interfaces but send from the new \
666 interface only.",
667 ),
668 )
669 .arg(
670 Arg::with_name("advertised_ip")
671 .long("advertised-ip")
672 .value_name("HOST")
673 .takes_value(true)
674 .validator(solana_net_utils::is_host)
675 .hidden(hidden_unless_forced())
676 .help(
677 "Use when running a validator behind a NAT. DNS name or IP address for this \
678 validator to advertise in gossip. This address will be used as the target \
679 destination address for peers trying to contact this node. [default: the \
680 first --bind-address, or ask --entrypoint when --bind-address is not \
681 provided, or 127.0.0.1 when --entrypoint is not provided]. Note: this \
682 argument cannot be used in a multihoming context (when multiple \
683 --bind-address values are provided).",
684 ),
685 )
686 .arg(
687 Arg::with_name("clone_account")
688 .long("clone")
689 .short("c")
690 .value_name("ADDRESS")
691 .takes_value(true)
692 .validator(is_pubkey_or_keypair)
693 .multiple(true)
694 .requires("json_rpc_url")
695 .help(
696 "Copy an account from the cluster referenced by the --url argument the \
697 genesis configuration. If the ledger already exists then this parameter is \
698 silently ignored",
699 ),
700 )
701 .arg(
702 Arg::with_name("deep_clone_address_lookup_table")
703 .long("deep-clone-address-lookup-table")
704 .takes_value(true)
705 .validator(is_pubkey_or_keypair)
706 .multiple(true)
707 .requires("json_rpc_url")
708 .help(
709 "Copy an address lookup table and all accounts it references from the cluster \
710 referenced by the --url argument in the genesis configuration. If the ledger \
711 already exists then this parameter is silently ignored",
712 ),
713 )
714 .arg(
715 Arg::with_name("maybe_clone_account")
716 .long("maybe-clone")
717 .value_name("ADDRESS")
718 .takes_value(true)
719 .validator(is_pubkey_or_keypair)
720 .multiple(true)
721 .requires("json_rpc_url")
722 .help(
723 "Copy an account from the cluster referenced by the --url argument, skipping \
724 it if it doesn't exist. If the ledger already exists then this parameter is \
725 silently ignored",
726 ),
727 )
728 .arg(
729 Arg::with_name("clone_upgradeable_program")
730 .long("clone-upgradeable-program")
731 .value_name("ADDRESS")
732 .takes_value(true)
733 .validator(is_pubkey_or_keypair)
734 .multiple(true)
735 .requires("json_rpc_url")
736 .help(
737 "Copy an upgradeable program and its executable data from the cluster \
738 referenced by the --url argument the genesis configuration. If the ledger \
739 already exists then this parameter is silently ignored",
740 ),
741 )
742 .arg(
743 Arg::with_name("warp_slot")
744 .required(false)
745 .long("warp-slot")
746 .short("w")
747 .takes_value(true)
748 .value_name("WARP_SLOT")
749 .validator(is_slot)
750 .min_values(0)
751 .max_values(1)
752 .help(
753 "Warp the ledger to WARP_SLOT after starting the validator. If no slot is \
754 provided then the current slot of the cluster referenced by the --url \
755 argument will be used",
756 ),
757 )
758 .arg(
759 Arg::with_name("limit_ledger_size")
760 .long("limit-ledger-size")
761 .value_name("SHRED_COUNT")
762 .takes_value(true)
763 .default_value(default_args.limit_ledger_size.as_str())
764 .help("Keep this amount of shreds in root slots."),
765 )
766 .arg(
767 Arg::with_name("faucet_sol")
768 .long("faucet-sol")
769 .takes_value(true)
770 .value_name("SOL")
771 .default_value(default_args.faucet_sol.as_str())
772 .help(
773 "Give the faucet address this much SOL in genesis. If the ledger already \
774 exists then this parameter is silently ignored",
775 ),
776 )
777 .arg(
778 Arg::with_name("faucet_time_slice_secs")
779 .long("faucet-time-slice-secs")
780 .takes_value(true)
781 .value_name("SECS")
782 .default_value(default_args.faucet_time_slice_secs.as_str())
783 .help("Time slice (in secs) over which to limit faucet requests"),
784 )
785 .arg(
786 Arg::with_name("faucet_per_time_sol_cap")
787 .long("faucet-per-time-sol-cap")
788 .takes_value(true)
789 .value_name("SOL")
790 .min_values(0)
791 .max_values(1)
792 .help("Per-time slice limit for faucet requests, in SOL"),
793 )
794 .arg(
795 Arg::with_name("faucet_per_request_sol_cap")
796 .long("faucet-per-request-sol-cap")
797 .takes_value(true)
798 .value_name("SOL")
799 .min_values(0)
800 .max_values(1)
801 .help("Per-request limit for faucet requests, in SOL"),
802 )
803 .arg(
804 Arg::with_name("geyser_plugin_config")
805 .long("geyser-plugin-config")
806 .alias("accountsdb-plugin-config")
807 .value_name("FILE")
808 .takes_value(true)
809 .multiple(true)
810 .help("Specify the configuration file for the Geyser plugin."),
811 )
812 .arg(
813 Arg::with_name("enable_scheduler_bindings")
814 .long("enable-scheduler-bindings")
815 .takes_value(false)
816 .help("Enables external processes to connect and manage block production"),
817 )
818 .arg(
819 Arg::with_name("deactivate_feature")
820 .long("deactivate-feature")
821 .takes_value(true)
822 .value_name("FEATURE_PUBKEY")
823 .validator(is_pubkey)
824 .multiple(true)
825 .help("deactivate this feature in genesis."),
826 )
827 .arg(
828 Arg::with_name("compute_unit_limit")
829 .long("compute-unit-limit")
830 .alias("max-compute-units")
831 .value_name("COMPUTE_UNITS")
832 .validator(is_parsable::<u64>)
833 .takes_value(true)
834 .help("Override the runtime's compute unit limit per transaction"),
835 )
836 .arg(
837 Arg::with_name("log_messages_bytes_limit")
838 .long("log-messages-bytes-limit")
839 .value_name("BYTES")
840 .validator(is_parsable::<usize>)
841 .takes_value(true)
842 .help("Maximum number of bytes written to the program log before truncation"),
843 )
844 .arg(
845 Arg::with_name("transaction_account_lock_limit")
846 .long("transaction-account-lock-limit")
847 .value_name("NUM_ACCOUNTS")
848 .validator(is_parsable::<u64>)
849 .takes_value(true)
850 .help("Override the runtime's account lock limit per transaction"),
851 )
852 .arg(
853 Arg::with_name("clone_feature_set")
854 .long("clone-feature-set")
855 .takes_value(false)
856 .requires("json_rpc_url")
857 .help(
858 "Copy a feature set from the cluster referenced by the --url argument in the \
859 genesis configuration. If the ledger already exists then this parameter is \
860 silently ignored",
861 ),
862 )
863 .args(&pub_sub_config::args(true))
864}
865
866pub struct DefaultTestArgs {
867 pub rpc_port: String,
868 pub faucet_port: String,
869 pub limit_ledger_size: String,
870 pub faucet_sol: String,
871 pub faucet_time_slice_secs: String,
872}
873
874impl DefaultTestArgs {
875 pub fn new() -> Self {
876 DefaultTestArgs {
877 rpc_port: 8899.to_string(),
878 faucet_port: FAUCET_PORT.to_string(),
879 limit_ledger_size: 10_000.to_string(),
884 faucet_sol: (1_000_000.).to_string(),
885 faucet_time_slice_secs: (faucet::TIME_SLICE).to_string(),
886 }
887 }
888}
889
890impl Default for DefaultTestArgs {
891 fn default() -> Self {
892 Self::new()
893 }
894}
895
896#[cfg(test)]
897mod test {
898 use super::*;
899
900 #[test]
901 fn make_sure_deprecated_arguments_are_sorted_alphabetically() {
902 let deprecated = deprecated_arguments();
903
904 for i in 0..deprecated.len().saturating_sub(1) {
905 let curr_name = deprecated[i].arg.b.name;
906 let next_name = deprecated[i + 1].arg.b.name;
907
908 assert!(
909 curr_name != next_name,
910 "Arguments in `deprecated_arguments()` should be distinct.\nArguments {} and {} \
911 use the same name: {}",
912 i,
913 i + 1,
914 curr_name,
915 );
916
917 assert!(
918 curr_name < next_name,
919 "To generate better diffs and for readability purposes, `deprecated_arguments()` \
920 should list arguments in alphabetical order.\nArguments {} and {} are \
921 not.\nArgument {} name: {}\nArgument {} name: {}",
922 i,
923 i + 1,
924 i,
925 curr_name,
926 i + 1,
927 next_name,
928 );
929 }
930 }
931}