1use ble_transport::BleError;
2use thiserror::Error;
3use trezor_connect::thp::{BackendError, ThpWorkflowError};
4
5#[derive(Debug, Clone, Copy, Eq, PartialEq)]
6pub enum WalletErrorKind {
7 Ble,
8 Workflow,
9 Device,
10 Validation,
11 Timeout,
12}
13
14#[derive(Debug, Error)]
15pub enum WalletError {
16 #[error("invalid BIP32 path: {0}")]
17 InvalidBip32Path(String),
18 #[error(
19 "BLE peer removed pairing information: remove device from OS Bluetooth settings and pair again"
20 )]
21 PeerRemovedPairingInfo,
22 #[error("BLE error: {0}")]
23 Ble(#[from] BleError),
24 #[error("workflow error: {0}")]
25 Workflow(#[from] ThpWorkflowError),
26 #[error("signing error: {0}")]
27 Signing(String),
28 #[error(
29 "solana serialized tx is too short ({len} bytes, minimum {min}); provide full serialized transaction bytes"
30 )]
31 SolanaTxTooShort { len: usize, min: usize },
32 #[error("Solana transaction version {0} is not supported by firmware")]
33 UnsupportedSolanaTxVersion(u8),
34}
35
36pub type WalletResult<T> = std::result::Result<T, WalletError>;
37
38impl WalletError {
39 pub fn kind(&self) -> WalletErrorKind {
40 match self {
41 Self::InvalidBip32Path(_)
42 | Self::Signing(_)
43 | Self::SolanaTxTooShort { .. }
44 | Self::UnsupportedSolanaTxVersion(_) => WalletErrorKind::Validation,
45 Self::PeerRemovedPairingInfo => WalletErrorKind::Device,
46 Self::Ble(error) => error.into(),
47 Self::Workflow(error) => error.into(),
48 }
49 }
50}
51
52impl From<&BleError> for WalletErrorKind {
53 fn from(error: &BleError) -> Self {
54 match error {
55 BleError::Timeout(_) => Self::Timeout,
56 BleError::Btleplug(_)
57 | BleError::AdapterUnavailable
58 | BleError::NotificationStreamClosed
59 | BleError::MissingCharacteristic { .. } => Self::Ble,
60 }
61 }
62}
63
64impl From<&ThpWorkflowError> for WalletErrorKind {
65 fn from(error: &ThpWorkflowError) -> Self {
66 match error {
67 ThpWorkflowError::Backend(error) => error.into(),
68 ThpWorkflowError::InvalidPhase
69 | ThpWorkflowError::MissingHandshake
70 | ThpWorkflowError::MissingHandshakeCredentials
71 | ThpWorkflowError::AlreadyPaired
72 | ThpWorkflowError::NoCommonPairingMethod
73 | ThpWorkflowError::PairingAborted
74 | ThpWorkflowError::PairingInteractionRequired
75 | ThpWorkflowError::PairingController(_)
76 | ThpWorkflowError::Storage(_) => Self::Workflow,
77 }
78 }
79}
80
81impl From<&BackendError> for WalletErrorKind {
82 fn from(error: &BackendError) -> Self {
83 match error {
84 BackendError::TransportTimeout => Self::Timeout,
85 BackendError::Device(_)
86 | BackendError::DeviceBusy
87 | BackendError::DeviceLocked
88 | BackendError::PinExpected
89 | BackendError::DeviceFirmwareError
90 | BackendError::SessionConfirmationRequired
91 | BackendError::DeviceError { .. } => Self::Device,
92 BackendError::UnsupportedPairingMethod => Self::Validation,
93 BackendError::Transport(_) | BackendError::TransportBusy => Self::Workflow,
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use std::time::Duration;
102
103 #[test]
104 fn ble_timeout_is_classified_as_timeout() {
105 let error = WalletError::Ble(BleError::Timeout(Duration::from_secs(1)));
106 assert_eq!(error.kind(), WalletErrorKind::Timeout);
107 assert_eq!(
108 WalletError::Ble(BleError::AdapterUnavailable).kind(),
109 WalletErrorKind::Ble
110 );
111 }
112}