update clap to v3: keygen

This commit is contained in:
klykov
2022-03-08 11:21:21 +01:00
parent c3de3886a9
commit f00254000b
2 changed files with 79 additions and 75 deletions

View File

@@ -11,7 +11,7 @@ edition = "2021"
[dependencies] [dependencies]
bs58 = "0.4.0" bs58 = "0.4.0"
clap = "3.1" clap = { version = "3.1.5", features = ["cargo"] }
dirs-next = "2.0.0" dirs-next = "2.0.0"
num_cpus = "1.13.1" num_cpus = "1.13.1"
solana-clap-utils = { path = "../clap-utils", version = "=1.10.1" } solana-clap-utils = { path = "../clap-utils", version = "=1.10.1" }

View File

@@ -1,10 +1,7 @@
#![allow(clippy::integer_arithmetic)] #![allow(clippy::integer_arithmetic)]
use { use {
bip39::{Language, Mnemonic, MnemonicType, Seed}, bip39::{Language, Mnemonic, MnemonicType, Seed},
clap::{ clap::{crate_description, crate_name, Arg, ArgMatches, Command},
crate_description, crate_name, value_t, value_t_or_exit, values_t_or_exit, App,
AppSettings, Arg, ArgMatches, SubCommand,
},
solana_clap_utils::{ solana_clap_utils::{
input_parsers::STDOUT_OUTFILE_TOKEN, input_parsers::STDOUT_OUTFILE_TOKEN,
input_validators::{is_parsable, is_prompt_signer_source}, input_validators::{is_parsable, is_prompt_signer_source},
@@ -68,8 +65,8 @@ const NO_OUTFILE_ARG: ArgConstant<'static> = ArgConstant {
help: "Only print a seed phrase and pubkey. Do not output a keypair file", help: "Only print a seed phrase and pubkey. Do not output a keypair file",
}; };
fn word_count_arg<'a, 'b>() -> Arg<'a, 'b> { fn word_count_arg<'a>() -> Arg<'a> {
Arg::with_name(WORD_COUNT_ARG.name) Arg::new(WORD_COUNT_ARG.name)
.long(WORD_COUNT_ARG.long) .long(WORD_COUNT_ARG.long)
.possible_values(&["12", "15", "18", "21", "24"]) .possible_values(&["12", "15", "18", "21", "24"])
.default_value("12") .default_value("12")
@@ -78,8 +75,8 @@ fn word_count_arg<'a, 'b>() -> Arg<'a, 'b> {
.help(WORD_COUNT_ARG.help) .help(WORD_COUNT_ARG.help)
} }
fn language_arg<'a, 'b>() -> Arg<'a, 'b> { fn language_arg<'a>() -> Arg<'a> {
Arg::with_name(LANGUAGE_ARG.name) Arg::new(LANGUAGE_ARG.name)
.long(LANGUAGE_ARG.long) .long(LANGUAGE_ARG.long)
.possible_values(&[ .possible_values(&[
"english", "english",
@@ -97,15 +94,15 @@ fn language_arg<'a, 'b>() -> Arg<'a, 'b> {
.help(LANGUAGE_ARG.help) .help(LANGUAGE_ARG.help)
} }
fn no_passphrase_arg<'a, 'b>() -> Arg<'a, 'b> { fn no_passphrase_arg<'a>() -> Arg<'a> {
Arg::with_name(NO_PASSPHRASE_ARG.name) Arg::new(NO_PASSPHRASE_ARG.name)
.long(NO_PASSPHRASE_ARG.long) .long(NO_PASSPHRASE_ARG.long)
.alias("no-passphrase") .alias("no-passphrase")
.help(NO_PASSPHRASE_ARG.help) .help(NO_PASSPHRASE_ARG.help)
} }
fn no_outfile_arg<'a, 'b>() -> Arg<'a, 'b> { fn no_outfile_arg<'a>() -> Arg<'a> {
Arg::with_name(NO_OUTFILE_ARG.name) Arg::new(NO_OUTFILE_ARG.name)
.long(NO_OUTFILE_ARG.long) .long(NO_OUTFILE_ARG.long)
.conflicts_with_all(&["outfile", "silent"]) .conflicts_with_all(&["outfile", "silent"])
.help(NO_OUTFILE_ARG.help) .help(NO_OUTFILE_ARG.help)
@@ -115,7 +112,7 @@ trait KeyGenerationCommonArgs {
fn key_generation_common_args(self) -> Self; fn key_generation_common_args(self) -> Self;
} }
impl KeyGenerationCommonArgs for App<'_, '_> { impl KeyGenerationCommonArgs for Command<'_> {
fn key_generation_common_args(self) -> Self { fn key_generation_common_args(self) -> Self {
self.arg(word_count_arg()) self.arg(word_count_arg())
.arg(language_arg()) .arg(language_arg())
@@ -213,7 +210,7 @@ fn grind_validator_starts_and_ends_with(v: String) -> Result<(), String> {
Ok(()) Ok(())
} }
fn acquire_language(matches: &ArgMatches<'_>) -> Language { fn acquire_language(matches: &ArgMatches) -> Language {
match matches.value_of(LANGUAGE_ARG.name).unwrap() { match matches.value_of(LANGUAGE_ARG.name).unwrap() {
"english" => Language::English, "english" => Language::English,
"chinese-simplified" => Language::ChineseSimplified, "chinese-simplified" => Language::ChineseSimplified,
@@ -232,7 +229,7 @@ fn no_passphrase_and_message() -> (String, String) {
} }
fn acquire_passphrase_and_message( fn acquire_passphrase_and_message(
matches: &ArgMatches<'_>, matches: &ArgMatches,
) -> Result<(String, String), Box<dyn error::Error>> { ) -> Result<(String, String), Box<dyn error::Error>> {
if matches.is_present(NO_PASSPHRASE_ARG.name) { if matches.is_present(NO_PASSPHRASE_ARG.name) {
Ok(no_passphrase_and_message()) Ok(no_passphrase_and_message())
@@ -331,13 +328,14 @@ fn grind_parse_args(
fn main() -> Result<(), Box<dyn error::Error>> { fn main() -> Result<(), Box<dyn error::Error>> {
let default_num_threads = num_cpus::get().to_string(); let default_num_threads = num_cpus::get().to_string();
let matches = App::new(crate_name!()) let matches = Command::new(crate_name!())
.about(crate_description!()) .about(crate_description!())
.version(solana_version::version!()) .version(solana_version::version!())
.setting(AppSettings::SubcommandRequiredElseHelp) .subcommand_required(true)
.arg_required_else_help(true)
.arg({ .arg({
let arg = Arg::with_name("config_file") let arg = Arg::new("config_file")
.short("C") .short('C')
.long("config") .long("config")
.value_name("FILEPATH") .value_name("FILEPATH")
.takes_value(true) .takes_value(true)
@@ -350,10 +348,10 @@ fn main() -> Result<(), Box<dyn error::Error>> {
} }
}) })
.subcommand( .subcommand(
SubCommand::with_name("verify") Command::new("verify")
.about("Verify a keypair can sign and verify a message.") .about("Verify a keypair can sign and verify a message.")
.arg( .arg(
Arg::with_name("pubkey") Arg::new("pubkey")
.index(1) .index(1)
.value_name("PUBKEY") .value_name("PUBKEY")
.takes_value(true) .takes_value(true)
@@ -361,7 +359,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.help("Public key"), .help("Public key"),
) )
.arg( .arg(
Arg::with_name("keypair") Arg::new("keypair")
.index(2) .index(2)
.value_name("KEYPAIR") .value_name("KEYPAIR")
.takes_value(true) .takes_value(true)
@@ -369,26 +367,26 @@ fn main() -> Result<(), Box<dyn error::Error>> {
) )
) )
.subcommand( .subcommand(
SubCommand::with_name("new") Command::new("new")
.about("Generate new keypair file from a random seed phrase and optional BIP39 passphrase") .about("Generate new keypair file from a random seed phrase and optional BIP39 passphrase")
.setting(AppSettings::DisableVersion) .disable_version_flag(true)
.arg( .arg(
Arg::with_name("outfile") Arg::new("outfile")
.short("o") .short('o')
.long("outfile") .long("outfile")
.value_name("FILEPATH") .value_name("FILEPATH")
.takes_value(true) .takes_value(true)
.help("Path to generated file"), .help("Path to generated file"),
) )
.arg( .arg(
Arg::with_name("force") Arg::new("force")
.short("f") .short('f')
.long("force") .long("force")
.help("Overwrite the output file if it exists"), .help("Overwrite the output file if it exists"),
) )
.arg( .arg(
Arg::with_name("silent") Arg::new("silent")
.short("s") .short('s')
.long("silent") .long("silent")
.help("Do not display seed phrase. Useful when piping output to other programs that prompt for user input, like gpg"), .help("Do not display seed phrase. Useful when piping output to other programs that prompt for user input, like gpg"),
) )
@@ -396,55 +394,58 @@ fn main() -> Result<(), Box<dyn error::Error>> {
.arg(no_outfile_arg()) .arg(no_outfile_arg())
) )
.subcommand( .subcommand(
SubCommand::with_name("grind") Command::new("grind")
.about("Grind for vanity keypairs") .about("Grind for vanity keypairs")
.setting(AppSettings::DisableVersion) .disable_version_flag(true)
.arg( .arg(
Arg::with_name("ignore_case") Arg::new("ignore_case")
.long("ignore-case") .long("ignore-case")
.help("Performs case insensitive matches"), .help("Performs case insensitive matches"),
) )
.arg( .arg(
Arg::with_name("starts_with") Arg::new("starts_with")
.long("starts-with") .long("starts-with")
.value_name("PREFIX:COUNT") .value_name("PREFIX:COUNT")
.number_of_values(1) .number_of_values(1)
.takes_value(true) .takes_value(true)
.multiple(true) .multiple_occurrences(true)
.validator(grind_validator_starts_with) .multiple_values(true)
.validator(|s| grind_validator_starts_with(s.to_string()))
.help("Saves specified number of keypairs whos public key starts with the indicated prefix\nExample: --starts-with sol:4\nPREFIX type is Base58\nCOUNT type is u64"), .help("Saves specified number of keypairs whos public key starts with the indicated prefix\nExample: --starts-with sol:4\nPREFIX type is Base58\nCOUNT type is u64"),
) )
.arg( .arg(
Arg::with_name("ends_with") Arg::new("ends_with")
.long("ends-with") .long("ends-with")
.value_name("SUFFIX:COUNT") .value_name("SUFFIX:COUNT")
.number_of_values(1) .number_of_values(1)
.takes_value(true) .takes_value(true)
.multiple(true) .multiple_occurrences(true)
.validator(grind_validator_ends_with) .multiple_values(true)
.validator(|s| grind_validator_ends_with(s.to_string()))
.help("Saves specified number of keypairs whos public key ends with the indicated suffix\nExample: --ends-with ana:4\nSUFFIX type is Base58\nCOUNT type is u64"), .help("Saves specified number of keypairs whos public key ends with the indicated suffix\nExample: --ends-with ana:4\nSUFFIX type is Base58\nCOUNT type is u64"),
) )
.arg( .arg(
Arg::with_name("starts_and_ends_with") Arg::new("starts_and_ends_with")
.long("starts-and-ends-with") .long("starts-and-ends-with")
.value_name("PREFIX:SUFFIX:COUNT") .value_name("PREFIX:SUFFIX:COUNT")
.number_of_values(1) .number_of_values(1)
.takes_value(true) .takes_value(true)
.multiple(true) .multiple_occurrences(true)
.validator(grind_validator_starts_and_ends_with) .multiple_values(true)
.validator(|s| grind_validator_starts_and_ends_with(s.to_string()))
.help("Saves specified number of keypairs whos public key starts and ends with the indicated perfix and suffix\nExample: --starts-and-ends-with sol:ana:4\nPREFIX and SUFFIX type is Base58\nCOUNT type is u64"), .help("Saves specified number of keypairs whos public key starts and ends with the indicated perfix and suffix\nExample: --starts-and-ends-with sol:ana:4\nPREFIX and SUFFIX type is Base58\nCOUNT type is u64"),
) )
.arg( .arg(
Arg::with_name("num_threads") Arg::new("num_threads")
.long("num-threads") .long("num-threads")
.value_name("NUMBER") .value_name("NUMBER")
.takes_value(true) .takes_value(true)
.validator(is_parsable::<usize>) .validator(|s| is_parsable::<usize>(s.to_string()))
.default_value(&default_num_threads) .default_value(&default_num_threads)
.help("Specify the number of grind threads"), .help("Specify the number of grind threads"),
) )
.arg( .arg(
Arg::with_name("use_mnemonic") Arg::new("use_mnemonic")
.long("use-mnemonic") .long("use-mnemonic")
.help("Generate using a mnemonic key phrase. Expect a significant slowdown in this mode"), .help("Generate using a mnemonic key phrase. Expect a significant slowdown in this mode"),
) )
@@ -457,64 +458,64 @@ fn main() -> Result<(), Box<dyn error::Error>> {
) )
) )
.subcommand( .subcommand(
SubCommand::with_name("pubkey") Command::new("pubkey")
.about("Display the pubkey from a keypair file") .about("Display the pubkey from a keypair file")
.setting(AppSettings::DisableVersion) .disable_version_flag(true)
.arg( .arg(
Arg::with_name("keypair") Arg::new("keypair")
.index(1) .index(1)
.value_name("KEYPAIR") .value_name("KEYPAIR")
.takes_value(true) .takes_value(true)
.help("Filepath or URL to a keypair"), .help("Filepath or URL to a keypair"),
) )
.arg( .arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name) Arg::new(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long) .long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help), .help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
) )
.arg( .arg(
Arg::with_name("outfile") Arg::new("outfile")
.short("o") .short('o')
.long("outfile") .long("outfile")
.value_name("FILEPATH") .value_name("FILEPATH")
.takes_value(true) .takes_value(true)
.help("Path to generated file"), .help("Path to generated file"),
) )
.arg( .arg(
Arg::with_name("force") Arg::new("force")
.short("f") .short('f')
.long("force") .long("force")
.help("Overwrite the output file if it exists"), .help("Overwrite the output file if it exists"),
) )
) )
.subcommand( .subcommand(
SubCommand::with_name("recover") Command::new("recover")
.about("Recover keypair from seed phrase and optional BIP39 passphrase") .about("Recover keypair from seed phrase and optional BIP39 passphrase")
.setting(AppSettings::DisableVersion) .disable_version_flag(true)
.arg( .arg(
Arg::with_name("prompt_signer") Arg::new("prompt_signer")
.index(1) .index(1)
.value_name("KEYPAIR") .value_name("KEYPAIR")
.takes_value(true) .takes_value(true)
.validator(is_prompt_signer_source) .validator(|s| is_prompt_signer_source(s))
.help("`prompt:` URI scheme or `ASK` keyword"), .help("`prompt:` URI scheme or `ASK` keyword"),
) )
.arg( .arg(
Arg::with_name("outfile") Arg::new("outfile")
.short("o") .short('o')
.long("outfile") .long("outfile")
.value_name("FILEPATH") .value_name("FILEPATH")
.takes_value(true) .takes_value(true)
.help("Path to generated file"), .help("Path to generated file"),
) )
.arg( .arg(
Arg::with_name("force") Arg::new("force")
.short("f") .short('f')
.long("force") .long("force")
.help("Overwrite the output file if it exists"), .help("Overwrite the output file if it exists"),
) )
.arg( .arg(
Arg::with_name(SKIP_SEED_PHRASE_VALIDATION_ARG.name) Arg::new(SKIP_SEED_PHRASE_VALIDATION_ARG.name)
.long(SKIP_SEED_PHRASE_VALIDATION_ARG.long) .long(SKIP_SEED_PHRASE_VALIDATION_ARG.long)
.help(SKIP_SEED_PHRASE_VALIDATION_ARG.help), .help(SKIP_SEED_PHRASE_VALIDATION_ARG.help),
), ),
@@ -525,7 +526,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into()) do_main(&matches).map_err(|err| DisplayError::new_as_boxed(err).into())
} }
fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> { fn do_main(matches: &ArgMatches) -> Result<(), Box<dyn error::Error>> {
let config = if let Some(config_file) = matches.value_of("config_file") { let config = if let Some(config_file) = matches.value_of("config_file") {
Config::load(config_file).unwrap_or_default() Config::load(config_file).unwrap_or_default()
} else { } else {
@@ -535,7 +536,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
let mut wallet_manager = None; let mut wallet_manager = None;
match matches.subcommand() { match matches.subcommand() {
("pubkey", Some(matches)) => { Some(("pubkey", matches)) => {
let pubkey = let pubkey =
get_keypair_from_matches(matches, config, &mut wallet_manager)?.try_pubkey()?; get_keypair_from_matches(matches, config, &mut wallet_manager)?.try_pubkey()?;
@@ -547,7 +548,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
println!("{}", pubkey); println!("{}", pubkey);
} }
} }
("new", Some(matches)) => { Some(("new", matches)) => {
let mut path = dirs_next::home_dir().expect("home directory"); let mut path = dirs_next::home_dir().expect("home directory");
let outfile = if matches.is_present("outfile") { let outfile = if matches.is_present("outfile") {
matches.value_of("outfile") matches.value_of("outfile")
@@ -564,7 +565,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
None => (), None => (),
} }
let word_count = value_t!(matches.value_of(WORD_COUNT_ARG.name), usize).unwrap(); let word_count: usize = matches.value_of_t(WORD_COUNT_ARG.name).unwrap();
let mnemonic_type = MnemonicType::for_word_count(word_count)?; let mnemonic_type = MnemonicType::for_word_count(word_count)?;
let language = acquire_language(matches); let language = acquire_language(matches);
@@ -592,7 +593,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
); );
} }
} }
("recover", Some(matches)) => { Some(("recover", matches)) => {
let mut path = dirs_next::home_dir().expect("home directory"); let mut path = dirs_next::home_dir().expect("home directory");
let outfile = if matches.is_present("outfile") { let outfile = if matches.is_present("outfile") {
matches.value_of("outfile").unwrap() matches.value_of("outfile").unwrap()
@@ -614,11 +615,12 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
}; };
output_keypair(&keypair, outfile, "recovered")?; output_keypair(&keypair, outfile, "recovered")?;
} }
("grind", Some(matches)) => { Some(("grind", matches)) => {
let ignore_case = matches.is_present("ignore_case"); let ignore_case = matches.is_present("ignore_case");
let starts_with_args = if matches.is_present("starts_with") { let starts_with_args = if matches.is_present("starts_with") {
values_t_or_exit!(matches, "starts_with", String) matches
.values_of_t_or_exit::<String>("starts_with")
.into_iter() .into_iter()
.map(|s| if ignore_case { s.to_lowercase() } else { s }) .map(|s| if ignore_case { s.to_lowercase() } else { s })
.collect() .collect()
@@ -626,7 +628,8 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
HashSet::new() HashSet::new()
}; };
let ends_with_args = if matches.is_present("ends_with") { let ends_with_args = if matches.is_present("ends_with") {
values_t_or_exit!(matches, "ends_with", String) matches
.values_of_t_or_exit::<String>("ends_with")
.into_iter() .into_iter()
.map(|s| if ignore_case { s.to_lowercase() } else { s }) .map(|s| if ignore_case { s.to_lowercase() } else { s })
.collect() .collect()
@@ -634,7 +637,8 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
HashSet::new() HashSet::new()
}; };
let starts_and_ends_with_args = if matches.is_present("starts_and_ends_with") { let starts_and_ends_with_args = if matches.is_present("starts_and_ends_with") {
values_t_or_exit!(matches, "starts_and_ends_with", String) matches
.values_of_t_or_exit::<String>("starts_and_ends_with")
.into_iter() .into_iter()
.map(|s| if ignore_case { s.to_lowercase() } else { s }) .map(|s| if ignore_case { s.to_lowercase() } else { s })
.collect() .collect()
@@ -652,7 +656,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
exit(1); exit(1);
} }
let num_threads = value_t_or_exit!(matches.value_of("num_threads"), usize); let num_threads: usize = matches.value_of_t_or_exit("num_threads");
let grind_matches = grind_parse_args( let grind_matches = grind_parse_args(
ignore_case, ignore_case,
@@ -664,7 +668,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
let use_mnemonic = matches.is_present("use_mnemonic"); let use_mnemonic = matches.is_present("use_mnemonic");
let word_count = value_t!(matches.value_of(WORD_COUNT_ARG.name), usize).unwrap(); let word_count: usize = matches.value_of_t(WORD_COUNT_ARG.name).unwrap();
let mnemonic_type = MnemonicType::for_word_count(word_count)?; let mnemonic_type = MnemonicType::for_word_count(word_count)?;
let language = acquire_language(matches); let language = acquire_language(matches);
@@ -766,7 +770,7 @@ fn do_main(matches: &ArgMatches<'_>) -> Result<(), Box<dyn error::Error>> {
thread_handle.join().unwrap(); thread_handle.join().unwrap();
} }
} }
("verify", Some(matches)) => { Some(("verify", matches)) => {
let keypair = get_keypair_from_matches(matches, config, &mut wallet_manager)?; let keypair = get_keypair_from_matches(matches, config, &mut wallet_manager)?;
let simple_message = Message::new( let simple_message = Message::new(
&[Instruction::new_with_bincode( &[Instruction::new_with_bincode(