miraland_validator/
cli.rs

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