Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
CLI BIP32 prep: KeypairUrl refactor (backport #16592) (#16604)
Browse files Browse the repository at this point in the history
* clap-utils: Rename KeypairUrl to SignerSource

(cherry picked from commit 09dcc9e)

* clap-utils: Reduce SignerSource's visibility

(cherry picked from commit c5ab3ba)

* clap-utils: Use `uriparse` crate to parse `SignerSource`

(cherry picked from commit 5d1ef5d)

# Conflicts:
#	Cargo.lock
#	clap-utils/Cargo.toml

* clap-utils: Add explicit schemes for `ask` and `file` `SignerSource`s

(cherry picked from commit 6444f0e)

Co-authored-by: Trent Nelson <[email protected]>
  • Loading branch information
mergify[bot] and t-nelson authored Apr 16, 2021
1 parent 8beaa43 commit 4790b0a
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 30 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions clap-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ solana-remote-wallet = { path = "../remote-wallet", version = "=1.5.19" }
solana-sdk = { path = "../sdk", version = "=1.5.19" }
thiserror = "1.0.21"
tiny-bip39 = "0.7.0"
uriparse = "0.6.3"
url = "2.1.0"
chrono = "0.4"

Expand Down
6 changes: 3 additions & 3 deletions clap-utils/src/input_validators.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::keypair::{parse_keypair_path, KeypairUrl, ASK_KEYWORD};
use crate::keypair::{parse_signer_source, SignerSource, ASK_KEYWORD};
use chrono::DateTime;
use solana_sdk::{
clock::{Epoch, Slot},
Expand Down Expand Up @@ -108,8 +108,8 @@ pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
match parse_keypair_path(string.as_ref()) {
KeypairUrl::Filepath(path) => is_keypair(path),
match parse_signer_source(string.as_ref()) {
SignerSource::Filepath(path) => is_keypair(path),
_ => Ok(()),
}
}
Expand Down
98 changes: 73 additions & 25 deletions clap-utils/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use solana_sdk::{
},
};
use std::{
convert::TryFrom,
error,
io::{stdin, stdout, Write},
process::exit,
Expand Down Expand Up @@ -133,25 +134,38 @@ impl DefaultSigner {
}
}

pub enum KeypairUrl {
pub(crate) enum SignerSource {
Ask,
Filepath(String),
Usb(String),
Stdin,
Pubkey(Pubkey),
}

pub fn parse_keypair_path(path: &str) -> KeypairUrl {
if path == "-" {
KeypairUrl::Stdin
} else if path == ASK_KEYWORD {
KeypairUrl::Ask
} else if path.starts_with("usb://") {
KeypairUrl::Usb(path.to_string())
} else if let Ok(pubkey) = Pubkey::from_str(path) {
KeypairUrl::Pubkey(pubkey)
} else {
KeypairUrl::Filepath(path.to_string())
pub(crate) fn parse_signer_source<S: AsRef<str>>(source: S) -> SignerSource {
let source = source.as_ref();
match uriparse::URIReference::try_from(source) {
Err(_) => SignerSource::Filepath(source.to_string()),
Ok(uri) => {
if let Some(scheme) = uri.scheme() {
let scheme = scheme.as_str().to_ascii_lowercase();
match scheme.as_str() {
"ask" => SignerSource::Ask,
"file" => SignerSource::Filepath(uri.path().to_string()),
"usb" => SignerSource::Usb(source.to_string()),
_ => SignerSource::Filepath(source.to_string()),
}
} else {
match source {
"-" => SignerSource::Stdin,
ASK_KEYWORD => SignerSource::Ask,
_ => match Pubkey::from_str(source) {
Ok(pubkey) => SignerSource::Pubkey(pubkey),
Err(_) => SignerSource::Filepath(source.to_string()),
},
}
}
}
}
}

Expand Down Expand Up @@ -198,28 +212,28 @@ pub fn signer_from_path_with_config(
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
config: &SignerFromPathConfig,
) -> Result<Box<dyn Signer>, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
match parse_signer_source(path) {
SignerSource::Ask => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
Ok(Box::new(keypair_from_seed_phrase(
keypair_name,
skip_validation,
false,
)?))
}
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
SignerSource::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
)
.into()),
Ok(file) => Ok(Box::new(file)),
},
KeypairUrl::Stdin => {
SignerSource::Stdin => {
let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?))
}
KeypairUrl::Usb(path) => {
SignerSource::Usb(path) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
Expand All @@ -234,7 +248,7 @@ pub fn signer_from_path_with_config(
Err(RemoteWalletError::NoDeviceFound.into())
}
}
KeypairUrl::Pubkey(pubkey) => {
SignerSource::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
.as_ref()
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
Expand All @@ -259,8 +273,8 @@ pub fn pubkey_from_path(
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Pubkey, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Pubkey(pubkey) => Ok(pubkey),
match parse_signer_source(path) {
SignerSource::Pubkey(pubkey) => Ok(pubkey),
_ => Ok(signer_from_path(matches, path, keypair_name, wallet_manager)?.pubkey()),
}
}
Expand All @@ -271,28 +285,28 @@ pub fn resolve_signer_from_path(
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Option<String>, Box<dyn error::Error>> {
match parse_keypair_path(path) {
KeypairUrl::Ask => {
match parse_signer_source(path) {
SignerSource::Ask => {
let skip_validation = matches.is_present(SKIP_SEED_PHRASE_VALIDATION_ARG.name);
// This method validates the seed phrase, but returns `None` because there is no path
// on disk or to a device
keypair_from_seed_phrase(keypair_name, skip_validation, false).map(|_| None)
}
KeypairUrl::Filepath(path) => match read_keypair_file(&path) {
SignerSource::Filepath(path) => match read_keypair_file(&path) {
Err(e) => Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("could not read keypair file \"{}\". Run \"solana-keygen new\" to create a keypair file: {}", path, e),
)
.into()),
Ok(_) => Ok(Some(path.to_string())),
},
KeypairUrl::Stdin => {
SignerSource::Stdin => {
let mut stdin = std::io::stdin();
// This method validates the keypair from stdin, but returns `None` because there is no
// path on disk or to a device
read_keypair(&mut stdin).map(|_| None)
}
KeypairUrl::Usb(path) => {
SignerSource::Usb(path) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
Expand Down Expand Up @@ -443,4 +457,38 @@ mod tests {
];
assert_eq!(signer_pubkeys, expect);
}

#[test]
fn test_parse_signer_source() {
assert!(matches!(parse_signer_source("-"), SignerSource::Stdin));
assert!(matches!(
parse_signer_source(ASK_KEYWORD),
SignerSource::Ask
));
let pubkey = Pubkey::new_unique();
assert!(
matches!(parse_signer_source(&pubkey.to_string()), SignerSource::Pubkey(p) if p == pubkey)
);
let path = "/absolute/path".to_string();
assert!(matches!(parse_signer_source(&path), SignerSource::Filepath(p) if p == path));
let path = "relative/path".to_string();
assert!(matches!(parse_signer_source(&path), SignerSource::Filepath(p) if p == path));
let usb = "usb://ledger".to_string();
assert!(matches!(parse_signer_source(&usb), SignerSource::Usb(u) if u == usb));
// Catchall into SignerSource::Filepath
let junk = "sometextthatisnotapubkey".to_string();
assert!(Pubkey::from_str(&junk).is_err());
assert!(matches!(parse_signer_source(&junk), SignerSource::Filepath(j) if j == junk));

let ask = "ask:".to_string();
assert!(matches!(parse_signer_source(&ask), SignerSource::Ask));
let path = "/absolute/path".to_string();
assert!(
matches!(parse_signer_source(&format!("file:{}", path)), SignerSource::Filepath(p) if p == path)
);
let path = "relative/path".to_string();
assert!(
matches!(parse_signer_source(&format!("file:{}", path)), SignerSource::Filepath(p) if p == path)
);
}
}
4 changes: 2 additions & 2 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2456,7 +2456,7 @@ mod tests {
}
);

// Test ResolveSigner Subcommand, KeypairUrl::Filepath
// Test ResolveSigner Subcommand, SignerSource::Filepath
let test_resolve_signer =
test_commands
.clone()
Expand All @@ -2468,7 +2468,7 @@ mod tests {
signers: vec![],
}
);
// Test ResolveSigner Subcommand, KeypairUrl::Pubkey (Presigner)
// Test ResolveSigner Subcommand, SignerSource::Pubkey (Presigner)
let test_resolve_signer =
test_commands
.clone()
Expand Down
11 changes: 11 additions & 0 deletions programs/bpf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4790b0a

Please sign in to comment.