Skip to content

Commit

Permalink
Cli api adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
CriesofCarrots committed Feb 7, 2020
1 parent 2ebad45 commit 8e31707
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 128 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
219 changes: 96 additions & 123 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 @@ -186,32 +189,48 @@ 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 parts = path.split('/');
let mut wallet_info = RemoteWalletInfo::default();
let manufacturer = parts.next().unwrap();
wallet_info.manufacturer = manufacturer.to_string();
wallet_info.model = parts.next().unwrap_or("").to_string();
wallet_info.pubkey = parts
.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,
});
Ok((wallet_info, derivation_path))
} else {
Err(RemoteWalletError::InvalidPath(path))
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();
wallet_info.manufacturer = manufacturer.to_string();
wallet_info.model = parts.next().unwrap_or("").to_string();
wallet_info.pubkey = parts
.next()
.and_then(|pubkey_str| Pubkey::from_str(pubkey_str).ok())
.unwrap_or_default();

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))
}

pub fn get_pretty_path(&self) -> String {
Expand All @@ -220,10 +239,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 @@ -232,52 +249,6 @@ 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 parts = path.split('/');
let manufacturer = parts.next().unwrap();
if manufacturer != "" {
if let Some(_model) = parts.next() {
if let Some(pubkey_str) = parts.next() {
let _pubkey = Pubkey::from_str(pubkey_str).map_err(|e| {
format!(
"Unable to parse pubkey in remote wallet path, provided: {}, err: {:?}",
pubkey_str, e
)
})?;
if let Some(account) = parts.next() {
let _account = account
.parse::<u16>()
.map_err(|e| {
format!(
"Unable to parse account in remote wallet path, provided: {}, err: {:?}",
account, 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
)
})?;
}
}
}
}
return Ok(());
}
}
Err(format!(
"Unable to parse input as remote wallet path, provided: {}",
value
))
}

#[derive(Default, PartialEq, Clone)]
pub struct DerivationPath {
pub account: u16,
Expand Down Expand Up @@ -313,25 +284,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,45 +342,17 @@ 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);
}

#[test]
fn test_is_remote_wallet_path() {
assert!(is_remote_wallet_path("usb://").is_err());
assert_eq!(is_remote_wallet_path("usb://ledger"), Ok(()));
assert_eq!(is_remote_wallet_path("usb://ledger/nano-s"), Ok(()));
assert!(is_remote_wallet_path("usb://ledger/nano-s/notpubkey").is_err());

let pubkey = Pubkey::new_rand();
assert_eq!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}", pubkey)),
Ok(())
);
assert_eq!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/1", 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_eq!(
is_remote_wallet_path(&format!("usb://ledger/nano-s/{:?}/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()
);
assert!(info.matches(&test_info));
}
}

0 comments on commit 8e31707

Please sign in to comment.