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