Cli: promote commitment to a global arg + config.yml (#14684)

* Make commitment a global arg

* Add commitment to solana/cli/config.yml

* Fixup a couple Display/Verbose bugs
This commit is contained in:
Tyera Eulberg
2021-01-20 09:48:10 -07:00
committed by GitHub
parent ed90ef76d4
commit a7086a0f83
11 changed files with 153 additions and 102 deletions

View File

@ -1,22 +0,0 @@
use crate::ArgConstant;
use clap::Arg;
pub const COMMITMENT_ARG: ArgConstant<'static> = ArgConstant {
name: "commitment",
long: "commitment",
help: "Return information at the selected commitment level",
};
pub fn commitment_arg<'a, 'b>() -> Arg<'a, 'b> {
commitment_arg_with_default("recent")
}
pub fn commitment_arg_with_default<'a, 'b>(default_value: &'static str) -> Arg<'a, 'b> {
Arg::with_name(COMMITMENT_ARG.name)
.long(COMMITMENT_ARG.long)
.takes_value(true)
.possible_values(&["recent", "single", "singleGossip", "root", "max"])
.default_value(default_value)
.value_name("COMMITMENT_LEVEL")
.help(COMMITMENT_ARG.help)
}

View File

@ -184,14 +184,9 @@ pub fn cluster_type_of(matches: &ArgMatches<'_>, name: &str) -> Option<ClusterTy
} }
pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> { pub fn commitment_of(matches: &ArgMatches<'_>, name: &str) -> Option<CommitmentConfig> {
matches.value_of(name).map(|value| match value { matches
"max" => CommitmentConfig::max(), .value_of(name)
"recent" => CommitmentConfig::recent(), .map(|value| CommitmentConfig::from_str(value).unwrap_or_default())
"root" => CommitmentConfig::root(),
"single" => CommitmentConfig::single(),
"singleGossip" => CommitmentConfig::single_gossip(),
_ => CommitmentConfig::default(),
})
} }
#[cfg(test)] #[cfg(test)]

View File

@ -23,7 +23,6 @@ impl std::fmt::Debug for DisplayError {
} }
} }
pub mod commitment;
pub mod fee_payer; pub mod fee_payer;
pub mod input_parsers; pub mod input_parsers;
pub mod input_validators; pub mod input_validators;

View File

@ -17,9 +17,10 @@ pub struct Config {
pub json_rpc_url: String, pub json_rpc_url: String,
pub websocket_url: String, pub websocket_url: String,
pub keypair_path: String, pub keypair_path: String,
#[serde(default)] #[serde(default)]
pub address_labels: HashMap<String, String>, pub address_labels: HashMap<String, String>,
#[serde(default)]
pub commitment: String,
} }
impl Default for Config { impl Default for Config {
@ -41,11 +42,14 @@ impl Default for Config {
"System Program".to_string(), "System Program".to_string(),
); );
let commitment = "singleGossip".to_string();
Self { Self {
json_rpc_url, json_rpc_url,
websocket_url, websocket_url,
keypair_path, keypair_path,
address_labels, address_labels,
commitment,
} }
} }
} }

View File

