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:
parent
39b3ce9bd3
commit
ca8bf8f964
|
@ -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())
|
||||
|
|
|
@ -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,12 +267,17 @@ impl RemoteWallet for LedgerWallet {
|
|||
.serial_number
|
||||
.clone()
|
||||
.unwrap_or_else(|| "Unknown".to_owned());
|
||||
self.get_pubkey(&DerivationPath::default(), false)
|
||||
.map(|pubkey| RemoteWalletInfo {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue