remote-wallet: Plumb `Locator` into `RemoteWalletInfo`

This commit is contained in:
Trent Nelson 2021-04-24 01:57:55 -06:00 committed by Trent Nelson
parent 64fcb792c2
commit 3d12be29ec
3 changed files with 36 additions and 76 deletions

View File

@ -1,7 +1,9 @@
use { use {
crate::{ crate::{
ledger_error::LedgerError, ledger_error::LedgerError,
remote_wallet::{RemoteWallet, RemoteWalletError, RemoteWalletInfo, RemoteWalletManager}, remote_wallet::{
Manufacturer, RemoteWallet, RemoteWalletError, RemoteWalletInfo, RemoteWalletManager,
},
}, },
console::Emoji, console::Emoji,
dialoguer::{theme::ColorfulTheme, Select}, dialoguer::{theme::ColorfulTheme, Select},
@ -9,7 +11,7 @@ use {
num_traits::FromPrimitive, num_traits::FromPrimitive,
semver::Version as FirmwareVersion, semver::Version as FirmwareVersion,
solana_sdk::{derivation_path::DerivationPath, pubkey::Pubkey, signature::Signature}, solana_sdk::{derivation_path::DerivationPath, pubkey::Pubkey, signature::Signature},
std::{cmp::min, fmt, sync::Arc}, std::{cmp::min, convert::TryFrom, fmt, sync::Arc},
}; };
static CHECK_MARK: Emoji = Emoji("", ""); static CHECK_MARK: Emoji = Emoji("", "");
@ -363,9 +365,8 @@ impl RemoteWallet for LedgerWallet {
) -> Result<RemoteWalletInfo, RemoteWalletError> { ) -> Result<RemoteWalletInfo, RemoteWalletError> {
let manufacturer = dev_info let manufacturer = dev_info
.manufacturer_string() .manufacturer_string()
.unwrap_or("Unknown") .and_then(|s| Manufacturer::try_from(s).ok())
.to_lowercase() .unwrap_or_default();
.replace(" ", "-");
let model = dev_info let model = dev_info
.product_string() .product_string()
.unwrap_or("Unknown") .unwrap_or("Unknown")

View File

@ -2,7 +2,7 @@ use {
crate::{ crate::{
ledger::get_ledger_from_info, ledger::get_ledger_from_info,
remote_wallet::{ remote_wallet::{
RemoteWallet, RemoteWalletError, RemoteWalletInfo, RemoteWalletManager, Manufacturer, RemoteWallet, RemoteWalletError, RemoteWalletInfo, RemoteWalletManager,
RemoteWalletType, RemoteWalletType,
}, },
}, },
@ -61,7 +61,7 @@ pub fn generate_remote_keypair(
keypair_name: &str, keypair_name: &str,
) -> Result<RemoteKeypair, RemoteWalletError> { ) -> Result<RemoteKeypair, RemoteWalletError> {
let (remote_wallet_info, derivation_path) = RemoteWalletInfo::parse_path(path)?; let (remote_wallet_info, derivation_path) = RemoteWalletInfo::parse_path(path)?;
if remote_wallet_info.manufacturer == "ledger" { if remote_wallet_info.manufacturer == Manufacturer::Ledger {
let ledger = get_ledger_from_info(remote_wallet_info, keypair_name, wallet_manager)?; let ledger = get_ledger_from_info(remote_wallet_info, keypair_name, wallet_manager)?;
let path = format!("{}{}", ledger.pretty_path, derivation_path.get_query()); let path = format!("{}{}", ledger.pretty_path, derivation_path.get_query());
Ok(RemoteKeypair::new( Ok(RemoteKeypair::new(

View File

@ -58,6 +58,9 @@ pub enum RemoteWalletError {
#[error("remote wallet operation rejected by the user")] #[error("remote wallet operation rejected by the user")]
UserCancel, UserCancel,
#[error(transparent)]
LocatorError(#[from] LocatorError),
} }
impl From<hidapi::HidError> for RemoteWalletError { impl From<hidapi::HidError> for RemoteWalletError {
@ -240,7 +243,7 @@ pub struct RemoteWalletInfo {
/// RemoteWallet device model /// RemoteWallet device model
pub model: String, pub model: String,
/// RemoteWallet device manufacturer /// RemoteWallet device manufacturer
pub manufacturer: String, pub manufacturer: Manufacturer,
/// RemoteWallet device serial number /// RemoteWallet device serial number
pub serial: String, pub serial: String,
/// RemoteWallet host device path /// RemoteWallet host device path
@ -444,63 +447,19 @@ impl Locator {
impl RemoteWalletInfo { impl RemoteWalletInfo {
pub fn parse_path(path: String) -> Result<(Self, DerivationPath), RemoteWalletError> { pub fn parse_path(path: String) -> Result<(Self, DerivationPath), RemoteWalletError> {
let wallet_path = Url::parse(&path).map_err(|e| { let Locator {
Into::<RemoteWalletError>::into(DerivationPathError::InvalidDerivationPath(format!( manufacturer,
"parse error: {:?}", pubkey,
e derivation_path,
))) } = Locator::new_from_path(path)?;
})?; Ok((
RemoteWalletInfo {
if wallet_path.host_str().is_none() { manufacturer,
return Err(DerivationPathError::InvalidDerivationPath( pubkey: pubkey.unwrap_or_default(),
"missing remote wallet type".to_string(),
)
.into());
}
let mut wallet_info = RemoteWalletInfo {
manufacturer: wallet_path.host_str().unwrap().to_string(),
..RemoteWalletInfo::default() ..RemoteWalletInfo::default()
}; },
derivation_path.unwrap_or_default(),
if let Some(wallet_id) = wallet_path.path_segments().map(|c| c.collect::<Vec<_>>()) {
if !wallet_id[0].is_empty() {
wallet_info.pubkey = Pubkey::from_str(wallet_id[0]).map_err(|e| {
Into::<RemoteWalletError>::into(DerivationPathError::InvalidDerivationPath(
format!("pubkey from_str error: {:?}", e),
)) ))
})?;
}
}
let mut derivation_path = DerivationPath::default();
let mut query_pairs = wallet_path.query_pairs();
if query_pairs.count() > 0 {
for _ in 0..query_pairs.count() {
if let Some(mut pair) = query_pairs.next() {
if pair.0 == "key" {
let key_path = pair.1.to_mut();
if key_path.ends_with('/') {
key_path.pop();
}
derivation_path = DerivationPath::from_key_str(key_path)?;
} else {
return Err(DerivationPathError::InvalidDerivationPath(format!(
"invalid query string `{}={}`, only `key` supported",
pair.0, pair.1
))
.into());
}
}
if query_pairs.next().is_some() {
return Err(DerivationPathError::InvalidDerivationPath(
"invalid query string, extra fields not supported".to_string(),
)
.into());
}
}
}
Ok((wallet_info, derivation_path))
} }
pub fn get_pretty_path(&self) -> String { pub fn get_pretty_path(&self) -> String {
@ -548,7 +507,7 @@ mod tests {
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/2", pubkey)).unwrap(); RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/2", pubkey)).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Manufacturer::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey, pubkey,
@ -559,7 +518,7 @@ mod tests {
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1'/2'", pubkey)).unwrap(); RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1'/2'", pubkey)).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Manufacturer::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey, pubkey,
@ -570,7 +529,7 @@ mod tests {
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1\'/2\'", pubkey)).unwrap(); RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1\'/2\'", pubkey)).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Manufacturer::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey, pubkey,
@ -581,7 +540,7 @@ mod tests {
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/2/", pubkey)).unwrap(); RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/2/", pubkey)).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Vendor::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey, pubkey,
@ -592,7 +551,7 @@ mod tests {
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/", pubkey)).unwrap(); RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/", pubkey)).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Vendor::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey, pubkey,
@ -605,7 +564,7 @@ mod tests {
RemoteWalletInfo::parse_path("usb://ledger?key=1".to_string()).unwrap(); RemoteWalletInfo::parse_path("usb://ledger?key=1".to_string()).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Manufacturer::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey: Pubkey::default(), pubkey: Pubkey::default(),
@ -616,7 +575,7 @@ mod tests {
RemoteWalletInfo::parse_path("usb://ledger/?key=1/2".to_string()).unwrap(); RemoteWalletInfo::parse_path("usb://ledger/?key=1/2".to_string()).unwrap();
assert!(wallet_info.matches(&RemoteWalletInfo { assert!(wallet_info.matches(&RemoteWalletInfo {
model: "".to_string(), model: "".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Manufacturer::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey: Pubkey::default(), pubkey: Pubkey::default(),
@ -644,7 +603,7 @@ mod tests {
fn test_remote_wallet_info_matches() { fn test_remote_wallet_info_matches() {
let pubkey = solana_sdk::pubkey::new_rand(); let pubkey = solana_sdk::pubkey::new_rand();
let info = RemoteWalletInfo { let info = RemoteWalletInfo {
manufacturer: "Ledger".to_string(), manufacturer: Manufacturer::Ledger,
model: "Nano S".to_string(), model: "Nano S".to_string(),
serial: "0001".to_string(), serial: "0001".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
@ -652,11 +611,11 @@ mod tests {
error: None, error: None,
}; };
let mut test_info = RemoteWalletInfo { let mut test_info = RemoteWalletInfo {
manufacturer: "Not Ledger".to_string(), manufacturer: Manufacturer::Unknown,
..RemoteWalletInfo::default() ..RemoteWalletInfo::default()
}; };
assert!(!info.matches(&test_info)); assert!(!info.matches(&test_info));
test_info.manufacturer = "Ledger".to_string(); test_info.manufacturer = Manufacturer::Ledger;
assert!(info.matches(&test_info)); assert!(info.matches(&test_info));
test_info.model = "Other".to_string(); test_info.model = "Other".to_string();
assert!(info.matches(&test_info)); assert!(info.matches(&test_info));
@ -677,7 +636,7 @@ mod tests {
let pubkey_str = pubkey.to_string(); let pubkey_str = pubkey.to_string();
let remote_wallet_info = RemoteWalletInfo { let remote_wallet_info = RemoteWalletInfo {
model: "nano-s".to_string(), model: "nano-s".to_string(),
manufacturer: "ledger".to_string(), manufacturer: Manufacturer::Ledger,
serial: "".to_string(), serial: "".to_string(),
host_device_path: "/host/device/path".to_string(), host_device_path: "/host/device/path".to_string(),
pubkey, pubkey,