@ -9,7 +9,6 @@ use serde_json::{self, Value};
use solana_account_decoder::{UiAccount, UiAccountEncoding}; use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_clap_utils::{ use solana_clap_utils::{
self, self,
commitment::commitment_arg_with_default,
fee_payer::{fee_payer_arg, FEE_PAYER_ARG}, fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
@ -431,6 +430,10 @@ impl CliConfig<'_> {
solana_cli_config::Config::default().websocket_url solana_cli_config::Config::default().websocket_url
} }
fn default_commitment() -> CommitmentConfig {
CommitmentConfig::single_gossip()
}
fn first_nonempty_setting( fn first_nonempty_setting(
settings: std::vec::Vec<(SettingType, String)>, settings: std::vec::Vec<(SettingType, String)>,
) -> (SettingType, String) { ) -> (SettingType, String) {
@ -440,6 +443,16 @@ impl CliConfig<'_> {
.expect("no nonempty setting") .expect("no nonempty setting")
} }
fn first_setting_is_some<T>(
settings: std::vec::Vec<(SettingType, Option<T>)>,
) -> (SettingType, T) {
let (setting_type, setting_option) = settings
.into_iter()
.find(|(_, value)| value.is_some())
.expect("all settings none");
(setting_type, setting_option.unwrap())
}
pub fn compute_websocket_url_setting( pub fn compute_websocket_url_setting(
websocket_cmd_url: &str, websocket_cmd_url: &str,
websocket_cfg_url: &str, websocket_cfg_url: &str,
@ -484,6 +497,23 @@ impl CliConfig<'_> {
]) ])
} }
pub fn compute_commitment_config(
commitment_cmd: &str,
commitment_cfg: &str,
) -> (SettingType, CommitmentConfig) {
Self::first_setting_is_some(vec![
(
SettingType::Explicit,
CommitmentConfig::from_str(commitment_cmd).ok(),
),
(
SettingType::Explicit,
CommitmentConfig::from_str(commitment_cfg).ok(),
),
(SettingType::SystemDefault, Some(Self::default_commitment())),
])
}
pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> { pub(crate) fn pubkey(&self) -> Result<Pubkey, SignerError> {
if !self.signers.is_empty() { if !self.signers.is_empty() {
self.signers[0].try_pubkey() self.signers[0].try_pubkey()
@ -1019,7 +1049,9 @@ fn process_show_account(
let mut account_string = config.output_format.formatted_string(&cli_account); let mut account_string = config.output_format.formatted_string(&cli_account);
if config.output_format == OutputFormat::Display { if config.output_format == OutputFormat::Display
|| config.output_format == OutputFormat::DisplayVerbose
{
if let Some(output_file) = output_file { if let Some(output_file) = output_file {
let mut f = File::create(output_file)?; let mut f = File::create(output_file)?;
f.write_all(&data)?; f.write_all(&data)?;
@ -1107,12 +1139,13 @@ fn process_transfer(
} }
pub fn process_command(config: &CliConfig) -> ProcessResult { pub fn process_command(config: &CliConfig) -> ProcessResult {
if config.verbose && config.output_format == OutputFormat::Display { if config.verbose && config.output_format == OutputFormat::DisplayVerbose {
println_name_value("RPC URL:", &config.json_rpc_url); println_name_value("RPC URL:", &config.json_rpc_url);
println_name_value("Default Signer Path:", &config.keypair_path); println_name_value("Default Signer Path:", &config.keypair_path);
if config.keypair_path.starts_with("usb://") { if config.keypair_path.starts_with("usb://") {
println_name_value("Pubkey:", &format!("{:?}", config.pubkey()?)); println_name_value("Pubkey:", &format!("{:?}", config.pubkey()?));
} }
println_name_value("Commitment:", &config.commitment.commitment.to_string());
} }
let mut _rpc_client; let mut _rpc_client;
@ -1851,8 +1884,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.long("lamports") .long("lamports")
.takes_value(false) .takes_value(false)
.help("Display balance in lamports instead of SOL"), .help("Display balance in lamports instead of SOL"),
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("confirm") SubCommand::with_name("confirm")
@ -1949,8 +1981,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.long("allow-excessive-deploy-account-balance") .long("allow-excessive-deploy-account-balance")
.takes_value(false) .takes_value(false)
.help("Use the designated program id, even if the account already holds a large balance of SOL") .help("Use the designated program id, even if the account already holds a large balance of SOL")
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("pay") SubCommand::with_name("pay")

View File

@ -7,7 +7,6 @@ use chrono::{Local, TimeZone};
use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{value_t, value_t_or_exit, App, AppSettings, Arg, ArgMatches, SubCommand};
use console::{style, Emoji}; use console::{style, Emoji};
use solana_clap_utils::{ use solana_clap_utils::{
commitment::{commitment_arg, commitment_arg_with_default},
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
keypair::DefaultSigner, keypair::DefaultSigner,
@ -121,20 +120,17 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.long("log") .long("log")
.takes_value(false) .takes_value(false)
.help("Don't update the progress inplace; instead show updates with its own new lines"), .help("Don't update the progress inplace; instead show updates with its own new lines"),
) ),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("cluster-date") SubCommand::with_name("cluster-date")
.about("Get current cluster date, computed from genesis creation time and network time") .about("Get current cluster date, computed from genesis creation time and network time"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("cluster-version") SubCommand::with_name("cluster-version")
.about("Get the version of the cluster entrypoint"), .about("Get the version of the cluster entrypoint"),
) )
.subcommand(SubCommand::with_name("fees").about("Display current cluster fees") .subcommand(SubCommand::with_name("fees").about("Display current cluster fees"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("first-available-block") SubCommand::with_name("first-available-block")
@ -165,8 +161,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.subcommand( .subcommand(
SubCommand::with_name("epoch-info") SubCommand::with_name("epoch-info")
.about("Get information about the current epoch") .about("Get information about the current epoch")
.alias("get-epoch-info") .alias("get-epoch-info"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("genesis-hash") SubCommand::with_name("genesis-hash")
@ -175,16 +170,13 @@ impl ClusterQuerySubCommands for App<'_, '_> {
) )
.subcommand( .subcommand(
SubCommand::with_name("slot").about("Get current slot") SubCommand::with_name("slot").about("Get current slot")
.alias("get-slot") .alias("get-slot"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("block-height").about("Get current block height") SubCommand::with_name("block-height").about("Get current block height"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("epoch").about("Get current epoch") SubCommand::with_name("epoch").about("Get current epoch"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("largest-accounts").about("Get addresses of largest cluster accounts") SubCommand::with_name("largest-accounts").about("Get addresses of largest cluster accounts")
@ -200,8 +192,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(false) .takes_value(false)
.conflicts_with("circulating") .conflicts_with("circulating")
.help("Filter address list to only non-circulating accounts") .help("Filter address list to only non-circulating accounts")
) ),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("supply").about("Get information about the cluster supply of SOL") SubCommand::with_name("supply").about("Get information about the cluster supply of SOL")
@ -210,18 +201,15 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.long("print-accounts") .long("print-accounts")
.takes_value(false) .takes_value(false)
.help("Print list of non-circualting account addresses") .help("Print list of non-circualting account addresses")
) ),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("total-supply").about("Get total number of SOL") SubCommand::with_name("total-supply").about("Get total number of SOL")
.setting(AppSettings::Hidden) .setting(AppSettings::Hidden),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("transaction-count").about("Get current transaction count") SubCommand::with_name("transaction-count").about("Get current transaction count")
.alias("get-transaction-count") .alias("get-transaction-count"),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("ping") SubCommand::with_name("ping")
@ -268,8 +256,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.default_value("15") .default_value("15")
.help("Wait up to timeout seconds for transaction confirmation"), .help("Wait up to timeout seconds for transaction confirmation"),
) )
.arg(blockhash_arg()) .arg(blockhash_arg()),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("live-slots") SubCommand::with_name("live-slots")
@ -292,8 +279,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.takes_value(false) .takes_value(false)
.conflicts_with("address") .conflicts_with("address")
.help("Include vote transactions when monitoring all transactions") .help("Include vote transactions when monitoring all transactions")
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("block-production") SubCommand::with_name("block-production")
@ -343,8 +329,7 @@ impl ClusterQuerySubCommands for App<'_, '_> {
.long("lamports") .long("lamports")
.takes_value(false) .takes_value(false)
.help("Display balance in lamports instead of SOL"), .help("Display balance in lamports instead of SOL"),
) ),
.arg(commitment_arg()),
) )
.subcommand( .subcommand(
SubCommand::with_name("transaction-history") SubCommand::with_name("transaction-history")
@ -1984,8 +1969,6 @@ mod tests {
"-t", "-t",
"3", "3",
"-D", "-D",
"--commitment",
"max",
"--blockhash", "--blockhash",
"4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX", "4CCNp28j6AhGq7PkjPDP4wbQWBS8LLbQin2xV5n8frKX",
]); ]);

View File

@ -3,10 +3,7 @@ use clap::{
SubCommand, SubCommand,
}; };
use console::style; use console::style;
use solana_clap_utils::{ use solana_clap_utils::{
commitment::COMMITMENT_ARG,
input_parsers::commitment_of,
input_validators::{is_url, is_url_or_moniker}, input_validators::{is_url, is_url_or_moniker},
keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG}, keypair::{CliSigners, DefaultSigner, SKIP_SEED_PHRASE_VALIDATION_ARG},
DisplayError, DisplayError,
@ -19,7 +16,6 @@ use solana_cli_config::{Config, CONFIG_FILE};
use solana_cli_output::{display::println_name_value, OutputFormat}; use solana_cli_output::{display::println_name_value, OutputFormat};
use solana_client::rpc_config::RpcSendTransactionConfig; use solana_client::rpc_config::RpcSendTransactionConfig;
use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_remote_wallet::remote_wallet::RemoteWalletManager;
use solana_sdk::commitment_config::CommitmentConfig;
use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration}; use std::{collections::HashMap, error, path::PathBuf, sync::Arc, time::Duration};
pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) { pub fn println_name_value_or(name: &str, value: &str, setting_type: SettingType) {
@ -64,12 +60,19 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
); );
let (keypair_setting_type, keypair_path) = let (keypair_setting_type, keypair_path) =
CliConfig::compute_keypair_path_setting("", &config.keypair_path); CliConfig::compute_keypair_path_setting("", &config.keypair_path);
let (commitment_setting_type, commitment) =
CliConfig::compute_commitment_config("", &config.commitment);
if let Some(field) = subcommand_matches.value_of("specific_setting") { if let Some(field) = subcommand_matches.value_of("specific_setting") {
let (field_name, value, setting_type) = match field { let (field_name, value, setting_type) = match field {
"json_rpc_url" => ("RPC URL", json_rpc_url, url_setting_type), "json_rpc_url" => ("RPC URL", json_rpc_url, url_setting_type),
"websocket_url" => ("WebSocket URL", websocket_url, ws_setting_type), "websocket_url" => ("WebSocket URL", websocket_url, ws_setting_type),
"keypair" => ("Key Path", keypair_path, keypair_setting_type), "keypair" => ("Key Path", keypair_path, keypair_setting_type),
"commitment" => (
"Commitment",
commitment.commitment.to_string(),
commitment_setting_type,
),
_ => unreachable!(), _ => unreachable!(),
}; };
println_name_value_or(&format!("{}:", field_name), &value, setting_type); println_name_value_or(&format!("{}:", field_name), &value, setting_type);
@ -78,6 +81,11 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type); println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type); println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type); println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
println_name_value_or(
"Commitment:",
&commitment.commitment.to_string(),
commitment_setting_type,
);
} }
} }
("set", Some(subcommand_matches)) => { ("set", Some(subcommand_matches)) => {
@ -93,6 +101,9 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
if let Some(keypair) = subcommand_matches.value_of("keypair") { if let Some(keypair) = subcommand_matches.value_of("keypair") {
config.keypair_path = keypair.to_string(); config.keypair_path = keypair.to_string();
} }
if let Some(commitment) = subcommand_matches.value_of("commitment") {
config.commitment = commitment.to_string();
}
config.save(config_file)?; config.save(config_file)?;
@ -106,11 +117,18 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
); );
let (keypair_setting_type, keypair_path) = let (keypair_setting_type, keypair_path) =
CliConfig::compute_keypair_path_setting("", &config.keypair_path); CliConfig::compute_keypair_path_setting("", &config.keypair_path);
let (commitment_setting_type, commitment) =
CliConfig::compute_commitment_config("", &config.commitment);
println_name_value("Config File:", config_file); println_name_value("Config File:", config_file);
println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type); println_name_value_or("RPC URL:", &json_rpc_url, url_setting_type);
println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type); println_name_value_or("WebSocket URL:", &websocket_url, ws_setting_type);
println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type); println_name_value_or("Keypair Path:", &keypair_path, keypair_setting_type);
println_name_value_or(
"Commitment:",
&commitment.commitment.to_string(),
commitment_setting_type,
);
} }
("import-address-labels", Some(subcommand_matches)) => { ("import-address-labels", Some(subcommand_matches)) => {
let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf); let filename = value_t_or_exit!(subcommand_matches, "filename", PathBuf);
@ -183,16 +201,10 @@ pub fn parse_args<'a>(
OutputFormat::Display OutputFormat::Display
}); });
let commitment = { let (_, commitment) = CliConfig::compute_commitment_config(
let mut sub_matches = matches; matches.value_of("commitment").unwrap_or(""),
while let Some(subcommand_name) = sub_matches.subcommand_name() { &config.commitment,
sub_matches = sub_matches );
.subcommand_matches(subcommand_name)
.expect("subcommand_matches");
}
commitment_of(sub_matches, COMMITMENT_ARG.long)
}
.unwrap_or_else(CommitmentConfig::single_gossip);
let address_labels = if matches.is_present("no_address_labels") { let address_labels = if matches.is_present("no_address_labels") {
HashMap::new() HashMap::new()
@ -274,6 +286,15 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.takes_value(true) .takes_value(true)
.help("Filepath or URL to a keypair"), .help("Filepath or URL to a keypair"),
) )
.arg(
Arg::with_name("commitment")
.long("commitment")
.takes_value(true)
.possible_values(&["recent", "single", "singleGossip", "root", "max"])
.value_name("COMMITMENT_LEVEL")
.global(true)
.help("Return information at the selected commitment level"),
)
.arg( .arg(
Arg::with_name("verbose") Arg::with_name("verbose")
.long("verbose") .long("verbose")
@ -325,7 +346,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.index(1) .index(1)
.value_name("CONFIG_FIELD") .value_name("CONFIG_FIELD")
.takes_value(true) .takes_value(true)
.possible_values(&["json_rpc_url", "websocket_url", "keypair"]) .possible_values(&[
"json_rpc_url",
"websocket_url",
"keypair",
"commitment",
])
.help("Return a specific config setting"), .help("Return a specific config setting"),
), ),
) )
@ -334,7 +360,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.about("Set a config setting") .about("Set a config setting")
.group( .group(
ArgGroup::with_name("config_settings") ArgGroup::with_name("config_settings")
.args(&["json_rpc_url", "websocket_url", "keypair"]) .args(&["json_rpc_url", "websocket_url", "keypair", "commitment"])
.multiple(true) .multiple(true)
.required(true), .required(true),
), ),

