agave_validator/commands/run/
args.rs

1use {
2    crate::{
3        bootstrap::RpcBootstrapConfig,
4        cli::{hash_validator, port_range_validator, port_validator, DefaultArgs},
5        commands::{FromClapArgMatches, Result},
6    },
7    clap::{values_t, App, Arg, ArgMatches},
8    solana_clap_utils::{
9        hidden_unless_forced,
10        input_parsers::keypair_of,
11        input_validators::{
12            is_keypair_or_ask_keyword, is_non_zero, is_parsable, is_pow2, is_pubkey,
13            is_pubkey_or_keypair, is_slot, is_within_range, validate_cpu_ranges,
14            validate_maximum_full_snapshot_archives_to_retain,
15            validate_maximum_incremental_snapshot_archives_to_retain,
16        },
17        keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
18    },
19    solana_core::{
20        banking_trace::DirByteLimit,
21        validator::{BlockProductionMethod, BlockVerificationMethod, TransactionStructure},
22    },
23    solana_keypair::Keypair,
24    solana_ledger::{blockstore_options::BlockstoreOptions, use_snapshot_archives_at_startup},
25    solana_pubkey::Pubkey,
26    solana_rpc::{rpc::JsonRpcConfig, rpc_pubsub_service::PubSubConfig},
27    solana_runtime::snapshot_utils::{SnapshotVersion, SUPPORTED_ARCHIVE_COMPRESSION},
28    solana_send_transaction_service::send_transaction_service::{
29        MAX_BATCH_SEND_RATE_MS, MAX_TRANSACTION_BATCH_SIZE,
30    },
31    solana_signer::Signer,
32    solana_streamer::socket::SocketAddrSpace,
33    solana_unified_scheduler_pool::DefaultSchedulerPool,
34    std::{collections::HashSet, net::SocketAddr, str::FromStr},
35};
36
37const EXCLUDE_KEY: &str = "account-index-exclude-key";
38const INCLUDE_KEY: &str = "account-index-include-key";
39
40// Declared out of line to allow use of #[rustfmt::skip]
41#[rustfmt::skip]
42const WEN_RESTART_HELP: &str =
43    "Only used during coordinated cluster restarts.\n\n\
44     Need to also specify the leader's pubkey in --wen-restart-leader.\n\n\
45     When specified, the validator will enter Wen Restart mode which pauses normal activity. \
46     Validators in this mode will gossip their last vote to reach consensus on a safe restart \
47     slot and repair all blocks on the selected fork. The safe slot will be a descendant of the \
48     latest optimistically confirmed slot to ensure we do not roll back any optimistically \
49     confirmed slots.\n\n\
50     The progress in this mode will be saved in the file location provided. If consensus is \
51     reached, the validator will automatically exit with 200 status code. Then the operators are \
52     expected to restart the validator with --wait_for_supermajority and other arguments \
53     (including new shred_version, supermajority slot, and bankhash) given in the error log \
54     before the exit so the cluster will resume execution. The progress file will be kept around \
55     for future debugging.\n\n\
56     If wen_restart fails, refer to the progress file (in proto3 format) for further debugging and \
57     watch the discord channel for instructions.";
58
59pub mod account_secondary_indexes;
60pub mod blockstore_options;
61pub mod json_rpc_config;
62pub mod pub_sub_config;
63pub mod rpc_bigtable_config;
64pub mod rpc_bootstrap_config;
65
66#[derive(Debug, PartialEq)]
67pub struct RunArgs {
68    pub identity_keypair: Keypair,
69    pub logfile: String,
70    pub entrypoints: Vec<SocketAddr>,
71    pub known_validators: Option<HashSet<Pubkey>>,
72    pub socket_addr_space: SocketAddrSpace,
73    pub rpc_bootstrap_config: RpcBootstrapConfig,
74    pub blockstore_options: BlockstoreOptions,
75    pub json_rpc_config: JsonRpcConfig,
76    pub pub_sub_config: PubSubConfig,
77}
78
79impl FromClapArgMatches for RunArgs {
80    fn from_clap_arg_match(matches: &ArgMatches) -> Result<Self> {
81        let identity_keypair =
82            keypair_of(matches, "identity").ok_or(clap::Error::with_description(
83                "The --identity <KEYPAIR> argument is required",
84                clap::ErrorKind::ArgumentNotFound,
85            ))?;
86
87        let logfile = matches
88            .value_of("logfile")
89            .map(|s| s.into())
90            .unwrap_or_else(|| format!("agave-validator-{}.log", identity_keypair.pubkey()));
91
92        let mut entrypoints = values_t!(matches, "entrypoint", String).unwrap_or_default();
93        // sort() + dedup() to yield a vector of unique elements
94        entrypoints.sort();
95        entrypoints.dedup();
96        let entrypoints = entrypoints
97            .into_iter()
98            .map(|entrypoint| {
99                solana_net_utils::parse_host_port(&entrypoint).map_err(|err| {
100                    crate::commands::Error::Dynamic(Box::<dyn std::error::Error>::from(format!(
101                        "failed to parse entrypoint address: {err}"
102                    )))
103                })
104            })
105            .collect::<Result<Vec<_>>>()?;
106
107        let known_validators = validators_set(
108            &identity_keypair.pubkey(),
109            matches,
110            "known_validators",
111            "known validator",
112        )?;
113
114        let socket_addr_space = SocketAddrSpace::new(matches.is_present("allow_private_addr"));
115
116        Ok(RunArgs {
117            identity_keypair,
118            logfile,
119            entrypoints,
120            known_validators,
121            socket_addr_space,
122            rpc_bootstrap_config: RpcBootstrapConfig::from_clap_arg_match(matches)?,
123            blockstore_options: BlockstoreOptions::from_clap_arg_match(matches)?,
124            json_rpc_config: JsonRpcConfig::from_clap_arg_match(matches)?,
125            pub_sub_config: PubSubConfig::from_clap_arg_match(matches)?,
126        })
127    }
128}
129
130pub fn add_args<'a>(app: App<'a, 'a>, default_args: &'a DefaultArgs) -> App<'a, 'a> {
131    app.arg(
132        Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
133            .long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
134            .help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
135    )
136    .arg(
137        Arg::with_name("identity")
138            .short("i")
139            .long("identity")
140            .value_name("KEYPAIR")
141            .takes_value(true)
142            .validator(is_keypair_or_ask_keyword)
143            .help("Validator identity keypair"),
144    )
145    .arg(
146        Arg::with_name("authorized_voter_keypairs")
147            .long("authorized-voter")
148            .value_name("KEYPAIR")
149            .takes_value(true)
150            .validator(is_keypair_or_ask_keyword)
151            .requires("vote_account")
152            .multiple(true)
153            .help(
154                "Include an additional authorized voter keypair. May be specified multiple times. \
155                 [default: the --identity keypair]",
156            ),
157    )
158    .arg(
159        Arg::with_name("vote_account")
160            .long("vote-account")
161            .value_name("ADDRESS")
162            .takes_value(true)
163            .validator(is_pubkey_or_keypair)
164            .requires("identity")
165            .help(
166                "Validator vote account public key. If unspecified, voting will be disabled. The \
167                 authorized voter for the account must either be the --identity keypair or set by \
168                 the --authorized-voter argument",
169            ),
170    )
171    .arg(
172        Arg::with_name("init_complete_file")
173            .long("init-complete-file")
174            .value_name("FILE")
175            .takes_value(true)
176            .help(
177                "Create this file if it doesn't already exist once validator initialization is \
178                 complete",
179            ),
180    )
181    .arg(
182        Arg::with_name("ledger_path")
183            .short("l")
184            .long("ledger")
185            .value_name("DIR")
186            .takes_value(true)
187            .required(true)
188            .default_value(&default_args.ledger_path)
189            .help("Use DIR as ledger location"),
190    )
191    .arg(
192        Arg::with_name("entrypoint")
193            .short("n")
194            .long("entrypoint")
195            .value_name("HOST:PORT")
196            .takes_value(true)
197            .multiple(true)
198            .validator(solana_net_utils::is_host_port)
199            .help("Rendezvous with the cluster at this gossip entrypoint"),
200    )
201    .arg(
202        Arg::with_name("no_snapshot_fetch")
203            .long("no-snapshot-fetch")
204            .takes_value(false)
205            .help(
206                "Do not attempt to fetch a snapshot from the cluster, start from a local snapshot \
207                 if present",
208            ),
209    )
210    .arg(
211        Arg::with_name("no_genesis_fetch")
212            .long("no-genesis-fetch")
213            .takes_value(false)
214            .help("Do not fetch genesis from the cluster"),
215    )
216    .arg(
217        Arg::with_name("no_voting")
218            .long("no-voting")
219            .takes_value(false)
220            .help("Launch validator without voting"),
221    )
222    .arg(
223        Arg::with_name("check_vote_account")
224            .long("check-vote-account")
225            .takes_value(true)
226            .value_name("RPC_URL")
227            .requires("entrypoint")
228            .conflicts_with_all(&["no_voting"])
229            .help(
230                "Sanity check vote account state at startup. The JSON RPC endpoint at RPC_URL \
231                 must expose `--full-rpc-api`",
232            ),
233    )
234    .arg(
235        Arg::with_name("restricted_repair_only_mode")
236            .long("restricted-repair-only-mode")
237            .takes_value(false)
238            .help(
239                "Do not publish the Gossip, TPU, TVU or Repair Service ports. Doing so causes the \
240                 node to operate in a limited capacity that reduces its exposure to the rest of \
241                 the cluster. The --no-voting flag is implicit when this flag is enabled",
242            ),
243    )
244    .arg(
245        Arg::with_name("dev_halt_at_slot")
246            .long("dev-halt-at-slot")
247            .value_name("SLOT")
248            .validator(is_slot)
249            .takes_value(true)
250            .help("Halt the validator when it reaches the given slot"),
251    )
252    .arg(
253        Arg::with_name("rpc_port")
254            .long("rpc-port")
255            .value_name("PORT")
256            .takes_value(true)
257            .validator(port_validator)
258            .help("Enable JSON RPC on this port, and the next port for the RPC websocket"),
259    )
260    .arg(
261        Arg::with_name("full_rpc_api")
262            .long("full-rpc-api")
263            .takes_value(false)
264            .help("Expose RPC methods for querying chain state and transaction history"),
265    )
266    .arg(
267        Arg::with_name("private_rpc")
268            .long("private-rpc")
269            .takes_value(false)
270            .help("Do not publish the RPC port for use by others"),
271    )
272    .arg(
273        Arg::with_name("no_port_check")
274            .long("no-port-check")
275            .takes_value(false)
276            .hidden(hidden_unless_forced())
277            .help("Do not perform TCP/UDP reachable port checks at start-up"),
278    )
279    .arg(
280        Arg::with_name("enable_rpc_transaction_history")
281            .long("enable-rpc-transaction-history")
282            .takes_value(false)
283            .help(
284                "Enable historical transaction info over JSON RPC, including the \
285                 'getConfirmedBlock' API. This will cause an increase in disk usage and IOPS",
286            ),
287    )
288    .arg(
289        Arg::with_name("enable_rpc_bigtable_ledger_storage")
290            .long("enable-rpc-bigtable-ledger-storage")
291            .requires("enable_rpc_transaction_history")
292            .takes_value(false)
293            .help(
294                "Fetch historical transaction info from a BigTable instance as a fallback to \
295                 local ledger data",
296            ),
297    )
298    .arg(
299        Arg::with_name("enable_bigtable_ledger_upload")
300            .long("enable-bigtable-ledger-upload")
301            .requires("enable_rpc_transaction_history")
302            .takes_value(false)
303            .help("Upload new confirmed blocks into a BigTable instance"),
304    )
305    .arg(
306        Arg::with_name("enable_extended_tx_metadata_storage")
307            .long("enable-extended-tx-metadata-storage")
308            .requires("enable_rpc_transaction_history")
309            .takes_value(false)
310            .help(
311                "Include CPI inner instructions, logs, and return data in the historical \
312                 transaction info stored",
313            ),
314    )
315    .arg(
316        Arg::with_name("rpc_max_multiple_accounts")
317            .long("rpc-max-multiple-accounts")
318            .value_name("MAX ACCOUNTS")
319            .takes_value(true)
320            .default_value(&default_args.rpc_max_multiple_accounts)
321            .help(
322                "Override the default maximum accounts accepted by the getMultipleAccounts JSON \
323                 RPC method",
324            ),
325    )
326    .arg(
327        Arg::with_name("health_check_slot_distance")
328            .long("health-check-slot-distance")
329            .value_name("SLOT_DISTANCE")
330            .takes_value(true)
331            .default_value(&default_args.health_check_slot_distance)
332            .help(
333                "Report this validator as healthy if its latest replayed optimistically confirmed \
334                 slot is within the specified number of slots from the cluster's latest \
335                 optimistically confirmed slot",
336            ),
337    )
338    .arg(
339        Arg::with_name("skip_preflight_health_check")
340            .long("skip-preflight-health-check")
341            .takes_value(false)
342            .help("Skip health check when running a preflight check"),
343    )
344    .arg(
345        Arg::with_name("rpc_faucet_addr")
346            .long("rpc-faucet-address")
347            .value_name("HOST:PORT")
348            .takes_value(true)
349            .validator(solana_net_utils::is_host_port)
350            .help("Enable the JSON RPC 'requestAirdrop' API with this faucet address."),
351    )
352    .arg(
353        Arg::with_name("account_paths")
354            .long("accounts")
355            .value_name("PATHS")
356            .takes_value(true)
357            .multiple(true)
358            .help(
359                "Comma separated persistent accounts location. May be specified multiple times. \
360                 [default: <LEDGER>/accounts]",
361            ),
362    )
363    .arg(
364        Arg::with_name("account_shrink_path")
365            .long("account-shrink-path")
366            .value_name("PATH")
367            .takes_value(true)
368            .multiple(true)
369            .help("Path to accounts shrink path which can hold a compacted account set."),
370    )
371    .arg(
372        Arg::with_name("snapshots")
373            .long("snapshots")
374            .value_name("DIR")
375            .takes_value(true)
376            .help("Use DIR as the base location for snapshots.")
377            .long_help(
378                "Use DIR as the base location for snapshots. Snapshot archives will use DIR \
379                 unless --full-snapshot-archive-path or --incremental-snapshot-archive-path is \
380                 specified. Additionally, a subdirectory named \"snapshots\" will be created in \
381                 DIR. This subdirectory holds internal files/data that are used when generating \
382                 snapshot archives. [default: --ledger value]",
383            ),
384    )
385    .arg(
386        Arg::with_name(use_snapshot_archives_at_startup::cli::NAME)
387            .long(use_snapshot_archives_at_startup::cli::LONG_ARG)
388            .takes_value(true)
389            .possible_values(use_snapshot_archives_at_startup::cli::POSSIBLE_VALUES)
390            .default_value(use_snapshot_archives_at_startup::cli::default_value())
391            .help(use_snapshot_archives_at_startup::cli::HELP)
392            .long_help(use_snapshot_archives_at_startup::cli::LONG_HELP),
393    )
394    .arg(
395        Arg::with_name("full_snapshot_archive_path")
396            .long("full-snapshot-archive-path")
397            .value_name("DIR")
398            .takes_value(true)
399            .help("Use DIR as full snapshot archives location [default: --snapshots value]"),
400    )
401    .arg(
402        Arg::with_name("incremental_snapshot_archive_path")
403            .long("incremental-snapshot-archive-path")
404            .conflicts_with("no-incremental-snapshots")
405            .value_name("DIR")
406            .takes_value(true)
407            .help("Use DIR as incremental snapshot archives location [default: --snapshots value]"),
408    )
409    .arg(
410        Arg::with_name("tower")
411            .long("tower")
412            .value_name("DIR")
413            .takes_value(true)
414            .help("Use DIR as file tower storage location [default: --ledger value]"),
415    )
416    .arg(
417        Arg::with_name("gossip_port")
418            .long("gossip-port")
419            .value_name("PORT")
420            .takes_value(true)
421            .help("Gossip port number for the validator"),
422    )
423    .arg(
424        Arg::with_name("public_tpu_addr")
425            .long("public-tpu-address")
426            .alias("tpu-host-addr")
427            .value_name("HOST:PORT")
428            .takes_value(true)
429            .validator(solana_net_utils::is_host_port)
430            .help(
431                "Specify TPU address to advertise in gossip [default: ask --entrypoint or \
432                 localhost when --entrypoint is not provided]",
433            ),
434    )
435    .arg(
436        Arg::with_name("public_tpu_forwards_addr")
437            .long("public-tpu-forwards-address")
438            .value_name("HOST:PORT")
439            .takes_value(true)
440            .validator(solana_net_utils::is_host_port)
441            .help(
442                "Specify TPU Forwards address to advertise in gossip [default: ask --entrypoint \
443                 or localhostwhen --entrypoint is not provided]",
444            ),
445    )
446    .arg(
447        Arg::with_name("tpu_vortexor_receiver_address")
448            .long("tpu-vortexor-receiver-address")
449            .value_name("HOST:PORT")
450            .takes_value(true)
451            .hidden(hidden_unless_forced())
452            .validator(solana_net_utils::is_host_port)
453            .help(
454                "TPU Vortexor Receiver address to which verified transaction packet will be \
455                 forwarded.",
456            ),
457    )
458    .arg(
459        Arg::with_name("public_rpc_addr")
460            .long("public-rpc-address")
461            .value_name("HOST:PORT")
462            .takes_value(true)
463            .conflicts_with("private_rpc")
464            .validator(solana_net_utils::is_host_port)
465            .help(
466                "RPC address for the validator to advertise publicly in gossip. Useful for \
467                 validators running behind a load balancer or proxy [default: use \
468                 --rpc-bind-address / --rpc-port]",
469            ),
470    )
471    .arg(
472        Arg::with_name("dynamic_port_range")
473            .long("dynamic-port-range")
474            .value_name("MIN_PORT-MAX_PORT")
475            .takes_value(true)
476            .default_value(&default_args.dynamic_port_range)
477            .validator(port_range_validator)
478            .help("Range to use for dynamically assigned ports"),
479    )
480    .arg(
481        Arg::with_name("maximum_local_snapshot_age")
482            .long("maximum-local-snapshot-age")
483            .value_name("NUMBER_OF_SLOTS")
484            .takes_value(true)
485            .default_value(&default_args.maximum_local_snapshot_age)
486            .help(
487                "Reuse a local snapshot if it's less than this many slots behind the highest \
488                 snapshot available for download from other validators",
489            ),
490    )
491    .arg(
492        Arg::with_name("no_snapshots")
493            .long("no-snapshots")
494            .takes_value(false)
495            .conflicts_with_all(&[
496                "no_incremental_snapshots",
497                "snapshot_interval_slots",
498                "full_snapshot_interval_slots",
499            ])
500            .help("Disable all snapshot generation"),
501    )
502    .arg(
503        Arg::with_name("no_incremental_snapshots")
504            .long("no-incremental-snapshots")
505            .takes_value(false)
506            .help("Disable incremental snapshots"),
507    )
508    .arg(
509        Arg::with_name("snapshot_interval_slots")
510            .long("snapshot-interval-slots")
511            .alias("incremental-snapshot-interval-slots")
512            .value_name("NUMBER")
513            .takes_value(true)
514            .default_value(&default_args.incremental_snapshot_archive_interval_slots)
515            .validator(is_non_zero)
516            .help("Number of slots between generating snapshots")
517            .long_help(
518                "Number of slots between generating snapshots. If incremental snapshots are \
519                 enabled, this sets the incremental snapshot interval. If incremental snapshots \
520                 are disabled, this sets the full snapshot interval. Must be greater than zero.",
521            ),
522    )
523    .arg(
524        Arg::with_name("full_snapshot_interval_slots")
525            .long("full-snapshot-interval-slots")
526            .value_name("NUMBER")
527            .takes_value(true)
528            .default_value(&default_args.full_snapshot_archive_interval_slots)
529            .validator(is_non_zero)
530            .help("Number of slots between generating full snapshots")
531            .long_help(
532                "Number of slots between generating full snapshots. Only used when incremental \
533                 snapshots are enabled. Must be greater than the incremental snapshot interval. \
534                 Must be greater than zero.",
535            ),
536    )
537    .arg(
538        Arg::with_name("maximum_full_snapshots_to_retain")
539            .long("maximum-full-snapshots-to-retain")
540            .alias("maximum-snapshots-to-retain")
541            .value_name("NUMBER")
542            .takes_value(true)
543            .default_value(&default_args.maximum_full_snapshot_archives_to_retain)
544            .validator(validate_maximum_full_snapshot_archives_to_retain)
545            .help(
546                "The maximum number of full snapshot archives to hold on to when purging older \
547                 snapshots.",
548            ),
549    )
550    .arg(
551        Arg::with_name("maximum_incremental_snapshots_to_retain")
552            .long("maximum-incremental-snapshots-to-retain")
553            .value_name("NUMBER")
554            .takes_value(true)
555            .default_value(&default_args.maximum_incremental_snapshot_archives_to_retain)
556            .validator(validate_maximum_incremental_snapshot_archives_to_retain)
557            .help(
558                "The maximum number of incremental snapshot archives to hold on to when purging \
559                 older snapshots.",
560            ),
561    )
562    .arg(
563        Arg::with_name("snapshot_packager_niceness_adj")
564            .long("snapshot-packager-niceness-adjustment")
565            .value_name("ADJUSTMENT")
566            .takes_value(true)
567            .validator(solana_perf::thread::is_niceness_adjustment_valid)
568            .default_value(&default_args.snapshot_packager_niceness_adjustment)
569            .help(
570                "Add this value to niceness of snapshot packager thread. Negative value increases \
571                 priority, positive value decreases priority.",
572            ),
573    )
574    .arg(
575        Arg::with_name("minimal_snapshot_download_speed")
576            .long("minimal-snapshot-download-speed")
577            .value_name("MINIMAL_SNAPSHOT_DOWNLOAD_SPEED")
578            .takes_value(true)
579            .default_value(&default_args.min_snapshot_download_speed)
580            .help(
581                "The minimal speed of snapshot downloads measured in bytes/second. If the initial \
582                 download speed falls below this threshold, the system will retry the download \
583                 against a different rpc node.",
584            ),
585    )
586    .arg(
587        Arg::with_name("maximum_snapshot_download_abort")
588            .long("maximum-snapshot-download-abort")
589            .value_name("MAXIMUM_SNAPSHOT_DOWNLOAD_ABORT")
590            .takes_value(true)
591            .default_value(&default_args.max_snapshot_download_abort)
592            .help(
593                "The maximum number of times to abort and retry when encountering a slow snapshot \
594                 download.",
595            ),
596    )
597    .arg(
598        Arg::with_name("contact_debug_interval")
599            .long("contact-debug-interval")
600            .value_name("CONTACT_DEBUG_INTERVAL")
601            .takes_value(true)
602            .default_value(&default_args.contact_debug_interval)
603            .help("Milliseconds between printing contact debug from gossip."),
604    )
605    .arg(
606        Arg::with_name("no_poh_speed_test")
607            .long("no-poh-speed-test")
608            .hidden(hidden_unless_forced())
609            .help("Skip the check for PoH speed."),
610    )
611    .arg(
612        Arg::with_name("no_os_network_limits_test")
613            .hidden(hidden_unless_forced())
614            .long("no-os-network-limits-test")
615            .help("Skip checks for OS network limits."),
616    )
617    .arg(
618        Arg::with_name("no_os_memory_stats_reporting")
619            .long("no-os-memory-stats-reporting")
620            .hidden(hidden_unless_forced())
621            .help("Disable reporting of OS memory statistics."),
622    )
623    .arg(
624        Arg::with_name("no_os_network_stats_reporting")
625            .long("no-os-network-stats-reporting")
626            .hidden(hidden_unless_forced())
627            .help("Disable reporting of OS network statistics."),
628    )
629    .arg(
630        Arg::with_name("no_os_cpu_stats_reporting")
631            .long("no-os-cpu-stats-reporting")
632            .hidden(hidden_unless_forced())
633            .help("Disable reporting of OS CPU statistics."),
634    )
635    .arg(
636        Arg::with_name("no_os_disk_stats_reporting")
637            .long("no-os-disk-stats-reporting")
638            .hidden(hidden_unless_forced())
639            .help("Disable reporting of OS disk statistics."),
640    )
641    .arg(
642        Arg::with_name("snapshot_version")
643            .long("snapshot-version")
644            .value_name("SNAPSHOT_VERSION")
645            .validator(is_parsable::<SnapshotVersion>)
646            .takes_value(true)
647            .default_value(default_args.snapshot_version.into())
648            .help("Output snapshot version"),
649    )
650    .arg(
651        Arg::with_name("limit_ledger_size")
652            .long("limit-ledger-size")
653            .value_name("SHRED_COUNT")
654            .takes_value(true)
655            .min_values(0)
656            .max_values(1)
657            /* .default_value() intentionally not used here! */
658            .help("Keep this amount of shreds in root slots."),
659    )
660    .arg(
661        Arg::with_name("rocksdb_shred_compaction")
662            .long("rocksdb-shred-compaction")
663            .value_name("ROCKSDB_COMPACTION_STYLE")
664            .takes_value(true)
665            .possible_values(&["level"])
666            .default_value(&default_args.rocksdb_shred_compaction)
667            .help(
668                "Controls how RocksDB compacts shreds. *WARNING*: You will lose your Blockstore \
669                 data when you switch between options.",
670            ),
671    )
672    .arg(
673        Arg::with_name("rocksdb_ledger_compression")
674            .hidden(hidden_unless_forced())
675            .long("rocksdb-ledger-compression")
676            .value_name("COMPRESSION_TYPE")
677            .takes_value(true)
678            .possible_values(&["none", "lz4", "snappy", "zlib"])
679            .default_value(&default_args.rocksdb_ledger_compression)
680            .help(
681                "The compression algorithm that is used to compress transaction status data. \
682                 Turning on compression can save ~10% of the ledger size.",
683            ),
684    )
685    .arg(
686        Arg::with_name("rocksdb_perf_sample_interval")
687            .hidden(hidden_unless_forced())
688            .long("rocksdb-perf-sample-interval")
689            .value_name("ROCKS_PERF_SAMPLE_INTERVAL")
690            .takes_value(true)
691            .validator(is_parsable::<usize>)
692            .default_value(&default_args.rocksdb_perf_sample_interval)
693            .help(
694                "Controls how often RocksDB read/write performance samples are collected. Perf \
695                 samples are collected in 1 / ROCKS_PERF_SAMPLE_INTERVAL sampling rate.",
696            ),
697    )
698    .arg(
699        Arg::with_name("skip_startup_ledger_verification")
700            .long("skip-startup-ledger-verification")
701            .takes_value(false)
702            .help("Skip ledger verification at validator bootup."),
703    )
704    .arg(
705        Arg::with_name("cuda")
706            .long("cuda")
707            .takes_value(false)
708            .help("Use CUDA"),
709    )
710    .arg(
711        clap::Arg::with_name("require_tower")
712            .long("require-tower")
713            .takes_value(false)
714            .help("Refuse to start if saved tower state is not found"),
715    )
716    .arg(
717        Arg::with_name("expected_genesis_hash")
718            .long("expected-genesis-hash")
719            .value_name("HASH")
720            .takes_value(true)
721            .validator(hash_validator)
722            .help("Require the genesis have this hash"),
723    )
724    .arg(
725        Arg::with_name("expected_bank_hash")
726            .long("expected-bank-hash")
727            .value_name("HASH")
728            .takes_value(true)
729            .validator(hash_validator)
730            .help("When wait-for-supermajority <x>, require the bank at <x> to have this hash"),
731    )
732    .arg(
733        Arg::with_name("expected_shred_version")
734            .long("expected-shred-version")
735            .value_name("VERSION")
736            .takes_value(true)
737            .validator(is_parsable::<u16>)
738            .help("Require the shred version be this value"),
739    )
740    .arg(
741        Arg::with_name("logfile")
742            .short("o")
743            .long("log")
744            .value_name("FILE")
745            .takes_value(true)
746            .help(
747                "Redirect logging to the specified file, '-' for standard error. Sending the \
748                 SIGUSR1 signal to the validator process will cause it to re-open the log file",
749            ),
750    )
751    .arg(
752        Arg::with_name("wait_for_supermajority")
753            .long("wait-for-supermajority")
754            .requires("expected_bank_hash")
755            .requires("expected_shred_version")
756            .value_name("SLOT")
757            .validator(is_slot)
758            .help(
759                "After processing the ledger and the next slot is SLOT, wait until a \
760                 supermajority of stake is visible on gossip before starting PoH",
761            ),
762    )
763    .arg(
764        Arg::with_name("no_wait_for_vote_to_start_leader")
765            .hidden(hidden_unless_forced())
766            .long("no-wait-for-vote-to-start-leader")
767            .help(
768                "If the validator starts up with no ledger, it will wait to start block \
769                 production until it sees a vote land in a rooted slot. This prevents double \
770                 signing. Turn off to risk double signing a block.",
771            ),
772    )
773    .arg(
774        Arg::with_name("hard_forks")
775            .long("hard-fork")
776            .value_name("SLOT")
777            .validator(is_slot)
778            .multiple(true)
779            .takes_value(true)
780            .help("Add a hard fork at this slot"),
781    )
782    .arg(
783        Arg::with_name("known_validators")
784            .alias("trusted-validator")
785            .long("known-validator")
786            .validator(is_pubkey)
787            .value_name("VALIDATOR IDENTITY")
788            .multiple(true)
789            .takes_value(true)
790            .help(
791                "A snapshot hash must be published in gossip by this validator to be accepted. \
792                 May be specified multiple times. If unspecified any snapshot hash will be \
793                 accepted",
794            ),
795    )
796    .arg(
797        Arg::with_name("debug_key")
798            .long("debug-key")
799            .validator(is_pubkey)
800            .value_name("ADDRESS")
801            .multiple(true)
802            .takes_value(true)
803            .help("Log when transactions are processed which reference a given key."),
804    )
805    .arg(
806        Arg::with_name("only_known_rpc")
807            .alias("no-untrusted-rpc")
808            .long("only-known-rpc")
809            .takes_value(false)
810            .requires("known_validators")
811            .help("Use the RPC service of known validators only"),
812    )
813    .arg(
814        Arg::with_name("repair_validators")
815            .long("repair-validator")
816            .validator(is_pubkey)
817            .value_name("VALIDATOR IDENTITY")
818            .multiple(true)
819            .takes_value(true)
820            .help(
821                "A list of validators to request repairs from. If specified, repair will not \
822                 request from validators outside this set [default: all validators]",
823            ),
824    )
825    .arg(
826        Arg::with_name("repair_whitelist")
827            .hidden(hidden_unless_forced())
828            .long("repair-whitelist")
829            .validator(is_pubkey)
830            .value_name("VALIDATOR IDENTITY")
831            .multiple(true)
832            .takes_value(true)
833            .help(
834                "A list of validators to prioritize repairs from. If specified, repair requests \
835                 from validators in the list will be prioritized over requests from other \
836                 validators. [default: all validators]",
837            ),
838    )
839    .arg(
840        Arg::with_name("gossip_validators")
841            .long("gossip-validator")
842            .validator(is_pubkey)
843            .value_name("VALIDATOR IDENTITY")
844            .multiple(true)
845            .takes_value(true)
846            .help(
847                "A list of validators to gossip with. If specified, gossip will not push/pull \
848                 from from validators outside this set. [default: all validators]",
849            ),
850    )
851    .arg(
852        Arg::with_name("tpu_coalesce_ms")
853            .long("tpu-coalesce-ms")
854            .value_name("MILLISECS")
855            .takes_value(true)
856            .validator(is_parsable::<u64>)
857            .help("Milliseconds to wait in the TPU receiver for packet coalescing."),
858    )
859    .arg(
860        Arg::with_name("tpu_connection_pool_size")
861            .long("tpu-connection-pool-size")
862            .takes_value(true)
863            .default_value(&default_args.tpu_connection_pool_size)
864            .validator(is_parsable::<usize>)
865            .help("Controls the TPU connection pool size per remote address"),
866    )
867    .arg(
868        Arg::with_name("tpu_max_connections_per_ipaddr_per_minute")
869            .long("tpu-max-connections-per-ipaddr-per-minute")
870            .takes_value(true)
871            .default_value(&default_args.tpu_max_connections_per_ipaddr_per_minute)
872            .validator(is_parsable::<u32>)
873            .hidden(hidden_unless_forced())
874            .help("Controls the rate of the clients connections per IpAddr per minute."),
875    )
876    .arg(
877        Arg::with_name("vote_use_quic")
878            .long("vote-use-quic")
879            .takes_value(true)
880            .default_value(&default_args.vote_use_quic)
881            .hidden(hidden_unless_forced())
882            .help("Controls if to use QUIC to send votes."),
883    )
884    .arg(
885        Arg::with_name("tpu_max_connections_per_peer")
886            .long("tpu-max-connections-per-peer")
887            .takes_value(true)
888            .default_value(&default_args.tpu_max_connections_per_peer)
889            .validator(is_parsable::<u32>)
890            .hidden(hidden_unless_forced())
891            .help("Controls the max concurrent connections per IpAddr."),
892    )
893    .arg(
894        Arg::with_name("tpu_max_staked_connections")
895            .long("tpu-max-staked-connections")
896            .takes_value(true)
897            .default_value(&default_args.tpu_max_staked_connections)
898            .validator(is_parsable::<u32>)
899            .hidden(hidden_unless_forced())
900            .help("Controls the max concurrent connections for TPU from staked nodes."),
901    )
902    .arg(
903        Arg::with_name("tpu_max_unstaked_connections")
904            .long("tpu-max-unstaked-connections")
905            .takes_value(true)
906            .default_value(&default_args.tpu_max_unstaked_connections)
907            .validator(is_parsable::<u32>)
908            .hidden(hidden_unless_forced())
909            .help("Controls the max concurrent connections fort TPU from unstaked nodes."),
910    )
911    .arg(
912        Arg::with_name("tpu_max_fwd_staked_connections")
913            .long("tpu-max-fwd-staked-connections")
914            .takes_value(true)
915            .default_value(&default_args.tpu_max_fwd_staked_connections)
916            .validator(is_parsable::<u32>)
917            .hidden(hidden_unless_forced())
918            .help("Controls the max concurrent connections for TPU-forward from staked nodes."),
919    )
920    .arg(
921        Arg::with_name("tpu_max_fwd_unstaked_connections")
922            .long("tpu-max-fwd-unstaked-connections")
923            .takes_value(true)
924            .default_value(&default_args.tpu_max_fwd_unstaked_connections)
925            .validator(is_parsable::<u32>)
926            .hidden(hidden_unless_forced())
927            .help("Controls the max concurrent connections for TPU-forward from unstaked nodes."),
928    )
929    .arg(
930        Arg::with_name("tpu_max_streams_per_ms")
931            .long("tpu-max-streams-per-ms")
932            .takes_value(true)
933            .default_value(&default_args.tpu_max_streams_per_ms)
934            .validator(is_parsable::<usize>)
935            .hidden(hidden_unless_forced())
936            .help("Controls the max number of streams for a TPU service."),
937    )
938    .arg(
939        Arg::with_name("num_quic_endpoints")
940            .long("num-quic-endpoints")
941            .takes_value(true)
942            .default_value(&default_args.num_quic_endpoints)
943            .validator(is_parsable::<usize>)
944            .hidden(hidden_unless_forced())
945            .help(
946                "The number of QUIC endpoints used for TPU and TPU-Forward. It can be increased \
947                 to increase network ingest throughput, at the expense of higher CPU and general \
948                 validator load.",
949            ),
950    )
951    .arg(
952        Arg::with_name("staked_nodes_overrides")
953            .long("staked-nodes-overrides")
954            .value_name("PATH")
955            .takes_value(true)
956            .help(
957                "Provide path to a yaml file with custom overrides for stakes of specific \
958                 identities. Overriding the amount of stake this validator considers as valid for \
959                 other peers in network. The stake amount is used for calculating the number of \
960                 QUIC streams permitted from the peer and vote packet sender stage. Format of the \
961                 file: `staked_map_id: {<pubkey>: <SOL stake amount>}",
962            ),
963    )
964    .arg(
965        Arg::with_name("bind_address")
966            .long("bind-address")
967            .value_name("HOST")
968            .takes_value(true)
969            .validator(solana_net_utils::is_host)
970            .default_value(&default_args.bind_address)
971            .multiple(true)
972            .help(
973                "Repeatable. IP addresses to bind the validator ports on. First is primary (used \
974                 on startup), the rest may be switched to during operation.",
975            ),
976    )
977    .arg(
978        Arg::with_name("rpc_bind_address")
979            .long("rpc-bind-address")
980            .value_name("HOST")
981            .takes_value(true)
982            .validator(solana_net_utils::is_host)
983            .help(
984                "IP address to bind the RPC port [default: 127.0.0.1 if --private-rpc is present, \
985                 otherwise use --bind-address]",
986            ),
987    )
988    .arg(
989        Arg::with_name("rpc_threads")
990            .long("rpc-threads")
991            .value_name("NUMBER")
992            .validator(is_parsable::<usize>)
993            .takes_value(true)
994            .default_value(&default_args.rpc_threads)
995            .help("Number of threads to use for servicing RPC requests"),
996    )
997    .arg(
998        Arg::with_name("rpc_blocking_threads")
999            .long("rpc-blocking-threads")
1000            .value_name("NUMBER")
1001            .validator(is_parsable::<usize>)
1002            .validator(|value| {
1003                value
1004                    .parse::<u64>()
1005                    .map_err(|err| format!("error parsing '{value}': {err}"))
1006                    .and_then(|threads| {
1007                        if threads > 0 {
1008                            Ok(())
1009                        } else {
1010                            Err("value must be >= 1".to_string())
1011                        }
1012                    })
1013            })
1014            .takes_value(true)
1015            .default_value(&default_args.rpc_blocking_threads)
1016            .help(
1017                "Number of blocking threads to use for servicing CPU bound RPC requests (eg \
1018                 getMultipleAccounts)",
1019            ),
1020    )
1021    .arg(
1022        Arg::with_name("rpc_niceness_adj")
1023            .long("rpc-niceness-adjustment")
1024            .value_name("ADJUSTMENT")
1025            .takes_value(true)
1026            .validator(solana_perf::thread::is_niceness_adjustment_valid)
1027            .default_value(&default_args.rpc_niceness_adjustment)
1028            .help(
1029                "Add this value to niceness of RPC threads. Negative value increases priority, \
1030                 positive value decreases priority.",
1031            ),
1032    )
1033    .arg(
1034        Arg::with_name("rpc_bigtable_timeout")
1035            .long("rpc-bigtable-timeout")
1036            .value_name("SECONDS")
1037            .validator(is_parsable::<u64>)
1038            .takes_value(true)
1039            .default_value(&default_args.rpc_bigtable_timeout)
1040            .help("Number of seconds before timing out RPC requests backed by BigTable"),
1041    )
1042    .arg(
1043        Arg::with_name("rpc_bigtable_instance_name")
1044            .long("rpc-bigtable-instance-name")
1045            .takes_value(true)
1046            .value_name("INSTANCE_NAME")
1047            .default_value(&default_args.rpc_bigtable_instance_name)
1048            .help("Name of the Bigtable instance to upload to"),
1049    )
1050    .arg(
1051        Arg::with_name("rpc_bigtable_app_profile_id")
1052            .long("rpc-bigtable-app-profile-id")
1053            .takes_value(true)
1054            .value_name("APP_PROFILE_ID")
1055            .default_value(&default_args.rpc_bigtable_app_profile_id)
1056            .help("Bigtable application profile id to use in requests"),
1057    )
1058    .arg(
1059        Arg::with_name("rpc_bigtable_max_message_size")
1060            .long("rpc-bigtable-max-message-size")
1061            .value_name("BYTES")
1062            .validator(is_parsable::<usize>)
1063            .takes_value(true)
1064            .default_value(&default_args.rpc_bigtable_max_message_size)
1065            .help("Max encoding and decoding message size used in Bigtable Grpc client"),
1066    )
1067    .arg(
1068        Arg::with_name("rpc_pubsub_worker_threads")
1069            .long("rpc-pubsub-worker-threads")
1070            .takes_value(true)
1071            .value_name("NUMBER")
1072            .validator(is_parsable::<usize>)
1073            .default_value(&default_args.rpc_pubsub_worker_threads)
1074            .help("PubSub worker threads"),
1075    )
1076    .arg(
1077        Arg::with_name("rpc_pubsub_enable_block_subscription")
1078            .long("rpc-pubsub-enable-block-subscription")
1079            .requires("enable_rpc_transaction_history")
1080            .takes_value(false)
1081            .help("Enable the unstable RPC PubSub `blockSubscribe` subscription"),
1082    )
1083    .arg(
1084        Arg::with_name("rpc_pubsub_enable_vote_subscription")
1085            .long("rpc-pubsub-enable-vote-subscription")
1086            .takes_value(false)
1087            .help("Enable the unstable RPC PubSub `voteSubscribe` subscription"),
1088    )
1089    .arg(
1090        Arg::with_name("rpc_pubsub_max_active_subscriptions")
1091            .long("rpc-pubsub-max-active-subscriptions")
1092            .takes_value(true)
1093            .value_name("NUMBER")
1094            .validator(is_parsable::<usize>)
1095            .default_value(&default_args.rpc_pubsub_max_active_subscriptions)
1096            .help(
1097                "The maximum number of active subscriptions that RPC PubSub will accept across \
1098                 all connections.",
1099            ),
1100    )
1101    .arg(
1102        Arg::with_name("rpc_pubsub_queue_capacity_items")
1103            .long("rpc-pubsub-queue-capacity-items")
1104            .takes_value(true)
1105            .value_name("NUMBER")
1106            .validator(is_parsable::<usize>)
1107            .default_value(&default_args.rpc_pubsub_queue_capacity_items)
1108            .help(
1109                "The maximum number of notifications that RPC PubSub will store across all \
1110                 connections.",
1111            ),
1112    )
1113    .arg(
1114        Arg::with_name("rpc_pubsub_queue_capacity_bytes")
1115            .long("rpc-pubsub-queue-capacity-bytes")
1116            .takes_value(true)
1117            .value_name("BYTES")
1118            .validator(is_parsable::<usize>)
1119            .default_value(&default_args.rpc_pubsub_queue_capacity_bytes)
1120            .help(
1121                "The maximum total size of notifications that RPC PubSub will store across all \
1122                 connections.",
1123            ),
1124    )
1125    .arg(
1126        Arg::with_name("rpc_pubsub_notification_threads")
1127            .long("rpc-pubsub-notification-threads")
1128            .requires("full_rpc_api")
1129            .takes_value(true)
1130            .value_name("NUM_THREADS")
1131            .validator(is_parsable::<usize>)
1132            .default_value_if(
1133                "full_rpc_api",
1134                None,
1135                &default_args.rpc_pubsub_notification_threads,
1136            )
1137            .help(
1138                "The maximum number of threads that RPC PubSub will use for generating \
1139                 notifications. 0 will disable RPC PubSub notifications",
1140            ),
1141    )
1142    .arg(
1143        Arg::with_name("rpc_send_transaction_retry_ms")
1144            .long("rpc-send-retry-ms")
1145            .value_name("MILLISECS")
1146            .takes_value(true)
1147            .validator(is_parsable::<u64>)
1148            .default_value(&default_args.rpc_send_transaction_retry_ms)
1149            .help("The rate at which transactions sent via rpc service are retried."),
1150    )
1151    .arg(
1152        Arg::with_name("rpc_send_transaction_batch_ms")
1153            .long("rpc-send-batch-ms")
1154            .value_name("MILLISECS")
1155            .hidden(hidden_unless_forced())
1156            .takes_value(true)
1157            .validator(|s| is_within_range(s, 1..=MAX_BATCH_SEND_RATE_MS))
1158            .default_value(&default_args.rpc_send_transaction_batch_ms)
1159            .help("The rate at which transactions sent via rpc service are sent in batch."),
1160    )
1161    .arg(
1162        Arg::with_name("rpc_send_transaction_leader_forward_count")
1163            .long("rpc-send-leader-count")
1164            .value_name("NUMBER")
1165            .takes_value(true)
1166            .validator(is_parsable::<u64>)
1167            .default_value(&default_args.rpc_send_transaction_leader_forward_count)
1168            .help(
1169                "The number of upcoming leaders to which to forward transactions sent via rpc \
1170                 service.",
1171            ),
1172    )
1173    .arg(
1174        Arg::with_name("rpc_send_transaction_default_max_retries")
1175            .long("rpc-send-default-max-retries")
1176            .value_name("NUMBER")
1177            .takes_value(true)
1178            .validator(is_parsable::<usize>)
1179            .help(
1180                "The maximum number of transaction broadcast retries when unspecified by the \
1181                 request, otherwise retried until expiration.",
1182            ),
1183    )
1184    .arg(
1185        Arg::with_name("rpc_send_transaction_service_max_retries")
1186            .long("rpc-send-service-max-retries")
1187            .value_name("NUMBER")
1188            .takes_value(true)
1189            .validator(is_parsable::<usize>)
1190            .default_value(&default_args.rpc_send_transaction_service_max_retries)
1191            .help(
1192                "The maximum number of transaction broadcast retries, regardless of requested \
1193                 value.",
1194            ),
1195    )
1196    .arg(
1197        Arg::with_name("rpc_send_transaction_batch_size")
1198            .long("rpc-send-batch-size")
1199            .value_name("NUMBER")
1200            .hidden(hidden_unless_forced())
1201            .takes_value(true)
1202            .validator(|s| is_within_range(s, 1..=MAX_TRANSACTION_BATCH_SIZE))
1203            .default_value(&default_args.rpc_send_transaction_batch_size)
1204            .help("The size of transactions to be sent in batch."),
1205    )
1206    .arg(
1207        Arg::with_name("rpc_send_transaction_retry_pool_max_size")
1208            .long("rpc-send-transaction-retry-pool-max-size")
1209            .value_name("NUMBER")
1210            .takes_value(true)
1211            .validator(is_parsable::<usize>)
1212            .default_value(&default_args.rpc_send_transaction_retry_pool_max_size)
1213            .help("The maximum size of transactions retry pool."),
1214    )
1215    .arg(
1216        Arg::with_name("rpc_send_transaction_tpu_peer")
1217            .long("rpc-send-transaction-tpu-peer")
1218            .takes_value(true)
1219            .number_of_values(1)
1220            .multiple(true)
1221            .value_name("HOST:PORT")
1222            .validator(solana_net_utils::is_host_port)
1223            .help("Peer(s) to broadcast transactions to instead of the current leader"),
1224    )
1225    .arg(
1226        Arg::with_name("rpc_send_transaction_also_leader")
1227            .long("rpc-send-transaction-also-leader")
1228            .requires("rpc_send_transaction_tpu_peer")
1229            .help(
1230                "With `--rpc-send-transaction-tpu-peer HOST:PORT`, also send to the current leader",
1231            ),
1232    )
1233    .arg(
1234        Arg::with_name("rpc_scan_and_fix_roots")
1235            .long("rpc-scan-and-fix-roots")
1236            .takes_value(false)
1237            .requires("enable_rpc_transaction_history")
1238            .help("Verifies blockstore roots on boot and fixes any gaps"),
1239    )
1240    .arg(
1241        Arg::with_name("rpc_max_request_body_size")
1242            .long("rpc-max-request-body-size")
1243            .value_name("BYTES")
1244            .takes_value(true)
1245            .validator(is_parsable::<usize>)
1246            .default_value(&default_args.rpc_max_request_body_size)
1247            .help("The maximum request body size accepted by rpc service"),
1248    )
1249    .arg(
1250        Arg::with_name("geyser_plugin_config")
1251            .long("geyser-plugin-config")
1252            .alias("accountsdb-plugin-config")
1253            .value_name("FILE")
1254            .takes_value(true)
1255            .multiple(true)
1256            .help("Specify the configuration file for the Geyser plugin."),
1257    )
1258    .arg(
1259        Arg::with_name("geyser_plugin_always_enabled")
1260            .long("geyser-plugin-always-enabled")
1261            .value_name("BOOLEAN")
1262            .takes_value(false)
1263            .help("Еnable Geyser interface even if no Geyser configs are specified."),
1264    )
1265    .arg(
1266        Arg::with_name("snapshot_archive_format")
1267            .long("snapshot-archive-format")
1268            .alias("snapshot-compression") // Legacy name used by Solana v1.5.x and older
1269            .possible_values(SUPPORTED_ARCHIVE_COMPRESSION)
1270            .default_value(&default_args.snapshot_archive_format)
1271            .value_name("ARCHIVE_TYPE")
1272            .takes_value(true)
1273            .help("Snapshot archive format to use."),
1274    )
1275    .arg(
1276        Arg::with_name("snapshot_zstd_compression_level")
1277            .long("snapshot-zstd-compression-level")
1278            .default_value(&default_args.snapshot_zstd_compression_level)
1279            .value_name("LEVEL")
1280            .takes_value(true)
1281            .help("The compression level to use when archiving with zstd")
1282            .long_help(
1283                "The compression level to use when archiving with zstd. Higher compression levels \
1284                 generally produce higher compression ratio at the expense of speed and memory. \
1285                 See the zstd manpage for more information.",
1286            ),
1287    )
1288    .arg(
1289        Arg::with_name("max_genesis_archive_unpacked_size")
1290            .long("max-genesis-archive-unpacked-size")
1291            .value_name("NUMBER")
1292            .takes_value(true)
1293            .default_value(&default_args.genesis_archive_unpacked_size)
1294            .help("maximum total uncompressed file size of downloaded genesis archive"),
1295    )
1296    .arg(
1297        Arg::with_name("wal_recovery_mode")
1298            .long("wal-recovery-mode")
1299            .value_name("MODE")
1300            .takes_value(true)
1301            .possible_values(&[
1302                "tolerate_corrupted_tail_records",
1303                "absolute_consistency",
1304                "point_in_time",
1305                "skip_any_corrupted_record",
1306            ])
1307            .help("Mode to recovery the ledger db write ahead log."),
1308    )
1309    .arg(
1310        Arg::with_name("poh_pinned_cpu_core")
1311            .hidden(hidden_unless_forced())
1312            .long("experimental-poh-pinned-cpu-core")
1313            .takes_value(true)
1314            .value_name("CPU_CORE_INDEX")
1315            .validator(|s| {
1316                let core_index = usize::from_str(&s).map_err(|e| e.to_string())?;
1317                let max_index = core_affinity::get_core_ids()
1318                    .map(|cids| cids.len() - 1)
1319                    .unwrap_or(0);
1320                if core_index > max_index {
1321                    return Err(format!("core index must be in the range [0, {max_index}]"));
1322                }
1323                Ok(())
1324            })
1325            .help("EXPERIMENTAL: Specify which CPU core PoH is pinned to"),
1326    )
1327    .arg(
1328        Arg::with_name("poh_hashes_per_batch")
1329            .hidden(hidden_unless_forced())
1330            .long("poh-hashes-per-batch")
1331            .takes_value(true)
1332            .value_name("NUM")
1333            .help("Specify hashes per batch in PoH service"),
1334    )
1335    .arg(
1336        Arg::with_name("process_ledger_before_services")
1337            .long("process-ledger-before-services")
1338            .hidden(hidden_unless_forced())
1339            .help("Process the local ledger fully before starting networking services"),
1340    )
1341    .arg(
1342        Arg::with_name("account_indexes")
1343            .long("account-index")
1344            .takes_value(true)
1345            .multiple(true)
1346            .possible_values(&["program-id", "spl-token-owner", "spl-token-mint"])
1347            .value_name("INDEX")
1348            .help("Enable an accounts index, indexed by the selected account field"),
1349    )
1350    .arg(
1351        Arg::with_name("account_index_exclude_key")
1352            .long(EXCLUDE_KEY)
1353            .takes_value(true)
1354            .validator(is_pubkey)
1355            .multiple(true)
1356            .value_name("KEY")
1357            .help("When account indexes are enabled, exclude this key from the index."),
1358    )
1359    .arg(
1360        Arg::with_name("account_index_include_key")
1361            .long(INCLUDE_KEY)
1362            .takes_value(true)
1363            .validator(is_pubkey)
1364            .conflicts_with("account_index_exclude_key")
1365            .multiple(true)
1366            .value_name("KEY")
1367            .help(
1368                "When account indexes are enabled, only include specific keys in the index. This \
1369                 overrides --account-index-exclude-key.",
1370            ),
1371    )
1372    .arg(
1373        Arg::with_name("accounts_db_verify_refcounts")
1374            .long("accounts-db-verify-refcounts")
1375            .help(
1376                "Debug option to scan all append vecs and verify account index refcounts prior to \
1377                 clean",
1378            )
1379            .hidden(hidden_unless_forced()),
1380    )
1381    .arg(
1382        Arg::with_name("accounts_db_scan_filter_for_shrinking")
1383            .long("accounts-db-scan-filter-for-shrinking")
1384            .takes_value(true)
1385            .possible_values(&["all", "only-abnormal", "only-abnormal-with-verify"])
1386            .help(
1387                "Debug option to use different type of filtering for accounts index scan in \
1388                 shrinking. \"all\" will scan both in-memory and on-disk accounts index, which is \
1389                 the default. \"only-abnormal\" will scan in-memory accounts index only for \
1390                 abnormal entries and skip scanning on-disk accounts index by assuming that \
1391                 on-disk accounts index contains only normal accounts index entry. \
1392                 \"only-abnormal-with-verify\" is similar to \"only-abnormal\", which will scan \
1393                 in-memory index for abnormal entries, but will also verify that on-disk account \
1394                 entries are indeed normal.",
1395            )
1396            .hidden(hidden_unless_forced()),
1397    )
1398    .arg(
1399        Arg::with_name("no_skip_initial_accounts_db_clean")
1400            .long("no-skip-initial-accounts-db-clean")
1401            .help("Do not skip the initial cleaning of accounts when verifying snapshot bank")
1402            .hidden(hidden_unless_forced()),
1403    )
1404    .arg(
1405        Arg::with_name("accounts_db_access_storages_method")
1406            .long("accounts-db-access-storages-method")
1407            .value_name("METHOD")
1408            .takes_value(true)
1409            .possible_values(&["mmap", "file"])
1410            .help("Access account storages using this method"),
1411    )
1412    .arg(
1413        Arg::with_name("accounts_db_ancient_append_vecs")
1414            .long("accounts-db-ancient-append-vecs")
1415            .value_name("SLOT-OFFSET")
1416            .validator(is_parsable::<i64>)
1417            .takes_value(true)
1418            .help(
1419                "AppendVecs that are older than (slots_per_epoch - SLOT-OFFSET) are squashed \
1420                 together.",
1421            )
1422            .hidden(hidden_unless_forced()),
1423    )
1424    .arg(
1425        Arg::with_name("accounts_db_ancient_storage_ideal_size")
1426            .long("accounts-db-ancient-storage-ideal-size")
1427            .value_name("BYTES")
1428            .validator(is_parsable::<u64>)
1429            .takes_value(true)
1430            .help("The smallest size of ideal ancient storage.")
1431            .hidden(hidden_unless_forced()),
1432    )
1433    .arg(
1434        Arg::with_name("accounts_db_max_ancient_storages")
1435            .long("accounts-db-max-ancient-storages")
1436            .value_name("USIZE")
1437            .validator(is_parsable::<usize>)
1438            .takes_value(true)
1439            .help("The number of ancient storages the ancient slot combining should converge to.")
1440            .hidden(hidden_unless_forced()),
1441    )
1442    .arg(
1443        Arg::with_name("accounts_db_cache_limit_mb")
1444            .long("accounts-db-cache-limit-mb")
1445            .value_name("MEGABYTES")
1446            .validator(is_parsable::<u64>)
1447            .takes_value(true)
1448            .help(
1449                "How large the write cache for account data can become. If this is exceeded, the \
1450                 cache is flushed more aggressively.",
1451            ),
1452    )
1453    .arg(
1454        Arg::with_name("accounts_db_read_cache_limit")
1455            .long("accounts-db-read-cache-limit")
1456            .value_name("LOW,HIGH")
1457            .takes_value(true)
1458            .min_values(2)
1459            .max_values(2)
1460            .multiple(false)
1461            .require_delimiter(true)
1462            .help("How large the read cache for account data can become, in bytes")
1463            .long_help(
1464                "How large the read cache for account data can become, in bytes. The values will \
1465                 be the low and high watermarks for the cache. When the cache exceeds the high \
1466                 watermark, entries will be evicted until the size reaches the low watermark.",
1467            )
1468            .hidden(hidden_unless_forced()),
1469    )
1470    .arg(
1471        Arg::with_name("accounts_db_mark_obsolete_accounts")
1472            .long("accounts-db-mark-obsolete-accounts")
1473            .help("Enables experimental obsolete account tracking")
1474            .long_help(
1475                "Enables experimental obsolete account tracking. \
1476                 This feature tracks obsolete accounts in the account storage entry allowing \
1477                 for earlier cleaning of obsolete accounts in the storages and index. \
1478                 At this time this feature is not compatible with booting from local \
1479                 snapshot state and must unpack from archives.",
1480            )
1481            .hidden(hidden_unless_forced()),
1482    )
1483    .arg(
1484        Arg::with_name("accounts_index_scan_results_limit_mb")
1485            .long("accounts-index-scan-results-limit-mb")
1486            .value_name("MEGABYTES")
1487            .validator(is_parsable::<usize>)
1488            .takes_value(true)
1489            .help(
1490                "How large accumulated results from an accounts index scan can become. If this is \
1491                 exceeded, the scan aborts.",
1492            ),
1493    )
1494    .arg(
1495        Arg::with_name("accounts_index_bins")
1496            .long("accounts-index-bins")
1497            .value_name("BINS")
1498            .validator(is_pow2)
1499            .takes_value(true)
1500            .help("Number of bins to divide the accounts index into"),
1501    )
1502    .arg(
1503        Arg::with_name("accounts_index_path")
1504            .long("accounts-index-path")
1505            .value_name("PATH")
1506            .takes_value(true)
1507            .multiple(true)
1508            .help(
1509                "Persistent accounts-index location. May be specified multiple times. [default: \
1510                 <LEDGER>/accounts_index]",
1511            ),
1512    )
1513    .arg(
1514        Arg::with_name("accounts_shrink_optimize_total_space")
1515            .long("accounts-shrink-optimize-total-space")
1516            .takes_value(true)
1517            .value_name("BOOLEAN")
1518            .default_value(&default_args.accounts_shrink_optimize_total_space)
1519            .help(
1520                "When this is set to true, the system will shrink the most sparse accounts and \
1521                 when the overall shrink ratio is above the specified accounts-shrink-ratio, the \
1522                 shrink will stop and it will skip all other less sparse accounts.",
1523            ),
1524    )
1525    .arg(
1526        Arg::with_name("accounts_shrink_ratio")
1527            .long("accounts-shrink-ratio")
1528            .takes_value(true)
1529            .value_name("RATIO")
1530            .default_value(&default_args.accounts_shrink_ratio)
1531            .help(
1532                "Specifies the shrink ratio for the accounts to be shrunk. The shrink ratio is \
1533                 defined as the ratio of the bytes alive over the  total bytes used. If the \
1534                 account's shrink ratio is less than this ratio it becomes a candidate for \
1535                 shrinking. The value must between 0. and 1.0 inclusive.",
1536            ),
1537    )
1538    .arg(
1539        Arg::with_name("allow_private_addr")
1540            .long("allow-private-addr")
1541            .takes_value(false)
1542            .help("Allow contacting private ip addresses")
1543            .hidden(hidden_unless_forced()),
1544    )
1545    .arg(
1546        Arg::with_name("log_messages_bytes_limit")
1547            .long("log-messages-bytes-limit")
1548            .takes_value(true)
1549            .validator(is_parsable::<usize>)
1550            .value_name("BYTES")
1551            .help("Maximum number of bytes written to the program log before truncation"),
1552    )
1553    .arg(
1554        Arg::with_name("banking_trace_dir_byte_limit")
1555            // expose friendly alternative name to cli than internal
1556            // implementation-oriented one
1557            .long("enable-banking-trace")
1558            .value_name("BYTES")
1559            .validator(is_parsable::<DirByteLimit>)
1560            .takes_value(true)
1561            // Firstly, zero limit value causes tracer to be disabled
1562            // altogether, intuitively. On the other hand, this non-zero
1563            // default doesn't enable banking tracer unless this flag is
1564            // explicitly given, similar to --limit-ledger-size.
1565            // see configure_banking_trace_dir_byte_limit() for this.
1566            .default_value(&default_args.banking_trace_dir_byte_limit)
1567            .help(
1568                "Enables the banking trace explicitly, which is enabled by default and writes \
1569                 trace files for simulate-leader-blocks, retaining up to the default or specified \
1570                 total bytes in the ledger. This flag can be used to override its byte limit.",
1571            ),
1572    )
1573    .arg(
1574        Arg::with_name("disable_banking_trace")
1575            .long("disable-banking-trace")
1576            .conflicts_with("banking_trace_dir_byte_limit")
1577            .takes_value(false)
1578            .help("Disables the banking trace"),
1579    )
1580    .arg(
1581        Arg::with_name("delay_leader_block_for_pending_fork")
1582            .hidden(hidden_unless_forced())
1583            .long("delay-leader-block-for-pending-fork")
1584            .takes_value(false)
1585            .help(
1586                "Delay leader block creation while replaying a block which descends from the \
1587                 current fork and has a lower slot than our next leader slot. If we don't delay \
1588                 here, our new leader block will be on a different fork from the block we are \
1589                 replaying and there is a high chance that the cluster will confirm that block's \
1590                 fork rather than our leader block's fork because it was created before we \
1591                 started creating ours.",
1592            ),
1593    )
1594    .arg(
1595        Arg::with_name("block_verification_method")
1596            .long("block-verification-method")
1597            .value_name("METHOD")
1598            .takes_value(true)
1599            .possible_values(BlockVerificationMethod::cli_names())
1600            .default_value(BlockVerificationMethod::default().into())
1601            .help(BlockVerificationMethod::cli_message()),
1602    )
1603    .arg(
1604        Arg::with_name("block_production_method")
1605            .long("block-production-method")
1606            .value_name("METHOD")
1607            .takes_value(true)
1608            .possible_values(BlockProductionMethod::cli_names())
1609            .default_value(BlockProductionMethod::default().into())
1610            .help(BlockProductionMethod::cli_message()),
1611    )
1612    .arg(
1613        Arg::with_name("transaction_struct")
1614            .long("transaction-structure")
1615            .value_name("STRUCT")
1616            .takes_value(true)
1617            .possible_values(TransactionStructure::cli_names())
1618            .default_value(TransactionStructure::default().into())
1619            .help(TransactionStructure::cli_message()),
1620    )
1621    .arg(
1622        Arg::with_name("unified_scheduler_handler_threads")
1623            .long("unified-scheduler-handler-threads")
1624            .value_name("COUNT")
1625            .takes_value(true)
1626            .validator(|s| is_within_range(s, 1..))
1627            .help(DefaultSchedulerPool::cli_message()),
1628    )
1629    .arg(
1630        Arg::with_name("wen_restart")
1631            .long("wen-restart")
1632            .hidden(hidden_unless_forced())
1633            .value_name("FILE")
1634            .takes_value(true)
1635            .required(false)
1636            .conflicts_with("wait_for_supermajority")
1637            .requires("wen_restart_coordinator")
1638            .help(WEN_RESTART_HELP),
1639    )
1640    .arg(
1641        Arg::with_name("wen_restart_coordinator")
1642            .long("wen-restart-coordinator")
1643            .hidden(hidden_unless_forced())
1644            .value_name("PUBKEY")
1645            .takes_value(true)
1646            .required(false)
1647            .requires("wen_restart")
1648            .help(
1649                "Specifies the pubkey of the leader used in wen restart. May get stuck if the \
1650                 leader used is different from others.",
1651            ),
1652    )
1653    .arg(
1654        Arg::with_name("retransmit_xdp_interface")
1655            .hidden(hidden_unless_forced())
1656            .long("experimental-retransmit-xdp-interface")
1657            .takes_value(true)
1658            .value_name("INTERFACE")
1659            .requires("retransmit_xdp_cpu_cores")
1660            .help("EXPERIMENTAL: The network interface to use for XDP retransmit"),
1661    )
1662    .arg(
1663        Arg::with_name("retransmit_xdp_cpu_cores")
1664            .hidden(hidden_unless_forced())
1665            .long("experimental-retransmit-xdp-cpu-cores")
1666            .takes_value(true)
1667            .value_name("CPU_LIST")
1668            .validator(|value| {
1669                validate_cpu_ranges(value, "--experimental-retransmit-xdp-cpu-cores")
1670            })
1671            .help("EXPERIMENTAL: Enable XDP retransmit on the specified CPU cores"),
1672    )
1673    .arg(
1674        Arg::with_name("retransmit_xdp_zero_copy")
1675            .hidden(hidden_unless_forced())
1676            .long("experimental-retransmit-xdp-zero-copy")
1677            .takes_value(false)
1678            .requires("retransmit_xdp_cpu_cores")
1679            .help("EXPERIMENTAL: Enable XDP zero copy. Requires hardware support"),
1680    )
1681    .arg(
1682        Arg::with_name("use_connection_cache")
1683            .long("use-connection-cache")
1684            .takes_value(false)
1685            .help(
1686                "Use connection-cache crate to send transactions over TPU ports. If not \
1687                 set,tpu-client-next is used by default.",
1688            ),
1689    )
1690}
1691
1692fn validators_set(
1693    identity_pubkey: &Pubkey,
1694    matches: &ArgMatches<'_>,
1695    matches_name: &str,
1696    arg_name: &str,
1697) -> Result<Option<HashSet<Pubkey>>> {
1698    if matches.is_present(matches_name) {
1699        let validators_set: Option<HashSet<Pubkey>> = values_t!(matches, matches_name, Pubkey)
1700            .ok()
1701            .map(|validators| validators.into_iter().collect());
1702        if let Some(validators_set) = &validators_set {
1703            if validators_set.contains(identity_pubkey) {
1704                return Err(crate::commands::Error::Dynamic(
1705                    Box::<dyn std::error::Error>::from(format!(
1706                        "the validator's identity pubkey cannot be a {arg_name}: {identity_pubkey}"
1707                    )),
1708                ));
1709            }
1710        }
1711        Ok(validators_set)
1712    } else {
1713        Ok(None)
1714    }
1715}
1716
1717#[cfg(test)]
1718mod tests {
1719    use {
1720        super::*,
1721        crate::cli::thread_args::thread_args,
1722        solana_rpc::rpc::MAX_REQUEST_BODY_SIZE,
1723        std::net::{IpAddr, Ipv4Addr},
1724    };
1725
1726    impl Default for RunArgs {
1727        fn default() -> Self {
1728            let identity_keypair = Keypair::new();
1729            let logfile = format!("agave-validator-{}.log", identity_keypair.pubkey());
1730            let entrypoints = vec![];
1731            let known_validators = None;
1732
1733            RunArgs {
1734                identity_keypair,
1735                logfile,
1736                entrypoints,
1737                known_validators,
1738                socket_addr_space: SocketAddrSpace::Global,
1739                rpc_bootstrap_config: RpcBootstrapConfig::default(),
1740                blockstore_options: BlockstoreOptions::default(),
1741                json_rpc_config: JsonRpcConfig {
1742                    health_check_slot_distance: 128,
1743                    max_multiple_accounts: Some(100),
1744                    rpc_threads: num_cpus::get(),
1745                    rpc_blocking_threads: 1.max(num_cpus::get() / 4),
1746                    max_request_body_size: Some(MAX_REQUEST_BODY_SIZE),
1747                    ..JsonRpcConfig::default()
1748                },
1749                pub_sub_config: PubSubConfig {
1750                    worker_threads: 4,
1751                    notification_threads: None,
1752                    queue_capacity_items:
1753                        solana_rpc::rpc_pubsub_service::DEFAULT_QUEUE_CAPACITY_ITEMS,
1754                    ..PubSubConfig::default_for_tests()
1755                },
1756            }
1757        }
1758    }
1759
1760    impl Clone for RunArgs {
1761        fn clone(&self) -> Self {
1762            RunArgs {
1763                identity_keypair: self.identity_keypair.insecure_clone(),
1764                logfile: self.logfile.clone(),
1765                entrypoints: self.entrypoints.clone(),
1766                known_validators: self.known_validators.clone(),
1767                socket_addr_space: self.socket_addr_space,
1768                rpc_bootstrap_config: self.rpc_bootstrap_config.clone(),
1769                blockstore_options: self.blockstore_options.clone(),
1770                json_rpc_config: self.json_rpc_config.clone(),
1771                pub_sub_config: self.pub_sub_config.clone(),
1772            }
1773        }
1774    }
1775
1776    fn verify_args_struct_by_command(
1777        default_args: &DefaultArgs,
1778        args: Vec<&str>,
1779        expected_args: RunArgs,
1780    ) {
1781        let app = add_args(App::new("run_command"), default_args)
1782            .args(&thread_args(&default_args.thread_args));
1783
1784        crate::commands::tests::verify_args_struct_by_command::<RunArgs>(
1785            app,
1786            [&["run_command"], &args[..]].concat(),
1787            expected_args,
1788        );
1789    }
1790
1791    #[test]
1792    fn verify_args_struct_by_command_run_with_identity() {
1793        let default_args = DefaultArgs::default();
1794        let default_run_args = RunArgs::default();
1795
1796        // generate a keypair
1797        let tmp_dir = tempfile::tempdir().unwrap();
1798        let file = tmp_dir.path().join("id.json");
1799        let keypair = default_run_args.identity_keypair.insecure_clone();
1800        solana_keypair::write_keypair_file(&keypair, &file).unwrap();
1801
1802        let expected_args = RunArgs {
1803            identity_keypair: keypair.insecure_clone(),
1804            ..default_run_args
1805        };
1806
1807        // short arg
1808        {
1809            verify_args_struct_by_command(
1810                &default_args,
1811                vec!["-i", file.to_str().unwrap()],
1812                expected_args.clone(),
1813            );
1814        }
1815
1816        // long arg
1817        {
1818            verify_args_struct_by_command(
1819                &default_args,
1820                vec!["--identity", file.to_str().unwrap()],
1821                expected_args.clone(),
1822            );
1823        }
1824    }
1825
1826    pub fn verify_args_struct_by_command_run_with_identity_setup(
1827        default_run_args: RunArgs,
1828        args: Vec<&str>,
1829        expected_args: RunArgs,
1830    ) {
1831        let default_args = DefaultArgs::default();
1832
1833        // generate a keypair
1834        let tmp_dir = tempfile::tempdir().unwrap();
1835        let file = tmp_dir.path().join("id.json");
1836        let keypair = default_run_args.identity_keypair.insecure_clone();
1837        solana_keypair::write_keypair_file(&keypair, &file).unwrap();
1838
1839        let args = [&["--identity", file.to_str().unwrap()], &args[..]].concat();
1840        verify_args_struct_by_command(&default_args, args, expected_args);
1841    }
1842
1843    pub fn verify_args_struct_by_command_run_is_error_with_identity_setup(
1844        default_run_args: RunArgs,
1845        args: Vec<&str>,
1846    ) {
1847        let default_args = DefaultArgs::default();
1848
1849        // generate a keypair
1850        let tmp_dir = tempfile::tempdir().unwrap();
1851        let file = tmp_dir.path().join("id.json");
1852        let keypair = default_run_args.identity_keypair.insecure_clone();
1853        solana_keypair::write_keypair_file(&keypair, &file).unwrap();
1854
1855        let app = add_args(App::new("run_command"), &default_args)
1856            .args(&thread_args(&default_args.thread_args));
1857
1858        crate::commands::tests::verify_args_struct_by_command_is_error::<RunArgs>(
1859            app,
1860            [
1861                &["run_command"],
1862                &["--identity", file.to_str().unwrap()][..],
1863                &args[..],
1864            ]
1865            .concat(),
1866        );
1867    }
1868
1869    #[test]
1870    fn verify_args_struct_by_command_run_with_log() {
1871        let default_run_args = RunArgs::default();
1872
1873        // default
1874        {
1875            let expected_args = RunArgs {
1876                logfile: "agave-validator-".to_string()
1877                    + &default_run_args.identity_keypair.pubkey().to_string()
1878                    + ".log",
1879                ..default_run_args.clone()
1880            };
1881            verify_args_struct_by_command_run_with_identity_setup(
1882                default_run_args.clone(),
1883                vec![],
1884                expected_args,
1885            );
1886        }
1887
1888        // short arg
1889        {
1890            let expected_args = RunArgs {
1891                logfile: "-".to_string(),
1892                ..default_run_args.clone()
1893            };
1894            verify_args_struct_by_command_run_with_identity_setup(
1895                default_run_args.clone(),
1896                vec!["-o", "-"],
1897                expected_args,
1898            );
1899        }
1900
1901        // long arg
1902        {
1903            let expected_args = RunArgs {
1904                logfile: "custom_log.log".to_string(),
1905                ..default_run_args.clone()
1906            };
1907            verify_args_struct_by_command_run_with_identity_setup(
1908                default_run_args.clone(),
1909                vec!["--log", "custom_log.log"],
1910                expected_args,
1911            );
1912        }
1913    }
1914
1915    #[test]
1916    fn verify_args_struct_by_command_run_with_entrypoints() {
1917        // short arg + single entrypoint
1918        {
1919            let default_run_args = RunArgs::default();
1920            let expected_args = RunArgs {
1921                entrypoints: vec![SocketAddr::new(
1922                    IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
1923                    8000,
1924                )],
1925                ..default_run_args.clone()
1926            };
1927            verify_args_struct_by_command_run_with_identity_setup(
1928                default_run_args.clone(),
1929                vec!["-n", "127.0.0.1:8000"],
1930                expected_args,
1931            );
1932        }
1933
1934        // long arg + single entrypoint
1935        {
1936            let default_run_args = RunArgs::default();
1937            let expected_args = RunArgs {
1938                entrypoints: vec![SocketAddr::new(
1939                    IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
1940                    8000,
1941                )],
1942                ..default_run_args.clone()
1943            };
1944            verify_args_struct_by_command_run_with_identity_setup(
1945                default_run_args.clone(),
1946                vec!["--entrypoint", "127.0.0.1:8000"],
1947                expected_args,
1948            );
1949        }
1950
1951        // long arg + multiple entrypoints
1952        {
1953            let default_run_args = RunArgs::default();
1954            let expected_args = RunArgs {
1955                entrypoints: vec![
1956                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000),
1957                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8001),
1958                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8002),
1959                ],
1960                ..default_run_args.clone()
1961            };
1962            verify_args_struct_by_command_run_with_identity_setup(
1963                default_run_args.clone(),
1964                vec![
1965                    "--entrypoint",
1966                    "127.0.0.1:8000",
1967                    "--entrypoint",
1968                    "127.0.0.1:8001",
1969                    "--entrypoint",
1970                    "127.0.0.1:8002",
1971                ],
1972                expected_args,
1973            );
1974        }
1975
1976        // long arg + duplicate entrypoints
1977        {
1978            let default_run_args = RunArgs::default();
1979            let expected_args = RunArgs {
1980                entrypoints: vec![
1981                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000),
1982                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8001),
1983                    SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8002),
1984                ],
1985                ..default_run_args.clone()
1986            };
1987            verify_args_struct_by_command_run_with_identity_setup(
1988                default_run_args.clone(),
1989                vec![
1990                    "--entrypoint",
1991                    "127.0.0.1:8000",
1992                    "--entrypoint",
1993                    "127.0.0.1:8001",
1994                    "--entrypoint",
1995                    "127.0.0.1:8002",
1996                    "--entrypoint",
1997                    "127.0.0.1:8000",
1998                ],
1999                expected_args,
2000            );
2001        }
2002    }
2003
2004    #[test]
2005    fn verify_args_struct_by_command_run_with_known_validators() {
2006        // long arg + single known validator
2007        {
2008            let default_run_args = RunArgs::default();
2009            let known_validators_pubkey = Pubkey::new_unique();
2010            let known_validators = Some(HashSet::from([known_validators_pubkey]));
2011            let expected_args = RunArgs {
2012                known_validators,
2013                ..default_run_args.clone()
2014            };
2015            verify_args_struct_by_command_run_with_identity_setup(
2016                default_run_args,
2017                vec!["--known-validator", &known_validators_pubkey.to_string()],
2018                expected_args,
2019            );
2020        }
2021
2022        // alias + single known validator
2023        {
2024            let default_run_args = RunArgs::default();
2025            let known_validators_pubkey = Pubkey::new_unique();
2026            let known_validators = Some(HashSet::from([known_validators_pubkey]));
2027            let expected_args = RunArgs {
2028                known_validators,
2029                ..default_run_args.clone()
2030            };
2031            verify_args_struct_by_command_run_with_identity_setup(
2032                default_run_args,
2033                vec!["--trusted-validator", &known_validators_pubkey.to_string()],
2034                expected_args,
2035            );
2036        }
2037
2038        // long arg + multiple known validators
2039        {
2040            let default_run_args = RunArgs::default();
2041            let known_validators_pubkey_1 = Pubkey::new_unique();
2042            let known_validators_pubkey_2 = Pubkey::new_unique();
2043            let known_validators_pubkey_3 = Pubkey::new_unique();
2044            let known_validators = Some(HashSet::from([
2045                known_validators_pubkey_1,
2046                known_validators_pubkey_2,
2047                known_validators_pubkey_3,
2048            ]));
2049            let expected_args = RunArgs {
2050                known_validators,
2051                ..default_run_args.clone()
2052            };
2053            verify_args_struct_by_command_run_with_identity_setup(
2054                default_run_args,
2055                vec![
2056                    "--known-validator",
2057                    &known_validators_pubkey_1.to_string(),
2058                    "--known-validator",
2059                    &known_validators_pubkey_2.to_string(),
2060                    "--known-validator",
2061                    &known_validators_pubkey_3.to_string(),
2062                ],
2063                expected_args,
2064            );
2065        }
2066
2067        // long arg + duplicate known validators
2068        {
2069            let default_run_args = RunArgs::default();
2070            let known_validators_pubkey_1 = Pubkey::new_unique();
2071            let known_validators_pubkey_2 = Pubkey::new_unique();
2072            let known_validators = Some(HashSet::from([
2073                known_validators_pubkey_1,
2074                known_validators_pubkey_2,
2075            ]));
2076            let expected_args = RunArgs {
2077                known_validators,
2078                ..default_run_args.clone()
2079            };
2080            verify_args_struct_by_command_run_with_identity_setup(
2081                default_run_args,
2082                vec![
2083                    "--known-validator",
2084                    &known_validators_pubkey_1.to_string(),
2085                    "--known-validator",
2086                    &known_validators_pubkey_2.to_string(),
2087                    "--known-validator",
2088                    &known_validators_pubkey_1.to_string(),
2089                ],
2090                expected_args,
2091            );
2092        }
2093
2094        // use identity pubkey as known validator
2095        {
2096            let default_args = DefaultArgs::default();
2097            let default_run_args = RunArgs::default();
2098
2099            // generate a keypair
2100            let tmp_dir = tempfile::tempdir().unwrap();
2101            let file = tmp_dir.path().join("id.json");
2102            solana_keypair::write_keypair_file(&default_run_args.identity_keypair, &file).unwrap();
2103
2104            let matches = add_args(App::new("run_command"), &default_args).get_matches_from(vec![
2105                "run_command",
2106                "--identity",
2107                file.to_str().unwrap(),
2108                "--known-validator",
2109                &default_run_args.identity_keypair.pubkey().to_string(),
2110            ]);
2111            let result = RunArgs::from_clap_arg_match(&matches);
2112            assert!(result.is_err());
2113            let error = result.unwrap_err();
2114            assert_eq!(
2115                error.to_string(),
2116                format!(
2117                    "the validator's identity pubkey cannot be a known validator: {}",
2118                    default_run_args.identity_keypair.pubkey()
2119                )
2120            );
2121        }
2122    }
2123
2124    #[test]
2125    fn verify_args_struct_by_command_run_with_max_genesis_archive_unpacked_size() {
2126        // long arg
2127        {
2128            let default_run_args = RunArgs::default();
2129            let max_genesis_archive_unpacked_size = 1000000000;
2130            let expected_args = RunArgs {
2131                rpc_bootstrap_config: RpcBootstrapConfig {
2132                    max_genesis_archive_unpacked_size,
2133                    ..RpcBootstrapConfig::default()
2134                },
2135                ..default_run_args.clone()
2136            };
2137            verify_args_struct_by_command_run_with_identity_setup(
2138                default_run_args,
2139                vec![
2140                    "--max-genesis-archive-unpacked-size",
2141                    &max_genesis_archive_unpacked_size.to_string(),
2142                ],
2143                expected_args,
2144            );
2145        }
2146    }
2147
2148    #[test]
2149    fn verify_args_struct_by_command_run_with_allow_private_addr() {
2150        let default_run_args = RunArgs::default();
2151        let expected_args = RunArgs {
2152            socket_addr_space: SocketAddrSpace::Unspecified,
2153            ..default_run_args.clone()
2154        };
2155        verify_args_struct_by_command_run_with_identity_setup(
2156            default_run_args,
2157            vec!["--allow-private-addr"],
2158            expected_args,
2159        );
2160    }
2161}