Skip to content

Commit

Permalink
Cli api adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyera Eulberg committed Feb 7, 2020
1 parent 2ebad45 commit e06f755
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 63 deletions.
7 changes: 6 additions & 1 deletion Cargo.lock

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

6 changes: 3 additions & 3 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
let config = Config::load(config_file).unwrap_or_default();
if let Some(field) = subcommand_matches.value_of("specific_setting") {
let (field_name, value, default_value) = match field {
"url" => ("RPC Url", config.url, CliConfig::default_json_rpc_url()),
"url" => ("RPC URL", config.url, CliConfig::default_json_rpc_url()),
"keypair" => (
"Key Path",
config.keypair_path,
Expand All @@ -38,12 +38,12 @@ fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error
} else {
println_name_value("Config File:", config_file);
println_name_value_or(
"RPC Url:",
"RPC URL:",
&config.url,
&CliConfig::default_json_rpc_url(),
);
println_name_value_or(
"Key Path:",
"Keypair Path:",
&config.keypair_path,
&CliConfig::default_keypair_path(),
);
Expand Down
2 changes: 1 addition & 1 deletion remote-wallet/src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ pub fn get_ledger_from_info(
let devices = wallet_manager.list_devices();
let (pubkeys, device_paths): (Vec<Pubkey>, Vec<String>) = devices
.iter()
.filter(|&device_info| device_info == &info)
.filter(|&device_info| device_info.matches(&info))
.map(|device_info| (device_info.pubkey, device_info.get_pretty_path()))
.unzip();
if pubkeys.is_empty() {
Expand Down
170 changes: 112 additions & 58 deletions remote-wallet/src/remote_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub enum RemoteWalletError {
#[error("device with non-supported product ID or vendor ID was detected")]
InvalidDevice,

#[error("invalid derivation path: {0}")]
InvalidDerivationPath(String),

#[error("invalid path: {0}")]
InvalidPath(String),

Expand Down Expand Up @@ -187,7 +190,10 @@ pub struct RemoteWalletInfo {
impl RemoteWalletInfo {
pub fn parse_path(mut path: String) -> Result<(Self, DerivationPath), RemoteWalletError> {
if is_remote_wallet_path(&path).is_ok() {
let path = path.split_off(6);
let mut path = path.split_off(6);
if path.ends_with('/') {
path.pop();
}
let mut parts = path.split('/');
let mut wallet_info = RemoteWalletInfo::default();
let manufacturer = parts.next().unwrap();
Expand All @@ -197,17 +203,34 @@ impl RemoteWalletInfo {
.next()
.and_then(|pubkey_str| Pubkey::from_str(pubkey_str).ok())
.unwrap_or_default();
let derivation_path = parts
.next()
.map(|account| {
let account = account.parse::<u16>().unwrap();
let change = parts.next().and_then(|change| change.parse::<u16>().ok());
DerivationPath { account, change }
})
.unwrap_or(DerivationPath {
account: 0,
change: None,
});

let mut derivation_path = DerivationPath::default();
if let Some(purpose) = parts.next() {
if purpose.replace("'", "") != "44" {
return Err(RemoteWalletError::InvalidDerivationPath(format!(
"Incorrect purpose number, found: {}, must be 44",
purpose
)));
}
if let Some(coin) = parts.next() {
if coin.replace("'", "") != "501" {
return Err(RemoteWalletError::InvalidDerivationPath(format!(
"Incorrect coin number, found: {}, must be 501",
coin
)));
}
if let Some(account) = parts.next() {
derivation_path.account = account.replace("'", "").parse::<u16>().unwrap();
derivation_path.change = parts
.next()
.and_then(|change| change.replace("'", "").parse::<u16>().ok());
}
} else {
return Err(RemoteWalletError::InvalidDerivationPath(
"Derivation path too short, missing coin number".to_string(),
));
}
}
Ok((wallet_info, derivation_path))
} else {
Err(RemoteWalletError::InvalidPath(path))
Expand All @@ -220,10 +243,8 @@ impl RemoteWalletInfo {
self.manufacturer, self.model, self.pubkey,
)
}
}

impl PartialEq for RemoteWalletInfo {
fn eq(&self, other: &Self) -> bool {
pub(crate) fn matches(&self, other: &Self) -> bool {
self.manufacturer == other.manufacturer
&& (self.model == other.model || self.model == "" || other.model == "")
&& (self.pubkey == other.pubkey
Expand All @@ -234,7 +255,11 @@ impl PartialEq for RemoteWalletInfo {

pub fn is_remote_wallet_path(value: &str) -> Result<(), String> {
if value.starts_with("usb://") {
let (_, path) = value.split_at(6);
let (_, mut path) = value.split_at(6);
if path.ends_with('/') {
path = &path[..path.len() - 1];
}
let path = path.replace("'", "");
let mut parts = path.split('/');
let manufacturer = parts.next().unwrap();
if manufacturer != "" {
Expand All @@ -246,26 +271,16 @@ pub fn is_remote_wallet_path(value: &str) -> Result<(), String> {
pubkey_str, e
)
})?;
if let Some(account) = parts.next() {
let _account = account
for derivation_str in parts {
let no_ticks = derivation_str.replace("'", "");
let _derivation_number = no_ticks
.parse::<u16>()
.map_err(|e| {
format!(
"Unable to parse account in remote wallet path, provided: {}, err: {:?}",
account, e
"Unable to parse number in remote wallet path, provided: {}, err: {:?}",
derivation_str, e
)
})?;

if let Some(change) = parts.next() {
let _change = change
.parse::<u16>()
.map_err(|e| {
format!(
"Unable to parse change in remote wallet path, provided: {}, err: {:?}",
account, e
)
})?;
}
}
}
}
Expand Down Expand Up @@ -313,25 +328,55 @@ mod tests {
#[test]
fn test_parse_path() {
let pubkey = Pubkey::new_rand();
let (wallet_info, derivation_path) =
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}/44/501/1/2", pubkey))
.unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(),
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
}));
assert_eq!(
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}/1/2", pubkey)).unwrap(),
(
RemoteWalletInfo {
model: "nano-s".to_string(),
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
},
DerivationPath {
account: 1,
change: Some(2),
}
)
)
derivation_path,
DerivationPath {
account: 1,
change: Some(2),
}
);
let (wallet_info, derivation_path) = RemoteWalletInfo::parse_path(format!(
"usb://ledger/nano-s/{:?}/44'/501'/1'/2'",
pubkey
))
.unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(),
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
}));
assert_eq!(
derivation_path,
DerivationPath {
account: 1,
change: Some(2),
}
);

assert!(RemoteWalletInfo::parse_path(format!(
"usb://ledger/nano-s/{:?}/43/501/1/2",
pubkey
))
.is_err());
assert!(RemoteWalletInfo::parse_path(format!(
"usb://ledger/nano-s/{:?}/44/500/1/2",
pubkey
))
.is_err());
}

#[test]
fn test_remote_wallet_info_partial_eq() {
fn test_remote_wallet_info_matches() {
let pubkey = Pubkey::new_rand();
let info = RemoteWalletInfo {
manufacturer: "Ledger".to_string(),
Expand All @@ -341,18 +386,18 @@ mod tests {
};
let mut test_info = RemoteWalletInfo::default();
test_info.manufacturer = "Not Ledger".to_string();
assert_ne!(info, test_info);
assert!(!info.matches(&test_info));
test_info.manufacturer = "Ledger".to_string();
assert_eq!(info, test_info);
assert!(info.matches(&test_info));
test_info.model = "Other".to_string();
assert_ne!(info, test_info);
assert!(!info.matches(&test_info));
test_info.model = "Nano S".to_string();
assert_eq!(info, test_info);
assert!(info.matches(&test_info));
let another_pubkey = Pubkey::new_rand();
test_info.pubkey = another_pubkey;
assert_ne!(info, test_info);
assert!(!info.matches(&test_info));
test_info.pubkey = pubkey;
assert_eq!(info, test_info);
assert!(info.matches(&test_info));
}

#[test]
Expand All @@ -368,18 +413,27 @@ mod tests {
Ok(())
);
assert_eq!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/1", pubkey)),
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44/501", pubkey)),
Ok(())
);
assert!(is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/a", pubkey)).is_err());
assert!(is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/65537", pubkey)).is_err());
assert!(is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44", pubkey)).is_err());
assert!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44/501/a", pubkey)).is_err()
);
assert!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44/501/65537", pubkey))
.is_err()
);
assert_eq!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/1/1", pubkey)),
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44/501/1/1", pubkey)),
Ok(())
);
assert!(is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/1/b", pubkey)).is_err());
assert!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/1/65537", pubkey)).is_err()
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44/501/1/b", pubkey)).is_err()
);
assert!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/44/501/1/65537", pubkey))
.is_err()
);
}
}

0 comments on commit e06f755

Please sign in to comment.