View File

@ -12,10 +12,7 @@ use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use log::*; use log::*;
use serde_json::{self, json}; use serde_json::{self, json};
use solana_bpf_loader_program::{bpf_verifier, BPFError, ThisInstructionMeter}; use solana_bpf_loader_program::{bpf_verifier, BPFError, ThisInstructionMeter};
use solana_clap_utils::{ use solana_clap_utils::{self, input_parsers::*, input_validators::*, keypair::*};
self, commitment::commitment_arg_with_default, input_parsers::*, input_validators::*,
keypair::*,
};
use solana_cli_output::display::new_spinner_progress_bar; use solana_cli_output::display::new_spinner_progress_bar;
use solana_client::{ use solana_client::{
rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig, rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig,
@ -148,8 +145,7 @@ impl ProgramSubCommands for App<'_, '_> {
.long("allow-excessive-deploy-account-balance") .long("allow-excessive-deploy-account-balance")
.takes_value(false) .takes_value(false)
.help("Use the designated program id even if the account already holds a large balance of SOL") .help("Use the designated program id even if the account already holds a large balance of SOL")
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("write-buffer") SubCommand::with_name("write-buffer")
@ -191,8 +187,7 @@ impl ProgramSubCommands for App<'_, '_> {
.required(false) .required(false)
.help("Maximum length of the upgradeable program \ .help("Maximum length of the upgradeable program \
[default: twice the length of the original deployed program]") [default: twice the length of the original deployed program]")
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("set-buffer-authority") SubCommand::with_name("set-buffer-authority")
@ -270,8 +265,7 @@ impl ProgramSubCommands for App<'_, '_> {
.takes_value(true) .takes_value(true)
.required(true) .required(true)
.help("Public key of the account to query") .help("Public key of the account to query")
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
) )
} }

View File

@ -10,7 +10,6 @@ use crate::{
use chrono::{Local, TimeZone}; use chrono::{Local, TimeZone};
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand}; use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{
commitment::commitment_arg_with_default,
fee_payer::{fee_payer_arg, FEE_PAYER_ARG}, fee_payer::{fee_payer_arg, FEE_PAYER_ARG},
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
@ -404,8 +403,7 @@ impl StakeSubCommands for App<'_, '_> {
.long("lamports") .long("lamports")
.takes_value(false) .takes_value(false)
.help("Display balance in lamports instead of SOL") .help("Display balance in lamports instead of SOL")
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("stake-history") SubCommand::with_name("stake-history")

View File

@ -8,7 +8,6 @@ use crate::{
}; };
use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand}; use clap::{value_t_or_exit, App, Arg, ArgMatches, SubCommand};
use solana_clap_utils::{ use solana_clap_utils::{
commitment::commitment_arg_with_default,
input_parsers::*, input_parsers::*,
input_validators::*, input_validators::*,
keypair::{DefaultSigner, SignerIndex}, keypair::{DefaultSigner, SignerIndex},
@ -208,8 +207,7 @@ impl VoteSubCommands for App<'_, '_> {
.long("lamports") .long("lamports")
.takes_value(false) .takes_value(false)
.help("Display balance in lamports instead of SOL"), .help("Display balance in lamports instead of SOL"),
) ),
.arg(commitment_arg_with_default("singleGossip")),
) )
.subcommand( .subcommand(
SubCommand::with_name("withdraw-from-vote-account") SubCommand::with_name("withdraw-from-vote-account")

View File

@ -1,3 +1,6 @@
use std::str::FromStr;
use thiserror::Error;
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq)] #[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CommitmentConfig { pub struct CommitmentConfig {
@ -44,6 +47,14 @@ impl CommitmentConfig {
} }
} }
impl FromStr for CommitmentConfig {
type Err = ParseCommitmentLevelError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
CommitmentLevel::from_str(s).map(|commitment| Self { commitment })
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
/// An attribute of a slot. It describes how finalized a block is at some point in time. For example, a slot /// An attribute of a slot. It describes how finalized a block is at some point in time. For example, a slot
@ -79,3 +90,37 @@ impl Default for CommitmentLevel {
Self::Max Self::Max
} }
} }
impl FromStr for CommitmentLevel {
type Err = ParseCommitmentLevelError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"max" => Ok(CommitmentLevel::Max),
"recent" => Ok(CommitmentLevel::Recent),
"root" => Ok(CommitmentLevel::Root),
"single" => Ok(CommitmentLevel::Single),
"singleGossip" => Ok(CommitmentLevel::SingleGossip),
_ => Err(ParseCommitmentLevelError::Invalid),
}
}
}
impl std::fmt::Display for CommitmentLevel {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s = match self {
CommitmentLevel::Max => "max",
CommitmentLevel::Recent => "recent",
CommitmentLevel::Root => "root",
CommitmentLevel::Single => "single",
CommitmentLevel::SingleGossip => "singleGossip",
};
write!(f, "{}", s)
}
}
#[derive(Error, Debug)]
pub enum ParseCommitmentLevelError {
#[error("invalid variant")]
Invalid,
}