solana_validator/
cli.rs

1use {
2    clap::{
3        crate_description, crate_name, App, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand,
4    },
5    log::warn,
6    solana_accounts_db::{
7        accounts_db::{
8            DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE, DEFAULT_ACCOUNTS_SHRINK_RATIO,
9        },
10        hardened_unpack::MAX_GENESIS_ARCHIVE_UNPACKED_SIZE,
11    },
12    solana_clap_utils::{
13        hidden_unless_forced,
14        input_validators::{
15            is_keypair, is_keypair_or_ask_keyword, is_parsable, is_pow2, is_pubkey,
16            is_pubkey_or_keypair, is_slot, is_url_or_moniker, is_valid_percentage, is_within_range,
17            validate_maximum_full_snapshot_archives_to_retain,
18            validate_maximum_incremental_snapshot_archives_to_retain,
19        },
20        keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
21    },
22    solana_core::{
23        banking_trace::{DirByteLimit, BANKING_TRACE_DIR_DEFAULT_BYTE_LIMIT},
24        validator::{BlockProductionMethod, BlockVerificationMethod},
25    },
26    solana_faucet::faucet::{self, FAUCET_PORT},
27    solana_ledger::use_snapshot_archives_at_startup,
28    solana_net_utils::{MINIMUM_VALIDATOR_PORT_RANGE_WIDTH, VALIDATOR_PORT_RANGE},
29    solana_rpc::{rpc::MAX_REQUEST_BODY_SIZE, rpc_pubsub_service::PubSubConfig},
30    solana_rpc_client_api::request::MAX_MULTIPLE_ACCOUNTS,
31    solana_runtime::{
32        snapshot_bank_utils::{
33            DEFAULT_FULL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS,
34            DEFAULT_INCREMENTAL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS,
35        },
36        snapshot_utils::{
37            SnapshotVersion, DEFAULT_ARCHIVE_COMPRESSION,
38            DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN,
39            DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN, SUPPORTED_ARCHIVE_COMPRESSION,
40        },
41    },
42    solana_sdk::{
43        clock::Slot, epoch_schedule::MINIMUM_SLOTS_PER_EPOCH, hash::Hash, quic::QUIC_PORT_OFFSET,
44        rpc_port,
45    },
46    solana_send_transaction_service::send_transaction_service::{
47        self, MAX_BATCH_SEND_RATE_MS, MAX_TRANSACTION_BATCH_SIZE,
48    },
49    solana_tpu_client::tpu_client::DEFAULT_TPU_CONNECTION_POOL_SIZE,
50    std::{path::PathBuf, str::FromStr},
51};
52
53const EXCLUDE_KEY: &str = "account-index-exclude-key";
54const INCLUDE_KEY: &str = "account-index-include-key";
55// The default minimal snapshot download speed (bytes/second)
56const DEFAULT_MIN_SNAPSHOT_DOWNLOAD_SPEED: u64 = 10485760;
57// The maximum times of snapshot download abort and retry
58const MAX_SNAPSHOT_DOWNLOAD_ABORT: u32 = 5;
59// We've observed missed leader slots leading to deadlocks on test validator
60// with less than 2 ticks per slot.
61const MINIMUM_TICKS_PER_SLOT: u64 = 2;
62
63pub fn app<'a>(version: &'a str, default_args: &'a DefaultArgs) -> App<'a, 'a> {
64    return App::new(crate_name!()).about(crate_description!())
65        .version(version)
66        .setting(AppSettings::VersionlessSubcommands)
67        .setting(AppSettings::InferSubcommands)
68        .arg(
69            Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
70                .long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
71                .help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
72        )
73        .arg(
74            Arg::with_name("identity")
75                .short("i")
76                .long("identity")
77                .value_name("KEYPAIR")
78                .takes_value(true)
79                .validator(is_keypair_or_ask_keyword)
80                .help("Validator identity keypair"),
81        )
82        .arg(
83            Arg::with_name("authorized_voter_keypairs")
84                .long("authorized-voter")
85                .value_name("KEYPAIR")
86                .takes_value(true)
87                .validator(is_keypair_or_ask_keyword)
88                .requires("vote_account")
89                .multiple(true)
90                .help("Include an additional authorized voter keypair. \
91                       May be specified multiple times. \
92                       [default: the --identity keypair]"),
93        )
94        .arg(
95            Arg::with_name("vote_account")
96                .long("vote-account")
97                .value_name("ADDRESS")
98                .takes_value(true)
99                .validator(is_pubkey_or_keypair)
100                .requires("identity")
101                .help("Validator vote account public key.  \
102                       If unspecified voting will be disabled. \
103                       The authorized voter for the account must either be the \
104                       --identity keypair or with the --authorized-voter argument")
105        )
106        .arg(
107            Arg::with_name("init_complete_file")
108                .long("init-complete-file")
109                .value_name("FILE")
110                .takes_value(true)
111                .help("Create this file if it doesn't already exist \
112                       once validator initialization is complete"),
113        )
114        .arg(
115            Arg::with_name("ledger_path")
116                .short("l")
117                .long("ledger")
118                .value_name("DIR")
119                .takes_value(true)
120                .required(true)
121                .default_value(&default_args.ledger_path)
122                .help("Use DIR as ledger location"),
123        )
124        .arg(
125            Arg::with_name("entrypoint")
126                .short("n")
127                .long("entrypoint")
128                .value_name("HOST:PORT")
129                .takes_value(true)
130                .multiple(true)
131                .validator(solana_net_utils::is_host_port)
132                .help("Rendezvous with the cluster at this gossip entrypoint"),
133        )
134        .arg(
135            Arg::with_name("no_snapshot_fetch")
136                .long("no-snapshot-fetch")
137                .takes_value(false)
138                .help("Do not attempt to fetch a snapshot from the cluster, \
139                      start from a local snapshot if present"),
140        )
141        .arg(
142            Arg::with_name("no_genesis_fetch")
143                .long("no-genesis-fetch")
144                .takes_value(false)
145                .help("Do not fetch genesis from the cluster"),
146        )
147        .arg(
148            Arg::with_name("no_voting")
149                .long("no-voting")
150                .takes_value(false)
151                .help("Launch validator without voting"),
152        )
153        .arg(
154            Arg::with_name("check_vote_account")
155                .long("check-vote-account")
156                .takes_value(true)
157                .value_name("RPC_URL")
158                .requires("entrypoint")
159                .conflicts_with_all(&["no_check_vote_account", "no_voting"])
160                .help("Sanity check vote account state at startup. The JSON RPC endpoint at RPC_URL must expose `--full-rpc-api`")
161        )
162        .arg(
163            Arg::with_name("restricted_repair_only_mode")
164                .long("restricted-repair-only-mode")
165                .takes_value(false)
166                .help("Do not publish the Gossip, TPU, TVU or Repair Service ports causing \
167                       the validator to operate in a limited capacity that reduces its \
168                       exposure to the rest of the cluster. \
169                       \
170                       The --no-voting flag is implicit when this flag is enabled \
171                      "),
172        )
173        .arg(
174            Arg::with_name("dev_halt_at_slot")
175                .long("dev-halt-at-slot")
176                .value_name("SLOT")
177                .validator(is_slot)
178                .takes_value(true)
179                .help("Halt the validator when it reaches the given slot"),
180        )
181        .arg(
182            Arg::with_name("rpc_port")
183                .long("rpc-port")
184                .value_name("PORT")
185                .takes_value(true)
186                .validator(port_validator)
187                .help("Enable JSON RPC on this port, and the next port for the RPC websocket"),
188        )
189        .arg(
190            Arg::with_name("full_rpc_api")
191                .long("full-rpc-api")
192                .conflicts_with("minimal_rpc_api")
193                .takes_value(false)
194                .help("Expose RPC methods for querying chain state and transaction history"),
195        )
196        .arg(
197            Arg::with_name("obsolete_v1_7_rpc_api")
198                .long("enable-rpc-obsolete_v1_7")
199                .takes_value(false)
200                .help("Enable the obsolete RPC methods removed in v1.7"),
201        )
202        .arg(
203            Arg::with_name("private_rpc")
204                .long("private-rpc")
205                .takes_value(false)
206                .help("Do not publish the RPC port for use by others")
207        )
208        .arg(
209            Arg::with_name("no_port_check")
210                .long("no-port-check")
211                .takes_value(false)
212                .hidden(hidden_unless_forced())
213                .help("Do not perform TCP/UDP reachable port checks at start-up")
214        )
215        .arg(
216            Arg::with_name("enable_rpc_transaction_history")
217                .long("enable-rpc-transaction-history")
218                .takes_value(false)
219                .help("Enable historical transaction info over JSON RPC, \
220                       including the 'getConfirmedBlock' API.  \
221                       This will cause an increase in disk usage and IOPS"),
222        )
223        .arg(
224            Arg::with_name("enable_rpc_bigtable_ledger_storage")
225                .long("enable-rpc-bigtable-ledger-storage")
226                .requires("enable_rpc_transaction_history")
227                .takes_value(false)
228                .help("Fetch historical transaction info from a BigTable instance \
229                       as a fallback to local ledger data"),
230        )
231        .arg(
232            Arg::with_name("enable_bigtable_ledger_upload")
233                .long("enable-bigtable-ledger-upload")
234                .requires("enable_rpc_transaction_history")
235                .takes_value(false)
236                .help("Upload new confirmed blocks into a BigTable instance"),
237        )
238        .arg(
239            Arg::with_name("enable_extended_tx_metadata_storage")
240                .long("enable-extended-tx-metadata-storage")
241                .requires("enable_rpc_transaction_history")
242                .takes_value(false)
243                .help("Include CPI inner instructions, logs, and return data in \
244                       the historical transaction info stored"),
245        )
246        .arg(
247            Arg::with_name("rpc_max_multiple_accounts")
248                .long("rpc-max-multiple-accounts")
249                .value_name("MAX ACCOUNTS")
250                .takes_value(true)
251                .default_value(&default_args.rpc_max_multiple_accounts)
252                .help("Override the default maximum accounts accepted by \
253                       the getMultipleAccounts JSON RPC method")
254        )
255        .arg(
256            Arg::with_name("health_check_slot_distance")
257                .long("health-check-slot-distance")
258                .value_name("SLOT_DISTANCE")
259                .takes_value(true)
260                .default_value(&default_args.health_check_slot_distance)
261                .help("Report this validator healthy if its latest optimistically confirmed slot \
262                       that has been replayed is no further behind than this number of slots from \
263                       the cluster latest optimistically confirmed slot")
264        )
265        .arg(
266            Arg::with_name("rpc_faucet_addr")
267                .long("rpc-faucet-address")
268                .value_name("HOST:PORT")
269                .takes_value(true)
270                .validator(solana_net_utils::is_host_port)
271                .help("Enable the JSON RPC 'requestAirdrop' API with this faucet address."),
272        )
273        .arg(
274            Arg::with_name("account_paths")
275                .long("accounts")
276                .value_name("PATHS")
277                .takes_value(true)
278                .multiple(true)
279                .help("Comma separated persistent accounts location"),
280        )
281        .arg(
282            Arg::with_name("account_shrink_path")
283                .long("account-shrink-path")
284                .value_name("PATH")
285                .takes_value(true)
286                .multiple(true)
287                .help("Path to accounts shrink path which can hold a compacted account set."),
288        )
289        .arg(
290            Arg::with_name("accounts_hash_cache_path")
291                .long("accounts-hash-cache-path")
292                .value_name("PATH")
293                .takes_value(true)
294                .help("Use PATH as accounts hash cache location [default: <LEDGER>/accounts_hash_cache]"),
295        )
296        .arg(
297            Arg::with_name("snapshots")
298                .long("snapshots")
299                .value_name("DIR")
300                .takes_value(true)
301                .help("Use DIR as snapshot location [default: --ledger value]"),
302        )
303        .arg(
304            Arg::with_name(use_snapshot_archives_at_startup::cli::NAME)
305                .long(use_snapshot_archives_at_startup::cli::LONG_ARG)
306                .takes_value(true)
307                .possible_values(use_snapshot_archives_at_startup::cli::POSSIBLE_VALUES)
308                .default_value(use_snapshot_archives_at_startup::cli::default_value())
309                .help(use_snapshot_archives_at_startup::cli::HELP)
310                .long_help(use_snapshot_archives_at_startup::cli::LONG_HELP)
311        )
312        .arg(
313            Arg::with_name("incremental_snapshot_archive_path")
314                .long("incremental-snapshot-archive-path")
315                .conflicts_with("no-incremental-snapshots")
316                .value_name("DIR")
317                .takes_value(true)
318                .help("Use DIR as separate location for incremental snapshot archives [default: --snapshots value]"),
319        )
320        .arg(
321            Arg::with_name("tower")
322                .long("tower")
323                .value_name("DIR")
324                .takes_value(true)
325                .help("Use DIR as file tower storage location [default: --ledger value]"),
326        )
327        .arg(
328            Arg::with_name("tower_storage")
329                .long("tower-storage")
330                .possible_values(&["file", "etcd"])
331                .default_value(&default_args.tower_storage)
332                .takes_value(true)
333                .help("Where to store the tower"),
334        )
335        .arg(
336            Arg::with_name("etcd_endpoint")
337                .long("etcd-endpoint")
338                .required_if("tower_storage", "etcd")
339                .value_name("HOST:PORT")
340                .takes_value(true)
341                .multiple(true)
342                .validator(solana_net_utils::is_host_port)
343                .help("etcd gRPC endpoint to connect with")
344        )
345        .arg(
346            Arg::with_name("etcd_domain_name")
347                .long("etcd-domain-name")
348                .required_if("tower_storage", "etcd")
349                .value_name("DOMAIN")
350                .default_value(&default_args.etcd_domain_name)
351                .takes_value(true)
352                .help("domain name against which to verify the etcd server’s TLS certificate")
353        )
354        .arg(
355            Arg::with_name("etcd_cacert_file")
356                .long("etcd-cacert-file")
357                .required_if("tower_storage", "etcd")
358                .value_name("FILE")
359                .takes_value(true)
360                .help("verify the TLS certificate of the etcd endpoint using this CA bundle")
361        )
362        .arg(
363            Arg::with_name("etcd_key_file")
364                .long("etcd-key-file")
365                .required_if("tower_storage", "etcd")
366                .value_name("FILE")
367                .takes_value(true)
368                .help("TLS key file to use when establishing a connection to the etcd endpoint")
369        )
370        .arg(
371            Arg::with_name("etcd_cert_file")
372                .long("etcd-cert-file")
373                .required_if("tower_storage", "etcd")
374                .value_name("FILE")
375                .takes_value(true)
376                .help("TLS certificate to use when establishing a connection to the etcd endpoint")
377        )
378        .arg(
379            Arg::with_name("gossip_port")
380                .long("gossip-port")
381                .value_name("PORT")
382                .takes_value(true)
383                .help("Gossip port number for the validator"),
384        )
385        .arg(
386            Arg::with_name("gossip_host")
387                .long("gossip-host")
388                .value_name("HOST")
389                .takes_value(true)
390                .validator(solana_net_utils::is_host)
391                .help("Gossip DNS name or IP address for the validator to advertise in gossip \
392                       [default: ask --entrypoint, or 127.0.0.1 when --entrypoint is not provided]"),
393        )
394        .arg(
395            Arg::with_name("public_tpu_addr")
396                .long("public-tpu-address")
397                .alias("tpu-host-addr")
398                .value_name("HOST:PORT")
399                .takes_value(true)
400                .validator(solana_net_utils::is_host_port)
401                .help("Specify TPU address to advertise in gossip [default: ask --entrypoint or localhost\
402                    when --entrypoint is not provided]"),
403        )
404        .arg(
405            Arg::with_name("public_tpu_forwards_addr")
406                .long("public-tpu-forwards-address")
407                .value_name("HOST:PORT")
408                .takes_value(true)
409                .validator(solana_net_utils::is_host_port)
410                .help("Specify TPU Forwards address to advertise in gossip [default: ask --entrypoint or localhost\
411                    when --entrypoint is not provided]"),
412        )
413        .arg(
414            Arg::with_name("public_rpc_addr")
415                .long("public-rpc-address")
416                .value_name("HOST:PORT")
417                .takes_value(true)
418                .conflicts_with("private_rpc")
419                .validator(solana_net_utils::is_host_port)
420                .help("RPC address for the validator to advertise publicly in gossip. \
421                      Useful for validators running behind a load balancer or proxy \
422                      [default: use --rpc-bind-address / --rpc-port]"),
423        )
424        .arg(
425            Arg::with_name("dynamic_port_range")
426                .long("dynamic-port-range")
427                .value_name("MIN_PORT-MAX_PORT")
428                .takes_value(true)
429                .default_value(&default_args.dynamic_port_range)
430                .validator(port_range_validator)
431                .help("Range to use for dynamically assigned ports"),
432        )
433        .arg(
434            Arg::with_name("maximum_local_snapshot_age")
435                .long("maximum-local-snapshot-age")
436                .value_name("NUMBER_OF_SLOTS")
437                .takes_value(true)
438                .default_value(&default_args.maximum_local_snapshot_age)
439                .help("Reuse a local snapshot if it's less than this many \
440                       slots behind the highest snapshot available for \
441                       download from other validators"),
442        )
443        .arg(
444            Arg::with_name("no_incremental_snapshots")
445                .long("no-incremental-snapshots")
446                .takes_value(false)
447                .help("Disable incremental snapshots")
448                .long_help("Disable incremental snapshots by setting this flag. \
449                   When enabled, --snapshot-interval-slots will set the \
450                   incremental snapshot interval. To set the full snapshot \
451                   interval, use --full-snapshot-interval-slots.")
452        )
453        .arg(
454            Arg::with_name("incremental_snapshot_interval_slots")
455                .long("incremental-snapshot-interval-slots")
456                .alias("snapshot-interval-slots")
457                .value_name("NUMBER")
458                .takes_value(true)
459                .default_value(&default_args.incremental_snapshot_archive_interval_slots)
460                .help("Number of slots between generating snapshots, \
461                      0 to disable snapshots"),
462        )
463        .arg(
464            Arg::with_name("full_snapshot_interval_slots")
465                .long("full-snapshot-interval-slots")
466                .value_name("NUMBER")
467                .takes_value(true)
468                .default_value(&default_args.full_snapshot_archive_interval_slots)
469                .help("Number of slots between generating full snapshots. \
470                    Must be a multiple of the incremental snapshot interval.")
471        )
472        .arg(
473            Arg::with_name("maximum_full_snapshots_to_retain")
474                .long("maximum-full-snapshots-to-retain")
475                .alias("maximum-snapshots-to-retain")
476                .value_name("NUMBER")
477                .takes_value(true)
478                .default_value(&default_args.maximum_full_snapshot_archives_to_retain)
479                .validator(validate_maximum_full_snapshot_archives_to_retain)
480                .help("The maximum number of full snapshot archives to hold on to when purging older snapshots.")
481        )
482        .arg(
483            Arg::with_name("maximum_incremental_snapshots_to_retain")
484                .long("maximum-incremental-snapshots-to-retain")
485                .value_name("NUMBER")
486                .takes_value(true)
487                .default_value(&default_args.maximum_incremental_snapshot_archives_to_retain)
488                .validator(validate_maximum_incremental_snapshot_archives_to_retain)
489                .help("The maximum number of incremental snapshot archives to hold on to when purging older snapshots.")
490        )
491        .arg(
492            Arg::with_name("snapshot_packager_niceness_adj")
493                .long("snapshot-packager-niceness-adjustment")
494                .value_name("ADJUSTMENT")
495                .takes_value(true)
496                .validator(solana_perf::thread::is_niceness_adjustment_valid)
497                .default_value(&default_args.snapshot_packager_niceness_adjustment)
498                .help("Add this value to niceness of snapshot packager thread. Negative value \
499                      increases priority, positive value decreases priority.")
500        )
501        .arg(
502            Arg::with_name("minimal_snapshot_download_speed")
503                .long("minimal-snapshot-download-speed")
504                .value_name("MINIMAL_SNAPSHOT_DOWNLOAD_SPEED")
505                .takes_value(true)
506                .default_value(&default_args.min_snapshot_download_speed)
507                .help("The minimal speed of snapshot downloads measured in bytes/second. \
508                      If the initial download speed falls below this threshold, the system will \
509                      retry the download against a different rpc node."),
510        )
511        .arg(
512            Arg::with_name("maximum_snapshot_download_abort")
513                .long("maximum-snapshot-download-abort")
514                .value_name("MAXIMUM_SNAPSHOT_DOWNLOAD_ABORT")
515                .takes_value(true)
516                .default_value(&default_args.max_snapshot_download_abort)
517                .help("The maximum number of times to abort and retry when encountering a \
518                      slow snapshot download."),
519        )
520        .arg(
521            Arg::with_name("contact_debug_interval")
522                .long("contact-debug-interval")
523                .value_name("CONTACT_DEBUG_INTERVAL")
524                .takes_value(true)
525                .default_value(&default_args.contact_debug_interval)
526                .help("Milliseconds between printing contact debug from gossip."),
527        )
528        .arg(
529            Arg::with_name("no_poh_speed_test")
530                .long("no-poh-speed-test")
531                .hidden(hidden_unless_forced())
532                .help("Skip the check for PoH speed."),
533        )
534        .arg(
535            Arg::with_name("no_os_network_limits_test")
536                .hidden(hidden_unless_forced())
537                .long("no-os-network-limits-test")
538                .help("Skip checks for OS network limits.")
539        )
540        .arg(
541            Arg::with_name("no_os_memory_stats_reporting")
542                .long("no-os-memory-stats-reporting")
543                .hidden(hidden_unless_forced())
544                .help("Disable reporting of OS memory statistics.")
545        )
546        .arg(
547            Arg::with_name("no_os_network_stats_reporting")
548                .long("no-os-network-stats-reporting")
549                .hidden(hidden_unless_forced())
550                .help("Disable reporting of OS network statistics.")
551        )
552        .arg(
553            Arg::with_name("no_os_cpu_stats_reporting")
554                .long("no-os-cpu-stats-reporting")
555                .hidden(hidden_unless_forced())
556                .help("Disable reporting of OS CPU statistics.")
557        )
558        .arg(
559            Arg::with_name("no_os_disk_stats_reporting")
560                .long("no-os-disk-stats-reporting")
561                .hidden(hidden_unless_forced())
562                .help("Disable reporting of OS disk statistics.")
563        )
564        .arg(
565            Arg::with_name("snapshot_version")
566                .long("snapshot-version")
567                .value_name("SNAPSHOT_VERSION")
568                .validator(is_parsable::<SnapshotVersion>)
569                .takes_value(true)
570                .default_value(default_args.snapshot_version.into())
571                .help("Output snapshot version"),
572        )
573        .arg(
574            Arg::with_name("limit_ledger_size")
575                .long("limit-ledger-size")
576                .value_name("SHRED_COUNT")
577                .takes_value(true)
578                .min_values(0)
579                .max_values(1)
580                /* .default_value() intentionally not used here! */
581                .help("Keep this amount of shreds in root slots."),
582        )
583        .arg(
584            Arg::with_name("rocksdb_shred_compaction")
585                .long("rocksdb-shred-compaction")
586                .value_name("ROCKSDB_COMPACTION_STYLE")
587                .takes_value(true)
588                .possible_values(&["level", "fifo"])
589                .default_value(&default_args.rocksdb_shred_compaction)
590                .help("Controls how RocksDB compacts shreds. \
591                       *WARNING*: You will lose your ledger data when you switch between options. \
592                       Possible values are: \
593                       'level': stores shreds using RocksDB's default (level) compaction. \
594                       'fifo': stores shreds under RocksDB's FIFO compaction. \
595                           This option is more efficient on disk-write-bytes of the ledger store."),
596        )
597        .arg(
598            Arg::with_name("rocksdb_fifo_shred_storage_size")
599                .long("rocksdb-fifo-shred-storage-size")
600                .value_name("SHRED_STORAGE_SIZE_BYTES")
601                .takes_value(true)
602                .validator(is_parsable::<u64>)
603                .help("The shred storage size in bytes. \
604                       The suggested value is at least 50% of your ledger storage size. \
605                       If this argument is unspecified, we will assign a proper \
606                       value based on --limit-ledger-size.  If --limit-ledger-size \
607                       is not presented, it means there is no limitation on the ledger \
608                       size and thus rocksdb_fifo_shred_storage_size will also be \
609                       unbounded."),
610        )
611        .arg(
612            Arg::with_name("rocksdb_ledger_compression")
613                .hidden(hidden_unless_forced())
614                .long("rocksdb-ledger-compression")
615                .value_name("COMPRESSION_TYPE")
616                .takes_value(true)
617                .possible_values(&["none", "lz4", "snappy", "zlib"])
618                .default_value(&default_args.rocksdb_ledger_compression)
619                .help("The compression algorithm that is used to compress \
620                       transaction status data.  \
621                       Turning on compression can save ~10% of the ledger size."),
622        )
623        .arg(
624            Arg::with_name("rocksdb_perf_sample_interval")
625                .hidden(hidden_unless_forced())
626                .long("rocksdb-perf-sample-interval")
627                .value_name("ROCKS_PERF_SAMPLE_INTERVAL")
628                .takes_value(true)
629                .validator(is_parsable::<usize>)
630                .default_value(&default_args.rocksdb_perf_sample_interval)
631                .help("Controls how often RocksDB read/write performance sample is collected. \
632                       Reads/writes perf samples are collected in 1 / ROCKS_PERF_SAMPLE_INTERVAL sampling rate."),
633        )
634        .arg(
635            Arg::with_name("skip_startup_ledger_verification")
636                .long("skip-startup-ledger-verification")
637                .takes_value(false)
638                .help("Skip ledger verification at validator bootup."),
639        )
640        .arg(
641            Arg::with_name("cuda")
642                .long("cuda")
643                .takes_value(false)
644                .help("Use CUDA"),
645        )
646        .arg(
647            clap::Arg::with_name("require_tower")
648                .long("require-tower")
649                .takes_value(false)
650                .help("Refuse to start if saved tower state is not found"),
651        )
652        .arg(
653            Arg::with_name("expected_genesis_hash")
654                .long("expected-genesis-hash")
655                .value_name("HASH")
656                .takes_value(true)
657                .validator(hash_validator)
658                .help("Require the genesis have this hash"),
659        )
660        .arg(
661            Arg::with_name("expected_bank_hash")
662                .long("expected-bank-hash")
663                .value_name("HASH")
664                .takes_value(true)
665                .validator(hash_validator)
666                .help("When wait-for-supermajority <x>, require the bank at <x> to have this hash"),
667        )
668        .arg(
669            Arg::with_name("expected_shred_version")
670                .long("expected-shred-version")
671                .value_name("VERSION")
672                .takes_value(true)
673                .validator(is_parsable::<u16>)
674                .help("Require the shred version be this value"),
675        )
676        .arg(
677            Arg::with_name("logfile")
678                .short("o")
679                .long("log")
680                .value_name("FILE")
681                .takes_value(true)
682                .help("Redirect logging to the specified file, '-' for standard error. \
683                       Sending the SIGUSR1 signal to the validator process will cause it \
684                       to re-open the log file"),
685        )
686        .arg(
687            Arg::with_name("wait_for_supermajority")
688                .long("wait-for-supermajority")
689                .requires("expected_bank_hash")
690                .value_name("SLOT")
691                .validator(is_slot)
692                .help("After processing the ledger and the next slot is SLOT, wait until a \
693                       supermajority of stake is visible on gossip before starting PoH"),
694        )
695        .arg(
696            Arg::with_name("no_wait_for_vote_to_start_leader")
697                .hidden(hidden_unless_forced())
698                .long("no-wait-for-vote-to-start-leader")
699                .help("If the validator starts up with no ledger, it will wait to start block
700                      production until it sees a vote land in a rooted slot. This prevents
701                      double signing. Turn off to risk double signing a block."),
702        )
703        .arg(
704            Arg::with_name("hard_forks")
705                .long("hard-fork")
706                .value_name("SLOT")
707                .validator(is_slot)
708                .multiple(true)
709                .takes_value(true)
710                .help("Add a hard fork at this slot"),
711        )
712        .arg(
713            Arg::with_name("known_validators")
714                .alias("trusted-validator")
715                .long("known-validator")
716                .validator(is_pubkey)
717                .value_name("VALIDATOR IDENTITY")
718                .multiple(true)
719                .takes_value(true)
720                .help("A snapshot hash must be published in gossip by this validator to be accepted. \
721                       May be specified multiple times. If unspecified any snapshot hash will be accepted"),
722        )
723        .arg(
724            Arg::with_name("debug_key")
725                .long("debug-key")
726                .validator(is_pubkey)
727                .value_name("ADDRESS")
728                .multiple(true)
729                .takes_value(true)
730                .help("Log when transactions are processed which reference a given key."),
731        )
732        .arg(
733            Arg::with_name("only_known_rpc")
734                .alias("no-untrusted-rpc")
735                .long("only-known-rpc")
736                .takes_value(false)
737                .requires("known_validators")
738                .help("Use the RPC service of known validators only")
739        )
740        .arg(
741            Arg::with_name("repair_validators")
742                .long("repair-validator")
743                .validator(is_pubkey)
744                .value_name("VALIDATOR IDENTITY")
745                .multiple(true)
746                .takes_value(true)
747                .help("A list of validators to request repairs from. If specified, repair will not \
748                       request from validators outside this set [default: all validators]")
749        )
750        .arg(
751            Arg::with_name("repair_whitelist")
752                .hidden(hidden_unless_forced())
753                .long("repair-whitelist")
754                .validator(is_pubkey)
755                .value_name("VALIDATOR IDENTITY")
756                .multiple(true)
757                .takes_value(true)
758                .help("A list of validators to prioritize repairs from. If specified, repair requests \
759                       from validators in the list will be prioritized over requests from other validators. \
760                       [default: all validators]")
761        )
762        .arg(
763            Arg::with_name("gossip_validators")
764                .long("gossip-validator")
765                .validator(is_pubkey)
766                .value_name("VALIDATOR IDENTITY")
767                .multiple(true)
768                .takes_value(true)
769                .help("A list of validators to gossip with.  If specified, gossip \
770                      will not push/pull from from validators outside this set. \
771                      [default: all validators]")
772        )
773        .arg(
774            Arg::with_name("tpu_coalesce_ms")
775                .long("tpu-coalesce-ms")
776                .value_name("MILLISECS")
777                .takes_value(true)
778                .validator(is_parsable::<u64>)
779                .help("Milliseconds to wait in the TPU receiver for packet coalescing."),
780        )
781        .arg(
782            Arg::with_name("tpu_use_quic")
783                .long("tpu-use-quic")
784                .takes_value(false)
785                .hidden(hidden_unless_forced())
786                .conflicts_with("tpu_disable_quic")
787                .help("Use QUIC to send transactions."),
788        )
789        .arg(
790            Arg::with_name("tpu_disable_quic")
791                .long("tpu-disable-quic")
792                .takes_value(false)
793                .help("Do not use QUIC to send transactions."),
794        )
795        .arg(
796            Arg::with_name("tpu_enable_udp")
797                .long("tpu-enable-udp")
798                .takes_value(false)
799                .help("Enable UDP for receiving/sending transactions."),
800        )
801        .arg(
802            Arg::with_name("tpu_connection_pool_size")
803                .long("tpu-connection-pool-size")
804                .takes_value(true)
805                .default_value(&default_args.tpu_connection_pool_size)
806                .validator(is_parsable::<usize>)
807                .help("Controls the TPU connection pool size per remote address"),
808        )
809        .arg(
810            Arg::with_name("staked_nodes_overrides")
811                .long("staked-nodes-overrides")
812                .value_name("PATH")
813                .takes_value(true)
814                .help("Provide path to a yaml file with custom overrides for stakes of specific
815                            identities. Overriding the amount of stake this validator considers
816                            as valid for other peers in network. The stake amount is used for calculating
817                            number of QUIC streams permitted from the peer and vote packet sender stage.
818                            Format of the file: `staked_map_id: {<pubkey>: <SOL stake amount>}"),
819        )
820        .arg(
821            Arg::with_name("bind_address")
822                .long("bind-address")
823                .value_name("HOST")
824                .takes_value(true)
825                .validator(solana_net_utils::is_host)
826                .default_value(&default_args.bind_address)
827                .help("IP address to bind the validator ports"),
828        )
829        .arg(
830            Arg::with_name("rpc_bind_address")
831                .long("rpc-bind-address")
832                .value_name("HOST")
833                .takes_value(true)
834                .validator(solana_net_utils::is_host)
835                .help("IP address to bind the RPC port [default: 127.0.0.1 if --private-rpc is present, otherwise use --bind-address]"),
836        )
837        .arg(
838            Arg::with_name("rpc_threads")
839                .long("rpc-threads")
840                .value_name("NUMBER")
841                .validator(is_parsable::<usize>)
842                .takes_value(true)
843                .default_value(&default_args.rpc_threads)
844                .help("Number of threads to use for servicing RPC requests"),
845        )
846        .arg(
847            Arg::with_name("rpc_niceness_adj")
848                .long("rpc-niceness-adjustment")
849                .value_name("ADJUSTMENT")
850                .takes_value(true)
851                .validator(solana_perf::thread::is_niceness_adjustment_valid)
852                .default_value(&default_args.rpc_niceness_adjustment)
853                .help("Add this value to niceness of RPC threads. Negative value \
854                      increases priority, positive value decreases priority.")
855        )
856        .arg(
857            Arg::with_name("rpc_bigtable_timeout")
858                .long("rpc-bigtable-timeout")
859                .value_name("SECONDS")
860                .validator(is_parsable::<u64>)
861                .takes_value(true)
862                .default_value(&default_args.rpc_bigtable_timeout)
863                .help("Number of seconds before timing out RPC requests backed by BigTable"),
864        )
865        .arg(
866            Arg::with_name("rpc_bigtable_instance_name")
867                .long("rpc-bigtable-instance-name")
868                .takes_value(true)
869                .value_name("INSTANCE_NAME")
870                .default_value(&default_args.rpc_bigtable_instance_name)
871                .help("Name of the Bigtable instance to upload to")
872        )
873        .arg(
874            Arg::with_name("rpc_bigtable_app_profile_id")
875                .long("rpc-bigtable-app-profile-id")
876                .takes_value(true)
877                .value_name("APP_PROFILE_ID")
878                .default_value(&default_args.rpc_bigtable_app_profile_id)
879                .help("Bigtable application profile id to use in requests")
880        )
881        .arg(
882            Arg::with_name("rpc_bigtable_max_message_size")
883                .long("rpc-bigtable-max-message-size")
884                .value_name("BYTES")
885                .validator(is_parsable::<usize>)
886                .takes_value(true)
887                .default_value(&default_args.rpc_bigtable_max_message_size)
888                .help("Max encoding and decoding message size used in Bigtable Grpc client"),
889        )
890        .arg(
891            Arg::with_name("rpc_pubsub_worker_threads")
892                .long("rpc-pubsub-worker-threads")
893                .takes_value(true)
894                .value_name("NUMBER")
895                .validator(is_parsable::<usize>)
896                .default_value(&default_args.rpc_pubsub_worker_threads)
897                .help("PubSub worker threads"),
898        )
899        .arg(
900            Arg::with_name("rpc_pubsub_enable_block_subscription")
901                .long("rpc-pubsub-enable-block-subscription")
902                .requires("enable_rpc_transaction_history")
903                .takes_value(false)
904                .help("Enable the unstable RPC PubSub `blockSubscribe` subscription"),
905        )
906        .arg(
907            Arg::with_name("rpc_pubsub_enable_vote_subscription")
908                .long("rpc-pubsub-enable-vote-subscription")
909                .takes_value(false)
910                .help("Enable the unstable RPC PubSub `voteSubscribe` subscription"),
911        )
912        .arg(
913            Arg::with_name("rpc_pubsub_max_connections")
914                .long("rpc-pubsub-max-connections")
915                .value_name("NUMBER")
916                .takes_value(true)
917                .validator(is_parsable::<usize>)
918                .hidden(hidden_unless_forced())
919                .help("The maximum number of connections that RPC PubSub will support. \
920                       This is a hard limit and no new connections beyond this limit can \
921                       be made until an old connection is dropped. (Obsolete)"),
922        )
923        .arg(
924            Arg::with_name("rpc_pubsub_max_fragment_size")
925                .long("rpc-pubsub-max-fragment-size")
926                .value_name("BYTES")
927                .takes_value(true)
928                .validator(is_parsable::<usize>)
929                .hidden(hidden_unless_forced())
930                .help("The maximum length in bytes of acceptable incoming frames. Messages longer \
931                       than this will be rejected. (Obsolete)"),
932        )
933        .arg(
934            Arg::with_name("rpc_pubsub_max_in_buffer_capacity")
935                .long("rpc-pubsub-max-in-buffer-capacity")
936                .value_name("BYTES")
937                .takes_value(true)
938                .validator(is_parsable::<usize>)
939                .hidden(hidden_unless_forced())
940                .help("The maximum size in bytes to which the incoming websocket buffer can grow. \
941                      (Obsolete)"),
942        )
943        .arg(
944            Arg::with_name("rpc_pubsub_max_out_buffer_capacity")
945                .long("rpc-pubsub-max-out-buffer-capacity")
946                .value_name("BYTES")
947                .takes_value(true)
948                .validator(is_parsable::<usize>)
949                .hidden(hidden_unless_forced())
950                .help("The maximum size in bytes to which the outgoing websocket buffer can grow. \
951                       (Obsolete)"),
952        )
953        .arg(
954            Arg::with_name("rpc_pubsub_max_active_subscriptions")
955                .long("rpc-pubsub-max-active-subscriptions")
956                .takes_value(true)
957                .value_name("NUMBER")
958                .validator(is_parsable::<usize>)
959                .default_value(&default_args.rpc_pubsub_max_active_subscriptions)
960                .help("The maximum number of active subscriptions that RPC PubSub will accept \
961                       across all connections."),
962        )
963        .arg(
964            Arg::with_name("rpc_pubsub_queue_capacity_items")
965                .long("rpc-pubsub-queue-capacity-items")
966                .takes_value(true)
967                .value_name("NUMBER")
968                .validator(is_parsable::<usize>)
969                .default_value(&default_args.rpc_pubsub_queue_capacity_items)
970                .help("The maximum number of notifications that RPC PubSub will store \
971                       across all connections."),
972        )
973        .arg(
974            Arg::with_name("rpc_pubsub_queue_capacity_bytes")
975                .long("rpc-pubsub-queue-capacity-bytes")
976                .takes_value(true)
977                .value_name("BYTES")
978                .validator(is_parsable::<usize>)
979                .default_value(&default_args.rpc_pubsub_queue_capacity_bytes)
980                .help("The maximum total size of notifications that RPC PubSub will store \
981                       across all connections."),
982        )
983        .arg(
984            Arg::with_name("rpc_pubsub_notification_threads")
985                .long("rpc-pubsub-notification-threads")
986                .requires("full_rpc_api")
987                .takes_value(true)
988                .value_name("NUM_THREADS")
989                .validator(is_parsable::<usize>)
990                .help("The maximum number of threads that RPC PubSub will use \
991                       for generating notifications. 0 will disable RPC PubSub notifications"),
992        )
993        .arg(
994            Arg::with_name("rpc_send_transaction_retry_ms")
995                .long("rpc-send-retry-ms")
996                .value_name("MILLISECS")
997                .takes_value(true)
998                .validator(is_parsable::<u64>)
999                .default_value(&default_args.rpc_send_transaction_retry_ms)
1000                .help("The rate at which transactions sent via rpc service are retried."),
1001        )
1002        .arg(
1003            Arg::with_name("rpc_send_transaction_batch_ms")
1004                .long("rpc-send-batch-ms")
1005                .value_name("MILLISECS")
1006                .hidden(hidden_unless_forced())
1007                .takes_value(true)
1008                .validator(|s| is_within_range(s, 1..=MAX_BATCH_SEND_RATE_MS))
1009                .default_value(&default_args.rpc_send_transaction_batch_ms)
1010                .help("The rate at which transactions sent via rpc service are sent in batch."),
1011        )
1012        .arg(
1013            Arg::with_name("rpc_send_transaction_leader_forward_count")
1014                .long("rpc-send-leader-count")
1015                .value_name("NUMBER")
1016                .takes_value(true)
1017                .validator(is_parsable::<u64>)
1018                .default_value(&default_args.rpc_send_transaction_leader_forward_count)
1019                .help("The number of upcoming leaders to which to forward transactions sent via rpc service."),
1020        )
1021        .arg(
1022            Arg::with_name("rpc_send_transaction_default_max_retries")
1023                .long("rpc-send-default-max-retries")
1024                .value_name("NUMBER")
1025                .takes_value(true)
1026                .validator(is_parsable::<usize>)
1027                .help("The maximum number of transaction broadcast retries when unspecified by the request, otherwise retried until expiration."),
1028        )
1029        .arg(
1030            Arg::with_name("rpc_send_transaction_service_max_retries")
1031                .long("rpc-send-service-max-retries")
1032                .value_name("NUMBER")
1033                .takes_value(true)
1034                .validator(is_parsable::<usize>)
1035                .default_value(&default_args.rpc_send_transaction_service_max_retries)
1036                .help("The maximum number of transaction broadcast retries, regardless of requested value."),
1037        )
1038        .arg(
1039            Arg::with_name("rpc_send_transaction_batch_size")
1040                .long("rpc-send-batch-size")
1041                .value_name("NUMBER")
1042                .hidden(hidden_unless_forced())
1043                .takes_value(true)
1044                .validator(|s| is_within_range(s, 1..=MAX_TRANSACTION_BATCH_SIZE))
1045                .default_value(&default_args.rpc_send_transaction_batch_size)
1046                .help("The size of transactions to be sent in batch."),
1047        )
1048        .arg(
1049            Arg::with_name("rpc_send_transaction_tpu_peer")
1050                .long("rpc-send-transaction-tpu-peer")
1051                .takes_value(true)
1052                .number_of_values(1)
1053                .multiple(true)
1054                .value_name("HOST:PORT")
1055                .validator(solana_net_utils::is_host_port)
1056                .help("Peer(s) to broadcast transactions to instead of the current leader")
1057        )
1058        .arg(
1059            Arg::with_name("rpc_send_transaction_also_leader")
1060                .long("rpc-send-transaction-also-leader")
1061                .requires("rpc_send_transaction_tpu_peer")
1062                .help("With `--rpc-send-transaction-tpu-peer HOST:PORT`, also send to the current leader")
1063        )
1064        .arg(
1065            Arg::with_name("rpc_scan_and_fix_roots")
1066                .long("rpc-scan-and-fix-roots")
1067                .takes_value(false)
1068                .requires("enable_rpc_transaction_history")
1069                .help("Verifies blockstore roots on boot and fixes any gaps"),
1070        )
1071        .arg(
1072            Arg::with_name("rpc_max_request_body_size")
1073                .long("rpc-max-request-body-size")
1074                .value_name("BYTES")
1075                .takes_value(true)
1076                .validator(is_parsable::<usize>)
1077                .default_value(&default_args.rpc_max_request_body_size)
1078                .help("The maximum request body size accepted by rpc service"),
1079        )
1080        .arg(
1081            Arg::with_name("enable_accountsdb_repl")
1082                .long("enable-accountsdb-repl")
1083                .takes_value(false)
1084                .hidden(hidden_unless_forced())
1085                .help("Enable AccountsDb Replication"),
1086        )
1087        .arg(
1088            Arg::with_name("accountsdb_repl_bind_address")
1089                .long("accountsdb-repl-bind-address")
1090                .value_name("HOST")
1091                .takes_value(true)
1092                .validator(solana_net_utils::is_host)
1093                .hidden(hidden_unless_forced())
1094                .help("IP address to bind the AccountsDb Replication port [default: use --bind-address]"),
1095        )
1096        .arg(
1097            Arg::with_name("accountsdb_repl_port")
1098                .long("accountsdb-repl-port")
1099                .value_name("PORT")
1100                .takes_value(true)
1101                .validator(port_validator)
1102                .hidden(hidden_unless_forced())
1103                .help("Enable AccountsDb Replication Service on this port"),
1104        )
1105        .arg(
1106            Arg::with_name("accountsdb_repl_threads")
1107                .long("accountsdb-repl-threads")
1108                .value_name("NUMBER")
1109                .validator(is_parsable::<usize>)
1110                .takes_value(true)
1111                .default_value(&default_args.accountsdb_repl_threads)
1112                .hidden(hidden_unless_forced())
1113                .help("Number of threads to use for servicing AccountsDb Replication requests"),
1114        )
1115        .arg(
1116            Arg::with_name("geyser_plugin_config")
1117                .long("geyser-plugin-config")
1118                .alias("accountsdb-plugin-config")
1119                .value_name("FILE")
1120                .takes_value(true)
1121                .multiple(true)
1122                .help("Specify the configuration file for the Geyser plugin."),
1123        )
1124        .arg(
1125            Arg::with_name("snapshot_archive_format")
1126                .long("snapshot-archive-format")
1127                .alias("snapshot-compression") // Legacy name used by Solana v1.5.x and older
1128                .possible_values(SUPPORTED_ARCHIVE_COMPRESSION)
1129                .default_value(&default_args.snapshot_archive_format)
1130                .value_name("ARCHIVE_TYPE")
1131                .takes_value(true)
1132                .help("Snapshot archive format to use."),
1133        )
1134        .arg(
1135            Arg::with_name("max_genesis_archive_unpacked_size")
1136                .long("max-genesis-archive-unpacked-size")
1137                .value_name("NUMBER")
1138                .takes_value(true)
1139                .default_value(&default_args.genesis_archive_unpacked_size)
1140                .help(
1141                    "maximum total uncompressed file size of downloaded genesis archive",
1142                ),
1143        )
1144        .arg(
1145            Arg::with_name("wal_recovery_mode")
1146                .long("wal-recovery-mode")
1147                .value_name("MODE")
1148                .takes_value(true)
1149                .possible_values(&[
1150                    "tolerate_corrupted_tail_records",
1151                    "absolute_consistency",
1152                    "point_in_time",
1153                    "skip_any_corrupted_record"])
1154                .help(
1155                    "Mode to recovery the ledger db write ahead log."
1156                ),
1157        )
1158        .arg(
1159            Arg::with_name("poh_pinned_cpu_core")
1160                .hidden(hidden_unless_forced())
1161                .long("experimental-poh-pinned-cpu-core")
1162                .takes_value(true)
1163                .value_name("CPU_CORE_INDEX")
1164                .validator(|s| {
1165                    let core_index = usize::from_str(&s).map_err(|e| e.to_string())?;
1166                    let max_index = core_affinity::get_core_ids().map(|cids| cids.len() - 1).unwrap_or(0);
1167                    if core_index > max_index {
1168                        return Err(format!("core index must be in the range [0, {max_index}]"));
1169                    }
1170                    Ok(())
1171                })
1172                .help("EXPERIMENTAL: Specify which CPU core PoH is pinned to"),
1173        )
1174        .arg(
1175            Arg::with_name("poh_hashes_per_batch")
1176                .hidden(hidden_unless_forced())
1177                .long("poh-hashes-per-batch")
1178                .takes_value(true)
1179                .value_name("NUM")
1180                .help("Specify hashes per batch in PoH service"),
1181        )
1182        .arg(
1183            Arg::with_name("process_ledger_before_services")
1184                .long("process-ledger-before-services")
1185                .hidden(hidden_unless_forced())
1186                .help("Process the local ledger fully before starting networking services")
1187        )
1188        .arg(
1189            Arg::with_name("account_indexes")
1190                .long("account-index")
1191                .takes_value(true)
1192                .multiple(true)
1193                .possible_values(&["program-id", "spl-token-owner", "spl-token-mint"])
1194                .value_name("INDEX")
1195                .help("Enable an accounts index, indexed by the selected account field"),
1196        )
1197        .arg(
1198            Arg::with_name("account_index_exclude_key")
1199                .long(EXCLUDE_KEY)
1200                .takes_value(true)
1201                .validator(is_pubkey)
1202                .multiple(true)
1203                .value_name("KEY")
1204                .help("When account indexes are enabled, exclude this key from the index."),
1205        )
1206        .arg(
1207            Arg::with_name("account_index_include_key")
1208                .long(INCLUDE_KEY)
1209                .takes_value(true)
1210                .validator(is_pubkey)
1211                .conflicts_with("account_index_exclude_key")
1212                .multiple(true)
1213                .value_name("KEY")
1214                .help("When account indexes are enabled, only include specific keys in the index. This overrides --account-index-exclude-key."),
1215        )
1216        .arg(
1217            Arg::with_name("accounts_db_verify_refcounts")
1218                .long("accounts-db-verify-refcounts")
1219                .help("Debug option to scan all append vecs and verify account index refcounts prior to clean")
1220                .hidden(hidden_unless_forced())
1221        )
1222        .arg(
1223            Arg::with_name("accounts_db_test_skip_rewrites")
1224                .long("accounts-db-test-skip-rewrites")
1225                .help("Debug option to skip rewrites for rent-exempt accounts but still add them in bank delta hash calculation")
1226                .hidden(hidden_unless_forced())
1227        )
1228        .arg(
1229            Arg::with_name("no_skip_initial_accounts_db_clean")
1230                .long("no-skip-initial-accounts-db-clean")
1231                .help("Do not skip the initial cleaning of accounts when verifying snapshot bank")
1232                .hidden(hidden_unless_forced())
1233                .conflicts_with("accounts_db_skip_shrink")
1234        )
1235        .arg(
1236            Arg::with_name("accounts_db_create_ancient_storage_packed")
1237                .long("accounts-db-create-ancient-storage-packed")
1238                .help("Create ancient storages in one shot instead of appending.")
1239                .hidden(hidden_unless_forced()),
1240            )
1241        .arg(
1242            Arg::with_name("accounts_db_ancient_append_vecs")
1243                .long("accounts-db-ancient-append-vecs")
1244                .value_name("SLOT-OFFSET")
1245                .validator(is_parsable::<i64>)
1246                .takes_value(true)
1247                .help("AppendVecs that are older than (slots_per_epoch - SLOT-OFFSET) are squashed together.")
1248                .hidden(hidden_unless_forced()),
1249        )
1250        .arg(
1251            Arg::with_name("accounts_db_cache_limit_mb")
1252                .long("accounts-db-cache-limit-mb")
1253                .value_name("MEGABYTES")
1254                .validator(is_parsable::<u64>)
1255                .takes_value(true)
1256                .help("How large the write cache for account data can become. If this is exceeded, the cache is flushed more aggressively."),
1257        )
1258        .arg(
1259            Arg::with_name("accounts_index_scan_results_limit_mb")
1260                .long("accounts-index-scan-results-limit-mb")
1261                .value_name("MEGABYTES")
1262                .validator(is_parsable::<usize>)
1263                .takes_value(true)
1264                .help("How large accumulated results from an accounts index scan can become. If this is exceeded, the scan aborts."),
1265        )
1266        .arg(
1267            Arg::with_name("accounts_index_memory_limit_mb")
1268                .long("accounts-index-memory-limit-mb")
1269                .value_name("MEGABYTES")
1270                .validator(is_parsable::<usize>)
1271                .takes_value(true)
1272                .help("How much memory the accounts index can consume. If this is exceeded, some account index entries will be stored on disk."),
1273        )
1274        .arg(
1275            Arg::with_name("accounts_index_bins")
1276                .long("accounts-index-bins")
1277                .value_name("BINS")
1278                .validator(is_pow2)
1279                .takes_value(true)
1280                .help("Number of bins to divide the accounts index into"),
1281        )
1282        .arg(
1283            Arg::with_name("partitioned_epoch_rewards_compare_calculation")
1284                .long("partitioned-epoch-rewards-compare-calculation")
1285                .takes_value(false)
1286                .help("Do normal epoch rewards distribution, but also calculate rewards using the partitioned rewards code path and compare the resulting vote and stake accounts")
1287                .hidden(hidden_unless_forced())
1288        )
1289        .arg(
1290            Arg::with_name("partitioned_epoch_rewards_force_enable_single_slot")
1291                .long("partitioned-epoch-rewards-force-enable-single-slot")
1292                .takes_value(false)
1293                .help("Force the partitioned rewards distribution, but distribute all rewards in the first slot in the epoch. This should match consensus with the normal rewards distribution.")
1294                .conflicts_with("partitioned_epoch_rewards_compare_calculation")
1295                .hidden(hidden_unless_forced())
1296        )
1297        .arg(
1298            Arg::with_name("accounts_index_path")
1299                .long("accounts-index-path")
1300                .value_name("PATH")
1301                .takes_value(true)
1302                .multiple(true)
1303                .help("Persistent accounts-index location. \
1304                       May be specified multiple times. \
1305                       [default: [ledger]/accounts_index]"),
1306        )
1307        .arg(
1308            Arg::with_name("accounts_db_test_hash_calculation")
1309                .long("accounts-db-test-hash-calculation")
1310                .help("Enables testing of hash calculation using stores in \
1311                      AccountsHashVerifier. This has a computational cost."),
1312        )
1313        .arg(
1314            Arg::with_name("accounts_shrink_optimize_total_space")
1315                .long("accounts-shrink-optimize-total-space")
1316                .takes_value(true)
1317                .value_name("BOOLEAN")
1318                .default_value(&default_args.accounts_shrink_optimize_total_space)
1319                .help("When this is set to true, the system will shrink the most \
1320                       sparse accounts and when the overall shrink ratio is above \
1321                       the specified accounts-shrink-ratio, the shrink will stop and \
1322                       it will skip all other less sparse accounts."),
1323        )
1324        .arg(
1325            Arg::with_name("accounts_shrink_ratio")
1326                .long("accounts-shrink-ratio")
1327                .takes_value(true)
1328                .value_name("RATIO")
1329                .default_value(&default_args.accounts_shrink_ratio)
1330                .help("Specifies the shrink ratio for the accounts to be shrunk. \
1331                       The shrink ratio is defined as the ratio of the bytes alive over the  \
1332                       total bytes used. If the account's shrink ratio is less than this ratio \
1333                       it becomes a candidate for shrinking. The value must between 0. and 1.0 \
1334                       inclusive."),
1335        )
1336        .arg(
1337            Arg::with_name("allow_private_addr")
1338                .long("allow-private-addr")
1339                .takes_value(false)
1340                .help("Allow contacting private ip addresses")
1341                .hidden(hidden_unless_forced()),
1342        )
1343        .arg(
1344            Arg::with_name("log_messages_bytes_limit")
1345                .long("log-messages-bytes-limit")
1346                .takes_value(true)
1347                .validator(is_parsable::<usize>)
1348                .value_name("BYTES")
1349                .help("Maximum number of bytes written to the program log before truncation")
1350        )
1351        .arg(
1352            Arg::with_name("replay_slots_concurrently")
1353                .long("replay-slots-concurrently")
1354                .help("Allow concurrent replay of slots on different forks")
1355        )
1356        .arg(
1357            Arg::with_name("banking_trace_dir_byte_limit")
1358                // expose friendly alternative name to cli than internal
1359                // implementation-oriented one
1360                .long("enable-banking-trace")
1361                .value_name("BYTES")
1362                .validator(is_parsable::<DirByteLimit>)
1363                .takes_value(true)
1364                // Firstly, zero limit value causes tracer to be disabled
1365                // altogether, intuitively. On the other hand, this non-zero
1366                // default doesn't enable banking tracer unless this flag is
1367                // explicitly given, similar to --limit-ledger-size.
1368                // see configure_banking_trace_dir_byte_limit() for this.
1369                .default_value(&default_args.banking_trace_dir_byte_limit)
1370                .help("Enables the banking trace explicitly, which is enabled by default and \
1371                       writes trace files for simulate-leader-blocks, retaining up to the default \
1372                       or specified total bytes in the ledger. This flag can be used to override \
1373                       its byte limit.")
1374        )
1375        .arg(
1376            Arg::with_name("disable_banking_trace")
1377                .long("disable-banking-trace")
1378                .conflicts_with("banking_trace_dir_byte_limit")
1379                .takes_value(false)
1380                .help("Disables the banking trace")
1381        )
1382        .arg(
1383            Arg::with_name("block_verification_method")
1384                .long("block-verification-method")
1385                .hidden(hidden_unless_forced())
1386                .value_name("METHOD")
1387                .takes_value(true)
1388                .possible_values(BlockVerificationMethod::cli_names())
1389                .help(BlockVerificationMethod::cli_message())
1390        )
1391        .arg(
1392            Arg::with_name("block_production_method")
1393                .long("block-production-method")
1394                .value_name("METHOD")
1395                .takes_value(true)
1396                .possible_values(BlockProductionMethod::cli_names())
1397                .help(BlockProductionMethod::cli_message())
1398        )
1399        .arg(
1400            Arg::with_name("wen_restart")
1401                .long("wen-restart")
1402                .hidden(hidden_unless_forced())
1403                .value_name("DIR")
1404                .takes_value(true)
1405                .required(false)
1406                .conflicts_with("wait_for_supermajority")
1407                .help(
1408                    "When specified, the validator will enter Wen Restart mode which
1409                    pauses normal activity. Validators in this mode will gossip their last
1410                    vote to reach consensus on a safe restart slot and repair all blocks
1411                    on the selected fork. The safe slot will be a descendant of the latest
1412                    optimistically confirmed slot to ensure we do not roll back any
1413                    optimistically confirmed slots.
1414
1415                    The progress in this mode will be saved in the file location provided.
1416                    If consensus is reached, the validator will automatically exit and then
1417                    execute wait_for_supermajority logic so the cluster will resume execution.
1418                    The progress file will be kept around for future debugging.
1419
1420                    After the cluster resumes normal operation, the validator arguments can
1421                    be adjusted to remove --wen_restart and update expected_shred_version to
1422                    the new shred_version agreed on in the consensus.
1423
1424                    If wen_restart fails, refer to the progress file (in proto3 format) for
1425                    further debugging.
1426                ")
1427        )
1428        .args(&get_deprecated_arguments())
1429        .after_help("The default subcommand is run")
1430        .subcommand(
1431            SubCommand::with_name("exit")
1432                .about("Send an exit request to the validator")
1433                .arg(
1434                    Arg::with_name("force")
1435                        .short("f")
1436                        .long("force")
1437                        .takes_value(false)
1438                        .help("Request the validator exit immediately instead of waiting for a restart window")
1439                )
1440                .arg(
1441                    Arg::with_name("monitor")
1442                        .short("m")
1443                        .long("monitor")
1444                        .takes_value(false)
1445                        .help("Monitor the validator after sending the exit request")
1446                )
1447                .arg(
1448                    Arg::with_name("min_idle_time")
1449                        .long("min-idle-time")
1450                        .takes_value(true)
1451                        .validator(is_parsable::<usize>)
1452                        .value_name("MINUTES")
1453                        .default_value(&default_args.exit_min_idle_time)
1454                        .help("Minimum time that the validator should not be leader before restarting")
1455                )
1456                .arg(
1457                    Arg::with_name("max_delinquent_stake")
1458                        .long("max-delinquent-stake")
1459                        .takes_value(true)
1460                        .validator(is_valid_percentage)
1461                        .default_value(&default_args.exit_max_delinquent_stake)
1462                        .value_name("PERCENT")
1463                        .help("The maximum delinquent stake % permitted for an exit")
1464                )
1465                .arg(
1466                    Arg::with_name("skip_new_snapshot_check")
1467                        .long("skip-new-snapshot-check")
1468                        .help("Skip check for a new snapshot")
1469                )
1470                .arg(
1471                    Arg::with_name("skip_health_check")
1472                        .long("skip-health-check")
1473                        .help("Skip health check")
1474                )
1475        )
1476        .subcommand(
1477            SubCommand::with_name("authorized-voter")
1478                .about("Adjust the validator authorized voters")
1479                .setting(AppSettings::SubcommandRequiredElseHelp)
1480                .setting(AppSettings::InferSubcommands)
1481                .subcommand(
1482                    SubCommand::with_name("add")
1483                        .about("Add an authorized voter")
1484                        .arg(
1485                            Arg::with_name("authorized_voter_keypair")
1486                                .index(1)
1487                                .value_name("KEYPAIR")
1488                                .required(false)
1489                                .takes_value(true)
1490                                .validator(is_keypair)
1491                                .help("Path to keypair of the authorized voter to add \
1492                               [default: read JSON keypair from stdin]"),
1493                        )
1494                        .after_help("Note: the new authorized voter only applies to the \
1495                             currently running validator instance")
1496                )
1497                .subcommand(
1498                    SubCommand::with_name("remove-all")
1499                        .about("Remove all authorized voters")
1500                        .after_help("Note: the removal only applies to the \
1501                             currently running validator instance")
1502                )
1503        )
1504        .subcommand(
1505            SubCommand::with_name("contact-info")
1506                .about("Display the validator's contact info")
1507                .arg(
1508                    Arg::with_name("output")
1509                        .long("output")
1510                        .takes_value(true)
1511                        .value_name("MODE")
1512                        .possible_values(&["json", "json-compact"])
1513                        .help("Output display mode")
1514                )
1515        )
1516        .subcommand(SubCommand::with_name("repair-shred-from-peer")
1517                .about("Request a repair from the specified validator")
1518                .arg(
1519                    Arg::with_name("pubkey")
1520                        .long("pubkey")
1521                        .value_name("PUBKEY")
1522                        .required(false)
1523                        .takes_value(true)
1524                        .validator(is_pubkey)
1525                        .help("Identity pubkey of the validator to repair from")
1526                )
1527                .arg(
1528                    Arg::with_name("slot")
1529                        .long("slot")
1530                        .value_name("SLOT")
1531                        .takes_value(true)
1532                        .validator(is_parsable::<u64>)
1533                        .help("Slot to repair")
1534                )
1535                .arg(
1536                    Arg::with_name("shred")
1537                        .long("shred")
1538                        .value_name("SHRED")
1539                        .takes_value(true)
1540                        .validator(is_parsable::<u64>)
1541                        .help("Shred to repair")
1542                )
1543        )
1544        .subcommand(
1545            SubCommand::with_name("repair-whitelist")
1546                .about("Manage the validator's repair protocol whitelist")
1547                .setting(AppSettings::SubcommandRequiredElseHelp)
1548                .setting(AppSettings::InferSubcommands)
1549                .subcommand(
1550                    SubCommand::with_name("get")
1551                        .about("Display the validator's repair protocol whitelist")
1552                        .arg(
1553                            Arg::with_name("output")
1554                                .long("output")
1555                                .takes_value(true)
1556                                .value_name("MODE")
1557                                .possible_values(&["json", "json-compact"])
1558                                .help("Output display mode")
1559                        )
1560                )
1561                .subcommand(
1562                    SubCommand::with_name("set")
1563                        .about("Set the validator's repair protocol whitelist")
1564                        .setting(AppSettings::ArgRequiredElseHelp)
1565                        .arg(
1566                            Arg::with_name("whitelist")
1567                            .long("whitelist")
1568                            .validator(is_pubkey)
1569                            .value_name("VALIDATOR IDENTITY")
1570                            .multiple(true)
1571                            .takes_value(true)
1572                            .help("Set the validator's repair protocol whitelist")
1573                        )
1574                        .after_help("Note: repair protocol whitelist changes only apply to the currently \
1575                                    running validator instance")
1576                )
1577                .subcommand(
1578                    SubCommand::with_name("remove-all")
1579                        .about("Clear the validator's repair protocol whitelist")
1580                        .after_help("Note: repair protocol whitelist changes only apply to the currently \
1581                                    running validator instance")
1582                )
1583        )
1584        .subcommand(
1585            SubCommand::with_name("init")
1586                .about("Initialize the ledger directory then exit")
1587        )
1588        .subcommand(
1589            SubCommand::with_name("monitor")
1590                .about("Monitor the validator")
1591        )
1592        .subcommand(
1593            SubCommand::with_name("run")
1594                .about("Run the validator")
1595        )
1596        .subcommand(
1597            SubCommand::with_name("plugin")
1598                .about("Manage and view geyser plugins")
1599                .setting(AppSettings::SubcommandRequiredElseHelp)
1600                .setting(AppSettings::InferSubcommands)
1601                .subcommand(
1602                    SubCommand::with_name("list")
1603                        .about("List all current running gesyer plugins")
1604                )
1605                .subcommand(
1606                    SubCommand::with_name("unload")
1607                        .about("Unload a particular gesyer plugin. You must specify the gesyer plugin name")
1608                        .arg(
1609                            Arg::with_name("name")
1610                                .required(true)
1611                                .takes_value(true)
1612                        )
1613                )
1614                .subcommand(
1615                    SubCommand::with_name("reload")
1616                        .about("Reload a particular gesyer plugin. You must specify the gesyer plugin name and the new config path")
1617                        .arg(
1618                            Arg::with_name("name")
1619                                .required(true)
1620                                .takes_value(true)
1621                        )
1622                        .arg(
1623                            Arg::with_name("config")
1624                                .required(true)
1625                                .takes_value(true)
1626                        )
1627                )
1628                .subcommand(
1629                    SubCommand::with_name("load")
1630                        .about("Load a new gesyer plugin. You must specify the config path. Fails if overwriting (use reload)")
1631                        .arg(
1632                            Arg::with_name("config")
1633                                .required(true)
1634                                .takes_value(true)
1635                        )
1636                )
1637        )
1638        .subcommand(
1639            SubCommand::with_name("set-identity")
1640                .about("Set the validator identity")
1641                .arg(
1642                    Arg::with_name("identity")
1643                        .index(1)
1644                        .value_name("KEYPAIR")
1645                        .required(false)
1646                        .takes_value(true)
1647                        .validator(is_keypair)
1648                        .help("Path to validator identity keypair \
1649                           [default: read JSON keypair from stdin]")
1650                )
1651                .arg(
1652                    clap::Arg::with_name("require_tower")
1653                        .long("require-tower")
1654                        .takes_value(false)
1655                        .help("Refuse to set the validator identity if saved tower state is not found"),
1656                )
1657                .after_help("Note: the new identity only applies to the \
1658                         currently running validator instance")
1659        )
1660        .subcommand(
1661            SubCommand::with_name("set-log-filter")
1662                .about("Adjust the validator log filter")
1663                .arg(
1664                    Arg::with_name("filter")
1665                        .takes_value(true)
1666                        .index(1)
1667                        .help("New filter using the same format as the RUST_LOG environment variable")
1668                )
1669                .after_help("Note: the new filter only applies to the currently running validator instance")
1670        )
1671        .subcommand(
1672            SubCommand::with_name("staked-nodes-overrides")
1673                .about("Overrides stakes of specific node identities.")
1674                .arg(
1675                    Arg::with_name("path")
1676                        .value_name("PATH")
1677                        .takes_value(true)
1678                        .required(true)
1679                        .help("Provide path to a file with custom overrides for stakes of specific validator identities."),
1680                )
1681                .after_help("Note: the new staked nodes overrides only applies to the \
1682                         currently running validator instance")
1683        )
1684        .subcommand(
1685            SubCommand::with_name("wait-for-restart-window")
1686                .about("Monitor the validator for a good time to restart")
1687                .arg(
1688                    Arg::with_name("min_idle_time")
1689                        .long("min-idle-time")
1690                        .takes_value(true)
1691                        .validator(is_parsable::<usize>)
1692                        .value_name("MINUTES")
1693                        .default_value(&default_args.wait_for_restart_window_min_idle_time)
1694                        .help("Minimum time that the validator should not be leader before restarting")
1695                )
1696                .arg(
1697                    Arg::with_name("identity")
1698                        .long("identity")
1699                        .value_name("ADDRESS")
1700                        .takes_value(true)
1701                        .validator(is_pubkey_or_keypair)
1702                        .help("Validator identity to monitor [default: your validator]")
1703                )
1704                .arg(
1705                    Arg::with_name("max_delinquent_stake")
1706                        .long("max-delinquent-stake")
1707                        .takes_value(true)
1708                        .validator(is_valid_percentage)
1709                        .default_value(&default_args.wait_for_restart_window_max_delinquent_stake)
1710                        .value_name("PERCENT")
1711                        .help("The maximum delinquent stake % permitted for a restart")
1712                )
1713                .arg(
1714                    Arg::with_name("skip_new_snapshot_check")
1715                        .long("skip-new-snapshot-check")
1716                        .help("Skip check for a new snapshot")
1717                )
1718                .arg(
1719                    Arg::with_name("skip_health_check")
1720                        .long("skip-health-check")
1721                        .help("Skip health check")
1722                )
1723                .after_help("Note: If this command exits with a non-zero status \
1724                         then this not a good time for a restart")
1725        ).
1726        subcommand(
1727            SubCommand::with_name("set-public-address")
1728                .about("Specify addresses to advertise in gossip")
1729                .arg(
1730                    Arg::with_name("tpu_addr")
1731                        .long("tpu")
1732                        .value_name("HOST:PORT")
1733                        .takes_value(true)
1734                        .validator(solana_net_utils::is_host_port)
1735                        .help("TPU address to advertise in gossip")
1736                )
1737                .arg(
1738                    Arg::with_name("tpu_forwards_addr")
1739                        .long("tpu-forwards")
1740                        .value_name("HOST:PORT")
1741                        .takes_value(true)
1742                        .validator(solana_net_utils::is_host_port)
1743                        .help("TPU Forwards address to advertise in gossip")
1744                )
1745                .group(
1746                    ArgGroup::with_name("set_public_address_details")
1747                        .args(&["tpu_addr", "tpu_forwards_addr"])
1748                        .required(true)
1749                        .multiple(true)
1750                )
1751                .after_help("Note: At least one arg must be used. Using multiple is ok"),
1752        );
1753}
1754
1755/// Deprecated argument description should be moved into the [`deprecated_arguments()`] function,
1756/// expressed as an instance of this type.
1757struct DeprecatedArg {
1758    /// Deprecated argument description, moved here as is.
1759    ///
1760    /// `hidden` property will be modified by [`deprecated_arguments()`] to only show this argument
1761    /// if [`hidden_unless_forced()`] says they should be displayed.
1762    arg: Arg<'static, 'static>,
1763
1764    /// If simply replaced by a different argument, this is the name of the replacement.
1765    ///
1766    /// Content should be an argument name, as presented to users.
1767    replaced_by: Option<&'static str>,
1768
1769    /// An explanation to be shown to the user if they still use this argument.
1770    ///
1771    /// Content should be a complete sentence or several, ending with a period.
1772    usage_warning: Option<&'static str>,
1773}
1774
1775fn deprecated_arguments() -> Vec<DeprecatedArg> {
1776    let mut res = vec![];
1777
1778    // This macro reduces indentation and removes some noise from the argument declaration list.
1779    macro_rules! add_arg {
1780        (
1781            $arg:expr
1782            $( , replaced_by: $replaced_by:expr )?
1783            $( , usage_warning: $usage_warning:expr )?
1784            $(,)?
1785        ) => {
1786            let replaced_by = add_arg!(@into-option $( $replaced_by )?);
1787            let usage_warning = add_arg!(@into-option $( $usage_warning )?);
1788            res.push(DeprecatedArg {
1789                arg: $arg,
1790                replaced_by,
1791                usage_warning,
1792            });
1793        };
1794
1795        (@into-option) => { None };
1796        (@into-option $v:expr) => { Some($v) };
1797    }
1798
1799    add_arg!(Arg::with_name("accounts_db_caching_enabled").long("accounts-db-caching-enabled"));
1800    add_arg!(
1801        Arg::with_name("accounts_db_index_hashing")
1802            .long("accounts-db-index-hashing")
1803            .help(
1804                "Enables the use of the index in hash calculation in \
1805                 AccountsHashVerifier/Accounts Background Service.",
1806            ),
1807        usage_warning: "The accounts hash is only calculated without using the index.",
1808    );
1809    add_arg!(
1810        Arg::with_name("accounts_db_skip_shrink")
1811            .long("accounts-db-skip-shrink")
1812            .help("Enables faster starting of validators by skipping startup clean and shrink."),
1813        usage_warning: "Enabled by default",
1814    );
1815    add_arg!(Arg::with_name("accounts_hash_interval_slots")
1816        .long("accounts-hash-interval-slots")
1817        .value_name("NUMBER")
1818        .takes_value(true)
1819        .help("Number of slots between verifying accounts hash.")
1820        .validator(|val| {
1821            if val.eq("0") {
1822                Err(String::from("Accounts hash interval cannot be zero"))
1823            } else {
1824                Ok(())
1825            }
1826        }));
1827    add_arg!(Arg::with_name("disable_accounts_disk_index")
1828        .long("disable-accounts-disk-index")
1829        .help("Disable the disk-based accounts index if it is enabled by default.")
1830        .conflicts_with("accounts_index_memory_limit_mb"));
1831    add_arg!(
1832        Arg::with_name("disable_quic_servers")
1833            .long("disable-quic-servers")
1834            .takes_value(false),
1835        usage_warning: "The quic server cannot be disabled.",
1836    );
1837    add_arg!(
1838        Arg::with_name("enable_cpi_and_log_storage")
1839            .long("enable-cpi-and-log-storage")
1840            .requires("enable_rpc_transaction_history")
1841            .takes_value(false)
1842            .help(
1843                "Include CPI inner instructions, logs and return data in the historical \
1844                 transaction info stored",
1845            ),
1846        replaced_by: "enable-extended-tx-metadata-storage",
1847    );
1848    add_arg!(
1849        Arg::with_name("enable_quic_servers")
1850            .long("enable-quic-servers"),
1851        usage_warning: "The quic server is now enabled by default.",
1852    );
1853    add_arg!(
1854        Arg::with_name("halt_on_known_validators_accounts_hash_mismatch")
1855            .alias("halt-on-trusted-validators-accounts-hash-mismatch")
1856            .long("halt-on-known-validators-accounts-hash-mismatch")
1857            .requires("known_validators")
1858            .takes_value(false)
1859            .help("Abort the validator if a bank hash mismatch is detected within known validator set"),
1860    );
1861    add_arg!(Arg::with_name("incremental_snapshots")
1862        .long("incremental-snapshots")
1863        .takes_value(false)
1864        .conflicts_with("no_incremental_snapshots")
1865        .help("Enable incremental snapshots")
1866        .long_help(
1867            "Enable incremental snapshots by setting this flag.  When enabled, \
1868                 --snapshot-interval-slots will set the incremental snapshot interval. To set the
1869                 full snapshot interval, use --full-snapshot-interval-slots.",
1870        ));
1871    add_arg!(Arg::with_name("minimal_rpc_api")
1872        .long("minimal-rpc-api")
1873        .takes_value(false)
1874        .help("Only expose the RPC methods required to serve snapshots to other nodes"));
1875    add_arg!(
1876        Arg::with_name("no_accounts_db_index_hashing")
1877            .long("no-accounts-db-index-hashing")
1878            .help(
1879                "This is obsolete. See --accounts-db-index-hashing. \
1880                   Disables the use of the index in hash calculation in \
1881                   AccountsHashVerifier/Accounts Background Service.",
1882            ),
1883        usage_warning: "The accounts hash is only calculated without using the index.",
1884    );
1885    add_arg!(
1886        Arg::with_name("no_check_vote_account")
1887            .long("no-check-vote-account")
1888            .takes_value(false)
1889            .conflicts_with("no_voting")
1890            .requires("entrypoint")
1891            .help("Skip the RPC vote account sanity check"),
1892        usage_warning: "Vote account sanity checks are no longer performed by default.",
1893    );
1894    add_arg!(Arg::with_name("no_rocksdb_compaction")
1895        .long("no-rocksdb-compaction")
1896        .takes_value(false)
1897        .help("Disable manual compaction of the ledger database"));
1898    add_arg!(Arg::with_name("rocksdb_compaction_interval")
1899        .long("rocksdb-compaction-interval-slots")
1900        .value_name("ROCKSDB_COMPACTION_INTERVAL_SLOTS")
1901        .takes_value(true)
1902        .help("Number of slots between compacting ledger"));
1903    add_arg!(Arg::with_name("rocksdb_max_compaction_jitter")
1904        .long("rocksdb-max-compaction-jitter-slots")
1905        .value_name("ROCKSDB_MAX_COMPACTION_JITTER_SLOTS")
1906        .takes_value(true)
1907        .help("Introduce jitter into the compaction to offset compaction operation"));
1908    add_arg!(
1909        Arg::with_name("skip_poh_verify")
1910            .long("skip-poh-verify")
1911            .takes_value(false)
1912            .help("Skip ledger verification at validator bootup."),
1913        replaced_by: "skip-startup-ledger-verification",
1914    );
1915
1916    res
1917}
1918
1919// Helper to add arguments that are no longer used but are being kept around to avoid breaking
1920// validator startup commands.
1921fn get_deprecated_arguments() -> Vec<Arg<'static, 'static>> {
1922    deprecated_arguments()
1923        .into_iter()
1924        .map(|info| {
1925            let arg = info.arg;
1926            // Hide all deprecated arguments by default.
1927            arg.hidden(hidden_unless_forced())
1928        })
1929        .collect()
1930}
1931
1932pub fn warn_for_deprecated_arguments(matches: &ArgMatches) {
1933    for DeprecatedArg {
1934        arg,
1935        replaced_by,
1936        usage_warning,
1937    } in deprecated_arguments().into_iter()
1938    {
1939        if matches.is_present(arg.b.name) {
1940            let mut msg = format!("--{} is deprecated", arg.b.name.replace('_', "-"));
1941            if let Some(replaced_by) = replaced_by {
1942                msg.push_str(&format!(", please use --{replaced_by}"));
1943            }
1944            msg.push('.');
1945            if let Some(usage_warning) = usage_warning {
1946                msg.push_str(&format!("  {usage_warning}"));
1947                if !msg.ends_with('.') {
1948                    msg.push('.');
1949                }
1950            }
1951            warn!("{}", msg);
1952        }
1953    }
1954}
1955
1956pub struct DefaultArgs {
1957    pub bind_address: String,
1958    pub dynamic_port_range: String,
1959    pub ledger_path: String,
1960
1961    pub genesis_archive_unpacked_size: String,
1962    pub health_check_slot_distance: String,
1963    pub tower_storage: String,
1964    pub etcd_domain_name: String,
1965    pub send_transaction_service_config: send_transaction_service::Config,
1966
1967    pub rpc_max_multiple_accounts: String,
1968    pub rpc_pubsub_max_active_subscriptions: String,
1969    pub rpc_pubsub_queue_capacity_items: String,
1970    pub rpc_pubsub_queue_capacity_bytes: String,
1971    pub rpc_send_transaction_retry_ms: String,
1972    pub rpc_send_transaction_batch_ms: String,
1973    pub rpc_send_transaction_leader_forward_count: String,
1974    pub rpc_send_transaction_service_max_retries: String,
1975    pub rpc_send_transaction_batch_size: String,
1976    pub rpc_threads: String,
1977    pub rpc_niceness_adjustment: String,
1978    pub rpc_bigtable_timeout: String,
1979    pub rpc_bigtable_instance_name: String,
1980    pub rpc_bigtable_app_profile_id: String,
1981    pub rpc_bigtable_max_message_size: String,
1982    pub rpc_max_request_body_size: String,
1983    pub rpc_pubsub_worker_threads: String,
1984
1985    pub maximum_local_snapshot_age: String,
1986    pub maximum_full_snapshot_archives_to_retain: String,
1987    pub maximum_incremental_snapshot_archives_to_retain: String,
1988    pub snapshot_packager_niceness_adjustment: String,
1989    pub full_snapshot_archive_interval_slots: String,
1990    pub incremental_snapshot_archive_interval_slots: String,
1991    pub min_snapshot_download_speed: String,
1992    pub max_snapshot_download_abort: String,
1993
1994    pub contact_debug_interval: String,
1995
1996    pub accountsdb_repl_threads: String,
1997
1998    pub snapshot_version: SnapshotVersion,
1999    pub snapshot_archive_format: String,
2000
2001    pub rocksdb_shred_compaction: String,
2002    pub rocksdb_ledger_compression: String,
2003    pub rocksdb_perf_sample_interval: String,
2004
2005    pub accounts_shrink_optimize_total_space: String,
2006    pub accounts_shrink_ratio: String,
2007    pub tpu_connection_pool_size: String,
2008
2009    // Exit subcommand
2010    pub exit_min_idle_time: String,
2011    pub exit_max_delinquent_stake: String,
2012
2013    // Wait subcommand
2014    pub wait_for_restart_window_min_idle_time: String,
2015    pub wait_for_restart_window_max_delinquent_stake: String,
2016
2017    pub banking_trace_dir_byte_limit: String,
2018
2019    pub wen_restart_path: String,
2020}
2021
2022impl DefaultArgs {
2023    pub fn new() -> Self {
2024        let default_send_transaction_service_config = send_transaction_service::Config::default();
2025
2026        DefaultArgs {
2027            bind_address: "0.0.0.0".to_string(),
2028            ledger_path: "ledger".to_string(),
2029            dynamic_port_range: format!("{}-{}", VALIDATOR_PORT_RANGE.0, VALIDATOR_PORT_RANGE.1),
2030            maximum_local_snapshot_age: "2500".to_string(),
2031            genesis_archive_unpacked_size: MAX_GENESIS_ARCHIVE_UNPACKED_SIZE.to_string(),
2032            rpc_max_multiple_accounts: MAX_MULTIPLE_ACCOUNTS.to_string(),
2033            health_check_slot_distance: "150".to_string(),
2034            tower_storage: "file".to_string(),
2035            etcd_domain_name: "localhost".to_string(),
2036            rpc_pubsub_max_active_subscriptions: PubSubConfig::default()
2037                .max_active_subscriptions
2038                .to_string(),
2039            rpc_pubsub_queue_capacity_items: PubSubConfig::default()
2040                .queue_capacity_items
2041                .to_string(),
2042            rpc_pubsub_queue_capacity_bytes: PubSubConfig::default()
2043                .queue_capacity_bytes
2044                .to_string(),
2045            send_transaction_service_config: send_transaction_service::Config::default(),
2046            rpc_send_transaction_retry_ms: default_send_transaction_service_config
2047                .retry_rate_ms
2048                .to_string(),
2049            rpc_send_transaction_batch_ms: default_send_transaction_service_config
2050                .batch_send_rate_ms
2051                .to_string(),
2052            rpc_send_transaction_leader_forward_count: default_send_transaction_service_config
2053                .leader_forward_count
2054                .to_string(),
2055            rpc_send_transaction_service_max_retries: default_send_transaction_service_config
2056                .service_max_retries
2057                .to_string(),
2058            rpc_send_transaction_batch_size: default_send_transaction_service_config
2059                .batch_size
2060                .to_string(),
2061            rpc_threads: num_cpus::get().to_string(),
2062            rpc_niceness_adjustment: "0".to_string(),
2063            rpc_bigtable_timeout: "30".to_string(),
2064            rpc_bigtable_instance_name: solana_storage_bigtable::DEFAULT_INSTANCE_NAME.to_string(),
2065            rpc_bigtable_app_profile_id: solana_storage_bigtable::DEFAULT_APP_PROFILE_ID
2066                .to_string(),
2067            rpc_bigtable_max_message_size: solana_storage_bigtable::DEFAULT_MAX_MESSAGE_SIZE
2068                .to_string(),
2069            rpc_pubsub_worker_threads: "4".to_string(),
2070            accountsdb_repl_threads: num_cpus::get().to_string(),
2071            maximum_full_snapshot_archives_to_retain: DEFAULT_MAX_FULL_SNAPSHOT_ARCHIVES_TO_RETAIN
2072                .to_string(),
2073            maximum_incremental_snapshot_archives_to_retain:
2074                DEFAULT_MAX_INCREMENTAL_SNAPSHOT_ARCHIVES_TO_RETAIN.to_string(),
2075            snapshot_packager_niceness_adjustment: "0".to_string(),
2076            full_snapshot_archive_interval_slots: DEFAULT_FULL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS
2077                .to_string(),
2078            incremental_snapshot_archive_interval_slots:
2079                DEFAULT_INCREMENTAL_SNAPSHOT_ARCHIVE_INTERVAL_SLOTS.to_string(),
2080            min_snapshot_download_speed: DEFAULT_MIN_SNAPSHOT_DOWNLOAD_SPEED.to_string(),
2081            max_snapshot_download_abort: MAX_SNAPSHOT_DOWNLOAD_ABORT.to_string(),
2082            snapshot_archive_format: DEFAULT_ARCHIVE_COMPRESSION.to_string(),
2083            contact_debug_interval: "120000".to_string(),
2084            snapshot_version: SnapshotVersion::default(),
2085            rocksdb_shred_compaction: "level".to_string(),
2086            rocksdb_ledger_compression: "none".to_string(),
2087            rocksdb_perf_sample_interval: "0".to_string(),
2088            accounts_shrink_optimize_total_space: DEFAULT_ACCOUNTS_SHRINK_OPTIMIZE_TOTAL_SPACE
2089                .to_string(),
2090            accounts_shrink_ratio: DEFAULT_ACCOUNTS_SHRINK_RATIO.to_string(),
2091            tpu_connection_pool_size: DEFAULT_TPU_CONNECTION_POOL_SIZE.to_string(),
2092            rpc_max_request_body_size: MAX_REQUEST_BODY_SIZE.to_string(),
2093            exit_min_idle_time: "10".to_string(),
2094            exit_max_delinquent_stake: "5".to_string(),
2095            wait_for_restart_window_min_idle_time: "10".to_string(),
2096            wait_for_restart_window_max_delinquent_stake: "5".to_string(),
2097            banking_trace_dir_byte_limit: BANKING_TRACE_DIR_DEFAULT_BYTE_LIMIT.to_string(),
2098            wen_restart_path: "wen_restart_progress.proto".to_string(),
2099        }
2100    }
2101}
2102
2103impl Default for DefaultArgs {
2104    fn default() -> Self {
2105        Self::new()
2106    }
2107}
2108
2109pub fn port_validator(port: String) -> Result<(), String> {
2110    port.parse::<u16>()
2111        .map(|_| ())
2112        .map_err(|e| format!("{e:?}"))
2113}
2114
2115pub fn port_range_validator(port_range: String) -> Result<(), String> {
2116    if let Some((start, end)) = solana_net_utils::parse_port_range(&port_range) {
2117        if end - start < MINIMUM_VALIDATOR_PORT_RANGE_WIDTH {
2118            Err(format!(
2119                "Port range is too small.  Try --dynamic-port-range {}-{}",
2120                start,
2121                start + MINIMUM_VALIDATOR_PORT_RANGE_WIDTH
2122            ))
2123        } else if end.checked_add(QUIC_PORT_OFFSET).is_none() {
2124            Err("Invalid dynamic_port_range.".to_string())
2125        } else {
2126            Ok(())
2127        }
2128    } else {
2129        Err("Invalid port range".to_string())
2130    }
2131}
2132
2133fn hash_validator(hash: String) -> Result<(), String> {
2134    Hash::from_str(&hash)
2135        .map(|_| ())
2136        .map_err(|e| format!("{e:?}"))
2137}
2138
2139/// Test validator
2140
2141pub fn test_app<'a>(version: &'a str, default_args: &'a DefaultTestArgs) -> App<'a, 'a> {
2142    return App::new("solana-test-validator")
2143        .about("Test Validator")
2144        .version(version)
2145        .arg({
2146            let arg = Arg::with_name("config_file")
2147                .short("C")
2148                .long("config")
2149                .value_name("PATH")
2150                .takes_value(true)
2151                .help("Configuration file to use");
2152            if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
2153                arg.default_value(config_file)
2154            } else {
2155                arg
2156            }
2157        })
2158        .arg(
2159            Arg::with_name("json_rpc_url")
2160                .short("u")
2161                .long("url")
2162                .value_name("URL_OR_MONIKER")
2163                .takes_value(true)
2164                .validator(is_url_or_moniker)
2165                .help(
2166                    "URL for Solana's JSON RPC or moniker (or their first letter): \
2167                   [mainnet-beta, testnet, devnet, localhost]",
2168                ),
2169        )
2170        .arg(
2171            Arg::with_name("mint_address")
2172                .long("mint")
2173                .value_name("PUBKEY")
2174                .validator(is_pubkey)
2175                .takes_value(true)
2176                .help(
2177                    "Address of the mint account that will receive tokens \
2178                       created at genesis.  If the ledger already exists then \
2179                       this parameter is silently ignored [default: client keypair]",
2180                ),
2181        )
2182        .arg(
2183            Arg::with_name("ledger_path")
2184                .short("l")
2185                .long("ledger")
2186                .value_name("DIR")
2187                .takes_value(true)
2188                .required(true)
2189                .default_value("test-ledger")
2190                .help("Use DIR as ledger location"),
2191        )
2192        .arg(
2193            Arg::with_name("reset")
2194                .short("r")
2195                .long("reset")
2196                .takes_value(false)
2197                .help(
2198                    "Reset the ledger to genesis if it exists. \
2199                       By default the validator will resume an existing ledger (if present)",
2200                ),
2201        )
2202        .arg(
2203            Arg::with_name("quiet")
2204                .short("q")
2205                .long("quiet")
2206                .takes_value(false)
2207                .conflicts_with("log")
2208                .help("Quiet mode: suppress normal output"),
2209        )
2210        .arg(
2211            Arg::with_name("log")
2212                .long("log")
2213                .takes_value(false)
2214                .conflicts_with("quiet")
2215                .help("Log mode: stream the validator log"),
2216        )
2217        .arg(
2218            Arg::with_name("account_indexes")
2219                .long("account-index")
2220                .takes_value(true)
2221                .multiple(true)
2222                .possible_values(&["program-id", "spl-token-owner", "spl-token-mint"])
2223                .value_name("INDEX")
2224                .help("Enable an accounts index, indexed by the selected account field"),
2225        )
2226        .arg(
2227            Arg::with_name("faucet_port")
2228                .long("faucet-port")
2229                .value_name("PORT")
2230                .takes_value(true)
2231                .default_value(&default_args.faucet_port)
2232                .validator(port_validator)
2233                .help("Enable the faucet on this port"),
2234        )
2235        .arg(
2236            Arg::with_name("rpc_port")
2237                .long("rpc-port")
2238                .value_name("PORT")
2239                .takes_value(true)
2240                .default_value(&default_args.rpc_port)
2241                .validator(port_validator)
2242                .help("Enable JSON RPC on this port, and the next port for the RPC websocket"),
2243        )
2244        .arg(
2245            Arg::with_name("enable_rpc_bigtable_ledger_storage")
2246                .long("enable-rpc-bigtable-ledger-storage")
2247                .takes_value(false)
2248                .hidden(hidden_unless_forced())
2249                .help("Fetch historical transaction info from a BigTable instance \
2250                       as a fallback to local ledger data"),
2251        )
2252        .arg(
2253            Arg::with_name("rpc_bigtable_instance")
2254                .long("rpc-bigtable-instance")
2255                .value_name("INSTANCE_NAME")
2256                .takes_value(true)
2257                .hidden(hidden_unless_forced())
2258                .default_value("solana-ledger")
2259                .help("Name of BigTable instance to target"),
2260        )
2261        .arg(
2262            Arg::with_name("rpc_bigtable_app_profile_id")
2263                .long("rpc-bigtable-app-profile-id")
2264                .value_name("APP_PROFILE_ID")
2265                .takes_value(true)
2266                .hidden(hidden_unless_forced())
2267                .default_value(solana_storage_bigtable::DEFAULT_APP_PROFILE_ID)
2268                .help("Application profile id to use in Bigtable requests")
2269        )
2270        .arg(
2271            Arg::with_name("rpc_pubsub_enable_vote_subscription")
2272                .long("rpc-pubsub-enable-vote-subscription")
2273                .takes_value(false)
2274                .help("Enable the unstable RPC PubSub `voteSubscribe` subscription"),
2275        )
2276        .arg(
2277            Arg::with_name("rpc_pubsub_enable_block_subscription")
2278                .long("rpc-pubsub-enable-block-subscription")
2279                .takes_value(false)
2280                .help("Enable the unstable RPC PubSub `blockSubscribe` subscription"),
2281        )
2282        .arg(
2283            Arg::with_name("bpf_program")
2284                .long("bpf-program")
2285                .value_names(&["ADDRESS_OR_KEYPAIR", "SBF_PROGRAM.SO"])
2286                .takes_value(true)
2287                .number_of_values(2)
2288                .multiple(true)
2289                .help(
2290                    "Add a SBF program to the genesis configuration with upgrades disabled. \
2291                       If the ledger already exists then this parameter is silently ignored. \
2292                       First argument can be a pubkey string or path to a keypair",
2293                ),
2294        )
2295        .arg(
2296            Arg::with_name("upgradeable_program")
2297                .long("upgradeable-program")
2298                .value_names(&["ADDRESS_OR_KEYPAIR", "SBF_PROGRAM.SO", "UPGRADE_AUTHORITY"])
2299                .takes_value(true)
2300                .number_of_values(3)
2301                .multiple(true)
2302                .help(
2303                    "Add an upgradeable SBF program to the genesis configuration. \
2304                       If the ledger already exists then this parameter is silently ignored. \
2305                       First and third arguments can be a pubkey string or path to a keypair. \
2306                       Upgrade authority set to \"none\" disables upgrades",
2307                ),
2308        )
2309        .arg(
2310            Arg::with_name("account")
2311                .long("account")
2312                .value_names(&["ADDRESS", "DUMP.JSON"])
2313                .takes_value(true)
2314                .number_of_values(2)
2315                .allow_hyphen_values(true)
2316                .multiple(true)
2317                .help(
2318                    "Load an account from the provided JSON file (see `solana account --help` on how to dump \
2319                        an account to file). Files are searched for relatively to CWD and tests/fixtures. \
2320                        If ADDRESS is omitted via the `-` placeholder, the one in the file will be used. \
2321                        If the ledger already exists then this parameter is silently ignored",
2322                ),
2323        )
2324        .arg(
2325            Arg::with_name("account_dir")
2326                .long("account-dir")
2327                .value_name("DIRECTORY")
2328                .validator(|value| {
2329                    value
2330                        .parse::<PathBuf>()
2331                        .map_err(|err| format!("error parsing '{value}': {err}"))
2332                        .and_then(|path| {
2333                            if path.exists() && path.is_dir() {
2334                                Ok(())
2335                            } else {
2336                                Err(format!("path does not exist or is not a directory: {value}"))
2337                            }
2338                        })
2339                })
2340                .takes_value(true)
2341                .multiple(true)
2342                .help(
2343                    "Load all the accounts from the JSON files found in the specified DIRECTORY \
2344                        (see also the `--account` flag). \
2345                        If the ledger already exists then this parameter is silently ignored",
2346                ),
2347        )
2348        .arg(
2349            Arg::with_name("ticks_per_slot")
2350                .long("ticks-per-slot")
2351                .value_name("TICKS")
2352                .validator(|value| {
2353                    value
2354                        .parse::<u64>()
2355                        .map_err(|err| format!("error parsing '{value}': {err}"))
2356                        .and_then(|ticks| {
2357                            if ticks < MINIMUM_TICKS_PER_SLOT {
2358                                Err(format!("value must be >= {MINIMUM_TICKS_PER_SLOT}"))
2359                            } else {
2360                                Ok(())
2361                            }
2362                        })
2363                })
2364                .takes_value(true)
2365                .help("The number of ticks in a slot"),
2366        )
2367        .arg(
2368            Arg::with_name("slots_per_epoch")
2369                .long("slots-per-epoch")
2370                .value_name("SLOTS")
2371                .validator(|value| {
2372                    value
2373                        .parse::<Slot>()
2374                        .map_err(|err| format!("error parsing '{value}': {err}"))
2375                        .and_then(|slot| {
2376                            if slot < MINIMUM_SLOTS_PER_EPOCH {
2377                                Err(format!("value must be >= {MINIMUM_SLOTS_PER_EPOCH}"))
2378                            } else {
2379                                Ok(())
2380                            }
2381                        })
2382                })
2383                .takes_value(true)
2384                .help(
2385                    "Override the number of slots in an epoch. \
2386                       If the ledger already exists then this parameter is silently ignored",
2387                ),
2388        )
2389        .arg(
2390            Arg::with_name("gossip_port")
2391                .long("gossip-port")
2392                .value_name("PORT")
2393                .takes_value(true)
2394                .help("Gossip port number for the validator"),
2395        )
2396        .arg(
2397            Arg::with_name("gossip_host")
2398                .long("gossip-host")
2399                .value_name("HOST")
2400                .takes_value(true)
2401                .validator(solana_net_utils::is_host)
2402                .help(
2403                    "Gossip DNS name or IP address for the validator to advertise in gossip \
2404                       [default: 127.0.0.1]",
2405                ),
2406        )
2407        .arg(
2408            Arg::with_name("dynamic_port_range")
2409                .long("dynamic-port-range")
2410                .value_name("MIN_PORT-MAX_PORT")
2411                .takes_value(true)
2412                .validator(port_range_validator)
2413                .help(
2414                    "Range to use for dynamically assigned ports \
2415                    [default: 1024-65535]",
2416                ),
2417        )
2418        .arg(
2419            Arg::with_name("bind_address")
2420                .long("bind-address")
2421                .value_name("HOST")
2422                .takes_value(true)
2423                .validator(solana_net_utils::is_host)
2424                .default_value("0.0.0.0")
2425                .help("IP address to bind the validator ports [default: 0.0.0.0]"),
2426        )
2427        .arg(
2428            Arg::with_name("clone_account")
2429                .long("clone")
2430                .short("c")
2431                .value_name("ADDRESS")
2432                .takes_value(true)
2433                .validator(is_pubkey_or_keypair)
2434                .multiple(true)
2435                .requires("json_rpc_url")
2436                .help(
2437                    "Copy an account from the cluster referenced by the --url argument the \
2438                     genesis configuration. \
2439                     If the ledger already exists then this parameter is silently ignored",
2440                ),
2441        )
2442        .arg(
2443            Arg::with_name("maybe_clone_account")
2444                .long("maybe-clone")
2445                .value_name("ADDRESS")
2446                .takes_value(true)
2447                .validator(is_pubkey_or_keypair)
2448                .multiple(true)
2449                .requires("json_rpc_url")
2450                .help(
2451                    "Copy an account from the cluster referenced by the --url argument, \
2452                     skipping it if it doesn't exist. \
2453                     If the ledger already exists then this parameter is silently ignored",
2454                ),
2455        )
2456        .arg(
2457            Arg::with_name("clone_upgradeable_program")
2458                .long("clone-upgradeable-program")
2459                .value_name("ADDRESS")
2460                .takes_value(true)
2461                .validator(is_pubkey_or_keypair)
2462                .multiple(true)
2463                .requires("json_rpc_url")
2464                .help(
2465                    "Copy an upgradeable program and its executable data from the cluster \
2466                     referenced by the --url argument the genesis configuration. \
2467                     If the ledger already exists then this parameter is silently ignored",
2468                ),
2469        )
2470        .arg(
2471            Arg::with_name("warp_slot")
2472                .required(false)
2473                .long("warp-slot")
2474                .short("w")
2475                .takes_value(true)
2476                .value_name("WARP_SLOT")
2477                .validator(is_slot)
2478                .min_values(0)
2479                .max_values(1)
2480                .help(
2481                    "Warp the ledger to WARP_SLOT after starting the validator. \
2482                        If no slot is provided then the current slot of the cluster \
2483                        referenced by the --url argument will be used",
2484                ),
2485        )
2486        .arg(
2487            Arg::with_name("limit_ledger_size")
2488                .long("limit-ledger-size")
2489                .value_name("SHRED_COUNT")
2490                .takes_value(true)
2491                .default_value(default_args.limit_ledger_size.as_str())
2492                .help("Keep this amount of shreds in root slots."),
2493        )
2494        .arg(
2495            Arg::with_name("faucet_sol")
2496                .long("faucet-sol")
2497                .takes_value(true)
2498                .value_name("SOL")
2499                .default_value(default_args.faucet_sol.as_str())
2500                .help(
2501                    "Give the faucet address this much SOL in genesis. \
2502                     If the ledger already exists then this parameter is silently ignored",
2503                ),
2504        )
2505        .arg(
2506            Arg::with_name("faucet_time_slice_secs")
2507                .long("faucet-time-slice-secs")
2508                .takes_value(true)
2509                .value_name("SECS")
2510                .default_value(default_args.faucet_time_slice_secs.as_str())
2511                .help(
2512                    "Time slice (in secs) over which to limit faucet requests",
2513                ),
2514        )
2515        .arg(
2516            Arg::with_name("faucet_per_time_sol_cap")
2517                .long("faucet-per-time-sol-cap")
2518                .takes_value(true)
2519                .value_name("SOL")
2520                .min_values(0)
2521                .max_values(1)
2522                .help(
2523                    "Per-time slice limit for faucet requests, in SOL",
2524                ),
2525        )
2526        .arg(
2527            Arg::with_name("faucet_per_request_sol_cap")
2528                .long("faucet-per-request-sol-cap")
2529                .takes_value(true)
2530                .value_name("SOL")
2531                .min_values(0)
2532                .max_values(1)
2533                .help(
2534                    "Per-request limit for faucet requests, in SOL",
2535                ),
2536        )
2537        .arg(
2538            Arg::with_name("geyser_plugin_config")
2539                .long("geyser-plugin-config")
2540                .alias("accountsdb-plugin-config")
2541                .value_name("FILE")
2542                .takes_value(true)
2543                .multiple(true)
2544                .help("Specify the configuration file for the Geyser plugin."),
2545        )
2546        .arg(
2547            Arg::with_name("deactivate_feature")
2548                .long("deactivate-feature")
2549                .takes_value(true)
2550                .value_name("FEATURE_PUBKEY")
2551                .validator(is_pubkey)
2552                .multiple(true)
2553                .help("deactivate this feature in genesis.")
2554        )
2555        .arg(
2556            Arg::with_name("compute_unit_limit")
2557                .long("compute-unit-limit")
2558                .alias("max-compute-units")
2559                .value_name("COMPUTE_UNITS")
2560                .validator(is_parsable::<u64>)
2561                .takes_value(true)
2562                .help("Override the runtime's compute unit limit per transaction")
2563        )
2564        .arg(
2565            Arg::with_name("log_messages_bytes_limit")
2566                .long("log-messages-bytes-limit")
2567                .value_name("BYTES")
2568                .validator(is_parsable::<usize>)
2569                .takes_value(true)
2570                .help("Maximum number of bytes written to the program log before truncation")
2571        )
2572        .arg(
2573            Arg::with_name("transaction_account_lock_limit")
2574                .long("transaction-account-lock-limit")
2575                .value_name("NUM_ACCOUNTS")
2576                .validator(is_parsable::<u64>)
2577                .takes_value(true)
2578                .help("Override the runtime's account lock limit per transaction")
2579        );
2580}
2581
2582pub struct DefaultTestArgs {
2583    pub rpc_port: String,
2584    pub faucet_port: String,
2585    pub limit_ledger_size: String,
2586    pub faucet_sol: String,
2587    pub faucet_time_slice_secs: String,
2588}
2589
2590impl DefaultTestArgs {
2591    pub fn new() -> Self {
2592        DefaultTestArgs {
2593            rpc_port: rpc_port::DEFAULT_RPC_PORT.to_string(),
2594            faucet_port: FAUCET_PORT.to_string(),
2595            /* 10,000 was derived empirically by watching the size
2596             * of the rocksdb/ directory self-limit itself to the
2597             * 40MB-150MB range when running `solana-test-validator`
2598             */
2599            limit_ledger_size: 10_000.to_string(),
2600            faucet_sol: (1_000_000.).to_string(),
2601            faucet_time_slice_secs: (faucet::TIME_SLICE).to_string(),
2602        }
2603    }
2604}
2605
2606impl Default for DefaultTestArgs {
2607    fn default() -> Self {
2608        Self::new()
2609    }
2610}
2611
2612#[cfg(test)]
2613mod test {
2614    use super::*;
2615
2616    #[test]
2617    fn make_sure_deprecated_arguments_are_sorted_alphabetically() {
2618        let deprecated = deprecated_arguments();
2619
2620        for i in 0..deprecated.len().saturating_sub(1) {
2621            let curr_name = deprecated[i].arg.b.name;
2622            let next_name = deprecated[i + 1].arg.b.name;
2623
2624            assert!(
2625                curr_name != next_name,
2626                "Arguments in `deprecated_arguments()` should be distinct.\n\
2627                 Arguments {} and {} use the same name: {}",
2628                i,
2629                i + 1,
2630                curr_name,
2631            );
2632
2633            assert!(
2634                curr_name < next_name,
2635                "To generate better diffs and for readability purposes, `deprecated_arguments()` \
2636                 should list arguments in alphabetical order.\n\
2637                 Arguments {} and {} are not.\n\
2638                 Argument {} name: {}\n\
2639                 Argument {} name: {}",
2640                i,
2641                i + 1,
2642                i,
2643                curr_name,
2644                i + 1,
2645                next_name,
2646            );
2647        }
2648    }
2649}