cli: Confirm recovered pubkeys (#7316)
* cli: Confirm recovered pubkeys * cargo clippy
This commit is contained in:
parent
b7d4330dd4
commit
186709ed75
|
@ -36,7 +36,7 @@ pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
|
||||||
if let Some(value) = matches.value_of(name) {
|
if let Some(value) = matches.value_of(name) {
|
||||||
if value == ASK_KEYWORD {
|
if value == ASK_KEYWORD {
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
keypair_from_seed_phrase(name, skip_validation).ok()
|
keypair_from_seed_phrase(name, skip_validation, true).ok()
|
||||||
} else {
|
} else {
|
||||||
read_keypair_file(value).ok()
|
read_keypair_file(value).ok()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,18 @@ use crate::ArgConstant;
|
||||||
use bip39::{Language, Mnemonic, Seed};
|
use bip39::{Language, Mnemonic, Seed};
|
||||||
use clap::values_t;
|
use clap::values_t;
|
||||||
use rpassword::prompt_password_stderr;
|
use rpassword::prompt_password_stderr;
|
||||||
use solana_sdk::signature::{
|
use solana_sdk::{
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::{
|
||||||
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
|
keypair_from_seed, keypair_from_seed_phrase_and_passphrase, read_keypair_file, Keypair,
|
||||||
KeypairUtil,
|
KeypairUtil,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
error,
|
||||||
|
io::{stdin, stdout, Write},
|
||||||
|
process::exit,
|
||||||
};
|
};
|
||||||
use std::error;
|
|
||||||
|
|
||||||
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
// Keyword used to indicate that the user should be asked for a keypair seed phrase
|
||||||
pub const ASK_KEYWORD: &str = "ASK";
|
pub const ASK_KEYWORD: &str = "ASK";
|
||||||
|
@ -54,9 +61,12 @@ pub fn prompt_passphrase(prompt: &str) -> Result<String, Box<dyn error::Error>>
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads user input from stdin to retrieve a seed phrase and passphrase for keypair derivation
|
/// Reads user input from stdin to retrieve a seed phrase and passphrase for keypair derivation
|
||||||
|
/// Optionally skips validation of seed phrase
|
||||||
|
/// Optionally confirms recovered public key
|
||||||
pub fn keypair_from_seed_phrase(
|
pub fn keypair_from_seed_phrase(
|
||||||
keypair_name: &str,
|
keypair_name: &str,
|
||||||
skip_validation: bool,
|
skip_validation: bool,
|
||||||
|
confirm_pubkey: bool,
|
||||||
) -> Result<Keypair, Box<dyn error::Error>> {
|
) -> Result<Keypair, Box<dyn error::Error>> {
|
||||||
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
let seed_phrase = prompt_password_stderr(&format!("[{}] seed phrase: ", keypair_name))?;
|
||||||
let seed_phrase = seed_phrase.trim();
|
let seed_phrase = seed_phrase.trim();
|
||||||
|
@ -65,18 +75,32 @@ pub fn keypair_from_seed_phrase(
|
||||||
keypair_name,
|
keypair_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
if skip_validation {
|
let keypair = if skip_validation {
|
||||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||||
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)
|
keypair_from_seed_phrase_and_passphrase(&seed_phrase, &passphrase)?
|
||||||
} else {
|
} else {
|
||||||
let sanitized = sanitize_seed_phrase(seed_phrase);
|
let sanitized = sanitize_seed_phrase(seed_phrase);
|
||||||
let mnemonic = Mnemonic::from_phrase(sanitized, Language::English)?;
|
let mnemonic = Mnemonic::from_phrase(sanitized, Language::English)?;
|
||||||
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
let passphrase = prompt_passphrase(&passphrase_prompt)?;
|
||||||
let seed = Seed::new(&mnemonic, &passphrase);
|
let seed = Seed::new(&mnemonic, &passphrase);
|
||||||
keypair_from_seed(seed.as_bytes())
|
keypair_from_seed(seed.as_bytes())?
|
||||||
|
};
|
||||||
|
|
||||||
|
if confirm_pubkey {
|
||||||
|
let pubkey = Pubkey::new(keypair.public.as_ref());
|
||||||
|
print!("Recovered pubkey `{:?}`. Continue? (y/n): ", pubkey);
|
||||||
|
let _ignored = stdout().flush();
|
||||||
|
let mut input = String::new();
|
||||||
|
stdin().read_line(&mut input).expect("Unexpected input");
|
||||||
|
if input.to_lowercase().trim() != "y" {
|
||||||
|
println!("Exiting");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(keypair)
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks CLI arguments to determine whether a keypair should be:
|
/// Checks CLI arguments to determine whether a keypair should be:
|
||||||
/// - inputted securely via stdin,
|
/// - inputted securely via stdin,
|
||||||
/// - read in from a file,
|
/// - read in from a file,
|
||||||
|
@ -104,7 +128,7 @@ pub fn keypair_input(
|
||||||
}
|
}
|
||||||
|
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
keypair_from_seed_phrase(keypair_name, skip_validation)
|
keypair_from_seed_phrase(keypair_name, skip_validation, true)
|
||||||
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
|
.map(|keypair| KeypairWithSource::new(keypair, Source::SeedPhrase))
|
||||||
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
|
} else if let Some(keypair_file) = matches.value_of(keypair_match_name) {
|
||||||
read_keypair_file(keypair_file).map(|keypair| KeypairWithSource::new(keypair, Source::File))
|
read_keypair_file(keypair_file).map(|keypair| KeypairWithSource::new(keypair, Source::File))
|
||||||
|
|
|
@ -52,7 +52,7 @@ fn get_keypair_from_matches(matches: &ArgMatches) -> Result<Keypair, Box<dyn err
|
||||||
read_keypair(&mut stdin)
|
read_keypair(&mut stdin)
|
||||||
} else if infile == ASK_KEYWORD {
|
} else if infile == ASK_KEYWORD {
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
keypair_from_seed_phrase("pubkey recovery", skip_validation)
|
keypair_from_seed_phrase("pubkey recovery", skip_validation, false)
|
||||||
} else {
|
} else {
|
||||||
read_keypair_file(infile)
|
read_keypair_file(infile)
|
||||||
}
|
}
|
||||||
|
@ -306,7 +306,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
|
||||||
let keypair = keypair_from_seed_phrase("recover", skip_validation)?;
|
let keypair = keypair_from_seed_phrase("recover", skip_validation, true)?;
|
||||||
output_keypair(&keypair, &outfile, "recovered")?;
|
output_keypair(&keypair, &outfile, "recovered")?;
|
||||||
}
|
}
|
||||||
("grind", Some(matches)) => {
|
("grind", Some(matches)) => {
|
||||||
|
|
Loading…
Reference in New Issue