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#[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 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 .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") .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 .long("enable-banking-trace")
1558 .value_name("BYTES")
1559 .validator(is_parsable::<DirByteLimit>)
1560 .takes_value(true)
1561 .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 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 {
1809 verify_args_struct_by_command(
1810 &default_args,
1811 vec!["-i", file.to_str().unwrap()],
1812 expected_args.clone(),
1813 );
1814 }
1815
1816 {
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 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 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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
2096 let default_args = DefaultArgs::default();
2097 let default_run_args = RunArgs::default();
2098
2099 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 {
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}