Ledger: return specific error if ledger-app-solana is not running (#8684)

* Specific error if ledger-app-solana is not running

* Return helpful error

* Include signer name in multiple-device prompt
This commit is contained in:
Tyera Eulberg 2020-03-06 16:03:23 -07:00 committed by GitHub
parent 39b3ce9bd3
commit ca8bf8f964
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 17 deletions

View File

@ -87,6 +87,7 @@ pub fn signer_from_path(
derivation_of(matches, "derivation_path"),
wallet_manager,
matches.is_present("confirm_key"),
keypair_name,
)?))
} else {
Err(RemoteWalletError::NoDeviceFound.into())

View File

@ -190,7 +190,9 @@ impl LedgerWallet {
#[allow(clippy::match_overlapping_arm)]
match status {
// These need to be aligned with solana Ledger app error codes, and clippy allowance removed
0x6700 => Err(RemoteWalletError::Protocol("Incorrect length")),
0x6700 => Err(RemoteWalletError::Protocol(
"Solana app not open on Ledger device",
)),
0x6802 => Err(RemoteWalletError::Protocol("Invalid parameter")),
0x6803 => Err(RemoteWalletError::Protocol(
"Overflow: message longer than MAX_MESSAGE_LENGTH",
@ -265,13 +267,18 @@ impl RemoteWallet for LedgerWallet {
.serial_number
.clone()
.unwrap_or_else(|| "Unknown".to_owned());
self.get_pubkey(&DerivationPath::default(), false)
.map(|pubkey| RemoteWalletInfo {
model,
manufacturer,
serial,
pubkey,
})
let pubkey_result = self.get_pubkey(&DerivationPath::default(), false);
let (pubkey, error) = match pubkey_result {
Ok(pubkey) => (pubkey, None),
Err(err) => (Pubkey::default(), Some(err)),
};
Ok(RemoteWalletInfo {
model,
manufacturer,
serial,
pubkey,
error,
})
}
fn get_pubkey(
@ -395,12 +402,24 @@ fn extend_and_serialize(derivation_path: &DerivationPath) -> Vec<u8> {
/// Choose a Ledger wallet based on matching info fields
pub fn get_ledger_from_info(
info: RemoteWalletInfo,
keypair_name: &str,
wallet_manager: &RemoteWalletManager,
) -> Result<Arc<LedgerWallet>, RemoteWalletError> {
let devices = wallet_manager.list_devices();
let (pubkeys, device_paths): (Vec<Pubkey>, Vec<String>) = devices
let mut matches = devices
.iter()
.filter(|&device_info| device_info.matches(&info))
.filter(|&device_info| device_info.matches(&info));
if matches
.clone()
.all(|device_info| device_info.error.is_some())
{
let first_device = matches.next();
if let Some(device) = first_device {
return Err(device.error.clone().unwrap());
}
}
let (pubkeys, device_paths): (Vec<Pubkey>, Vec<String>) = matches
.filter(|&device_info| device_info.error.is_none())
.map(|device_info| (device_info.pubkey, device_info.get_pretty_path()))
.unzip();
if pubkeys.is_empty() {
@ -408,7 +427,10 @@ pub fn get_ledger_from_info(
}
let wallet_base_pubkey = if pubkeys.len() > 1 {
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Multiple hardware wallets found. Please select a device")
.with_prompt(&format!(
"Multiple hardware wallets found. Please select a device for {:?}",
keypair_name
))
.default(0)
.items(&device_paths[..])
.interact()

View File

@ -53,13 +53,14 @@ pub fn generate_remote_keypair(
explicit_derivation_path: Option<DerivationPath>,
wallet_manager: &RemoteWalletManager,
confirm_key: bool,
keypair_name: &str,
) -> Result<RemoteKeypair, RemoteWalletError> {
let (remote_wallet_info, mut derivation_path) = RemoteWalletInfo::parse_path(path)?;
if let Some(derivation) = explicit_derivation_path {
derivation_path = derivation;
}
if remote_wallet_info.manufacturer == "ledger" {
let ledger = get_ledger_from_info(remote_wallet_info, wallet_manager)?;
let ledger = get_ledger_from_info(remote_wallet_info, keypair_name, wallet_manager)?;
Ok(RemoteKeypair::new(
RemoteWalletType::Ledger(ledger),
derivation_path,

View File

@ -18,10 +18,10 @@ const HID_GLOBAL_USAGE_PAGE: u16 = 0xFF00;
const HID_USB_DEVICE_CLASS: u8 = 0;
/// Remote wallet error.
#[derive(Error, Debug)]
#[derive(Error, Debug, Clone)]
pub enum RemoteWalletError {
#[error("hidapi error")]
Hid(#[from] hidapi::HidError),
Hid(String),
#[error("device type mismatch")]
DeviceTypeMismatch,
@ -51,12 +51,16 @@ pub enum RemoteWalletError {
UserCancel,
}
impl From<hidapi::HidError> for RemoteWalletError {
fn from(err: hidapi::HidError) -> RemoteWalletError {
RemoteWalletError::Hid(err.to_string())
}
}
impl From<RemoteWalletError> for SignerError {
fn from(err: RemoteWalletError) -> SignerError {
match err {
RemoteWalletError::Hid(hid_error) => {
SignerError::ConnectionError(hid_error.to_string())
}
RemoteWalletError::Hid(hid_error) => SignerError::ConnectionError(hid_error),
RemoteWalletError::DeviceTypeMismatch => SignerError::ConnectionError(err.to_string()),
RemoteWalletError::InvalidDevice => SignerError::ConnectionError(err.to_string()),
RemoteWalletError::InvalidInput(input) => SignerError::InvalidInput(input),
@ -215,6 +219,8 @@ pub struct RemoteWalletInfo {
pub serial: String,
/// Base pubkey of device at Solana derivation path
pub pubkey: Pubkey,
/// Initial read error
pub error: Option<RemoteWalletError>,
}
impl RemoteWalletInfo {
@ -360,6 +366,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
error: None,
}));
assert_eq!(
derivation_path,
@ -376,6 +383,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
error: None,
}));
assert_eq!(
derivation_path,
@ -392,6 +400,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
error: None,
}));
assert_eq!(
derivation_path,
@ -408,6 +417,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
error: None,
}));
assert_eq!(
derivation_path,
@ -424,6 +434,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey,
error: None,
}));
assert_eq!(
derivation_path,
@ -441,6 +452,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey: Pubkey::default(),
error: None,
}));
assert_eq!(
derivation_path,
@ -456,6 +468,7 @@ mod tests {
manufacturer: "ledger".to_string(),
serial: "".to_string(),
pubkey: Pubkey::default(),
error: None,
}));
assert_eq!(
derivation_path,
@ -490,6 +503,7 @@ mod tests {
model: "Nano S".to_string(),
serial: "0001".to_string(),
pubkey: pubkey.clone(),
error: None,
};
let mut test_info = RemoteWalletInfo::default();
test_info.manufacturer = "Not Ledger".to_string();