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

Commit

Permalink
Wrap SignerSource to share derivation_path
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyera Eulberg committed Apr 29, 2021
1 parent baae352 commit fe3ba60
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 48 deletions.
9 changes: 6 additions & 3 deletions clap-utils/src/input_validators.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
crate::keypair::{parse_signer_source, SignerSource, ASK_KEYWORD},
crate::keypair::{parse_signer_source, SignerSourceKind, ASK_KEYWORD},
chrono::DateTime,
solana_sdk::{
clock::{Epoch, Slot},
Expand Down Expand Up @@ -110,8 +110,11 @@ pub fn is_valid_pubkey<T>(string: T) -> Result<(), String>
where
T: AsRef<str> + Display,
{
match parse_signer_source(string.as_ref()).map_err(|err| format!("{}", err))? {
SignerSource::Filepath(path) => is_keypair(path),
match parse_signer_source(string.as_ref())
.map_err(|err| format!("{}", err))?
.kind
{
SignerSourceKind::Filepath(path) => is_keypair(path),
_ => Ok(()),
}
}
Expand Down
153 changes: 108 additions & 45 deletions clap-utils/src/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,24 @@ impl DefaultSigner {
}
}

pub(crate) enum SignerSource {
pub(crate) struct SignerSource {
pub kind: SignerSourceKind,
pub derivation_path: Option<DerivationPath>,
}

impl SignerSource {
fn new(kind: SignerSourceKind) -> Self {
Self {
kind,
derivation_path: None,
}
}
}

pub(crate) enum SignerSourceKind {
Ask,
Filepath(String),
Usb(RemoteWalletLocator, Option<DerivationPath>),
Usb(RemoteWalletLocator),
Stdin,
Pubkey(Pubkey),
}
Expand All @@ -165,22 +179,26 @@ pub(crate) fn parse_signer_source<S: AsRef<str>>(
if let Some(scheme) = uri.scheme() {
let scheme = scheme.as_str().to_ascii_lowercase();
match scheme.as_str() {
"ask" => Ok(SignerSource::Ask),
"file" => Ok(SignerSource::Filepath(uri.path().to_string())),
"stdin" => Ok(SignerSource::Stdin),
"usb" => Ok(SignerSource::Usb(
RemoteWalletLocator::new_from_uri(&uri)?,
DerivationPath::from_uri(&uri)?,
)),
"ask" => Ok(SignerSource::new(SignerSourceKind::Ask)),
"file" => Ok(SignerSource::new(SignerSourceKind::Filepath(
uri.path().to_string(),
))),
"stdin" => Ok(SignerSource::new(SignerSourceKind::Stdin)),
"usb" => Ok(SignerSource {
kind: SignerSourceKind::Usb(RemoteWalletLocator::new_from_uri(&uri)?),
derivation_path: DerivationPath::from_uri(&uri)?,
}),
_ => Err(SignerSourceError::UnrecognizedSource),
}
} else {
match source {
"-" => Ok(SignerSource::Stdin),
ASK_KEYWORD => Ok(SignerSource::Ask),
"-" => Ok(SignerSource::new(SignerSourceKind::Stdin)),
ASK_KEYWORD => Ok(SignerSource::new(SignerSourceKind::Ask)),
_ => match Pubkey::from_str(source) {
Ok(pubkey) => Ok(SignerSource::Pubkey(pubkey)),
Err(_) => Ok(SignerSource::Filepath(source.to_string())),
Ok(pubkey) => Ok(SignerSource::new(SignerSourceKind::Pubkey(pubkey))),
Err(_) => Ok(SignerSource::new(SignerSourceKind::Filepath(
source.to_string(),
))),
},
}
}
Expand Down Expand Up @@ -231,28 +249,32 @@ 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_signer_source(path)? {
SignerSource::Ask => {
let SignerSource {
kind,
derivation_path,
} = parse_signer_source(path)?;
match kind {
SignerSourceKind::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,
)?))
}
SignerSource::Filepath(path) => match read_keypair_file(&path) {
SignerSourceKind::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)),
},
SignerSource::Stdin => {
SignerSourceKind::Stdin => {
let mut stdin = std::io::stdin();
Ok(Box::new(read_keypair(&mut stdin)?))
}
SignerSource::Usb(locator, derivation_path) => {
SignerSourceKind::Usb(locator) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
Expand All @@ -268,7 +290,7 @@ pub fn signer_from_path_with_config(
Err(RemoteWalletError::NoDeviceFound.into())
}
}
SignerSource::Pubkey(pubkey) => {
SignerSourceKind::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, SIGNER_ARG.name)
.as_ref()
.and_then(|presigners| presigner_from_pubkey_sigs(&pubkey, presigners));
Expand All @@ -293,8 +315,9 @@ pub fn pubkey_from_path(
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Pubkey, Box<dyn error::Error>> {
match parse_signer_source(path)? {
SignerSource::Pubkey(pubkey) => Ok(pubkey),
let SignerSource { kind, .. } = parse_signer_source(path)?;
match kind {
SignerSourceKind::Pubkey(pubkey) => Ok(pubkey),
_ => Ok(signer_from_path(matches, path, keypair_name, wallet_manager)?.pubkey()),
}
}
Expand All @@ -305,28 +328,32 @@ pub fn resolve_signer_from_path(
keypair_name: &str,
wallet_manager: &mut Option<Arc<RemoteWalletManager>>,
) -> Result<Option<String>, Box<dyn error::Error>> {
match parse_signer_source(path)? {
SignerSource::Ask => {
let SignerSource {
kind,
derivation_path,
} = parse_signer_source(path)?;
match kind {
SignerSourceKind::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)
}
SignerSource::Filepath(path) => match read_keypair_file(&path) {
SignerSourceKind::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())),
},
SignerSource::Stdin => {
SignerSourceKind::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)
}
SignerSource::Usb(locator, derivation_path) => {
SignerSourceKind::Usb(locator) => {
if wallet_manager.is_none() {
*wallet_manager = maybe_wallet_manager()?;
}
Expand Down Expand Up @@ -484,56 +511,92 @@ mod tests {
fn test_parse_signer_source() {
assert!(matches!(
parse_signer_source("-").unwrap(),
SignerSource::Stdin
SignerSource {
kind: SignerSourceKind::Stdin,
derivation_path: None,
}
));
let ask = "stdin:".to_string();
assert!(matches!(
parse_signer_source(&ask).unwrap(),
SignerSource::Stdin
SignerSource {
kind: SignerSourceKind::Stdin,
derivation_path: None,
}
));
assert!(matches!(
parse_signer_source(ASK_KEYWORD).unwrap(),
SignerSource::Ask
SignerSource {
kind: SignerSourceKind::Ask,
derivation_path: None,
}
));
let pubkey = Pubkey::new_unique();
assert!(
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource::Pubkey(p) if p == pubkey)
matches!(parse_signer_source(&pubkey.to_string()).unwrap(), SignerSource {
kind: SignerSourceKind::Pubkey(p),
derivation_path: None,
}
if p == pubkey)
);
let path = "/absolute/path".to_string();
assert!(
matches!(parse_signer_source(&path).unwrap(), SignerSource::Filepath(p) if p == path)
);
assert!(matches!(parse_signer_source(&path).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
} if p == path));
let path = "relative/path".to_string();
assert!(
matches!(parse_signer_source(&path).unwrap(), SignerSource::Filepath(p) if p == path)
);
assert!(matches!(parse_signer_source(&path).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
} if p == path));
let usb = "usb://ledger".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
assert!(
matches!(parse_signer_source(&usb).unwrap(), SignerSource::Usb(u, None) if u == expected_locator)
);
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
kind: SignerSourceKind::Usb(u),
derivation_path: None,
} if u == expected_locator));
let usb = "usb://ledger?key=0/0".to_string();
let expected_locator = RemoteWalletLocator {
manufacturer: Manufacturer::Ledger,
pubkey: None,
};
let expected_derivation_path = Some(DerivationPath::new_bip44(Some(0), Some(0)));
assert!(matches!(parse_signer_source(&usb).unwrap(), SignerSource {
kind: SignerSourceKind::Usb(u),
derivation_path: d,
} if u == expected_locator && d == expected_derivation_path));
// Catchall into SignerSource::Filepath
let junk = "sometextthatisnotapubkey".to_string();
assert!(Pubkey::from_str(&junk).is_err());
assert!(
matches!(parse_signer_source(&junk).unwrap(), SignerSource::Filepath(j) if j == junk)
);
assert!(matches!(parse_signer_source(&junk).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(j),
derivation_path: None,
} if j == junk));

let ask = "ask:".to_string();
assert!(matches!(
parse_signer_source(&ask).unwrap(),
SignerSource::Ask
SignerSource {
kind: SignerSourceKind::Ask,
derivation_path: None,
}
));
let path = "/absolute/path".to_string();
assert!(
matches!(parse_signer_source(&format!("file:{}", path)).unwrap(), SignerSource::Filepath(p) if p == path)
matches!(parse_signer_source(&format!("file:{}", path)).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
} if p == path)
);
let path = "relative/path".to_string();
assert!(
matches!(parse_signer_source(&format!("file:{}", path)).unwrap(), SignerSource::Filepath(p) if p == path)
matches!(parse_signer_source(&format!("file:{}", path)).unwrap(), SignerSource {
kind: SignerSourceKind::Filepath(p),
derivation_path: None,
} if p == path)
);
}
}

0 comments on commit fe3ba60

Please sign in to comment.