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