-
Notifications
You must be signed in to change notification settings - Fork 1k
Refactor remote wallet #8423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Refactor remote wallet #8423
Changes from all commits
2a6faca
f543731
bac740b
8a17b1e
d20f4c8
94f4f70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,144 @@ | ||||||||||||||||||||||
| Solana Remote Wallet | ||||||||||||||||||||||
| === | ||||||||||||||||||||||
| # Solana Remote Wallet | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Library for interacting with "remote" wallets, meaning any wallet where the private key bytes are not directly available, | ||||||||||||||||||||||
| such as Ledger devices. | ||||||||||||||||||||||
| A Rust library for interacting with hardware wallets in the Solana ecosystem. This library provides a unified interface for discovering, connecting to, and performing operations with various hardware wallet devices. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Ledger udev-rules | ||||||||||||||||||||||
| ## Features | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| In order to use a Ledger device on Linux machines, users must apply certain udev rules. These are available at the | ||||||||||||||||||||||
| [udev-rules repository](https://github.com/LedgerHQ/udev-rules) maintained by the Ledger team. | ||||||||||||||||||||||
| - **Multi-Wallet Support**: Currently supports Ledger hardware wallets, with extensible architecture for additional wallet types | ||||||||||||||||||||||
| - **USB HID Communication**: Secure communication with hardware wallets via USB HID protocol | ||||||||||||||||||||||
| - **Device Discovery**: Automatic detection and connection to connected hardware wallets | ||||||||||||||||||||||
| - **Transaction Signing**: Sign Solana transactions and messages using hardware wallet private keys | ||||||||||||||||||||||
| - **Public Key Derivation**: Derive Solana public keys from hardware wallet derivation paths | ||||||||||||||||||||||
| - **Cross-Platform**: Works on Linux, macOS, and Windows | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure about that? Windows support doesn't always work 😅 |
||||||||||||||||||||||
| - **Thread-Safe**: Concurrent access to wallet manager with proper synchronization | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Supported Hardware Wallets | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Ledger | ||||||||||||||||||||||
| - **Protocol**: USB HID with APDU commands | ||||||||||||||||||||||
| - **Features**: Transaction signing, message signing, public key derivation | ||||||||||||||||||||||
| - **Ledger udev-rules**: In order to use a Ledger device on Linux machines, users must apply certain udev rules. These are available at the [udev-rules repository](https://github.com/LedgerHQ/udev-rules) maintained by the Ledger team. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Keystone (Planned/In Development) | ||||||||||||||||||||||
| - Support for Keystone hardware wallets is being added | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Architecture | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| The library is organized into several key modules: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
| remote-wallet/ | ||||||||||||||||||||||
| ├── src/ | ||||||||||||||||||||||
| │ ├── lib.rs # Main library entry point | ||||||||||||||||||||||
| │ ├── errors.rs # Error types and conversions | ||||||||||||||||||||||
| │ ├── remote_wallet.rs # Core wallet manager and traits | ||||||||||||||||||||||
| │ ├── remote_keypair.rs # Remote keypair implementation | ||||||||||||||||||||||
| │ ├── locator.rs # Device locator and URI parsing | ||||||||||||||||||||||
| │ ├── wallet/ # Wallet implementations | ||||||||||||||||||||||
| │ │ ├── mod.rs # Wallet module definitions | ||||||||||||||||||||||
| │ │ ├── types.rs # Common wallet types | ||||||||||||||||||||||
| │ │ ├── errors.rs # Wallet-specific errors | ||||||||||||||||||||||
| │ │ └── ledger/ # Ledger wallet implementation | ||||||||||||||||||||||
| │ │ ├── mod.rs # Ledger module | ||||||||||||||||||||||
| │ │ ├── ledger.rs # Ledger wallet logic | ||||||||||||||||||||||
| │ │ └── error.rs # Ledger-specific errors | ||||||||||||||||||||||
| │ └── transport/ # Communication layer | ||||||||||||||||||||||
| │ ├── mod.rs # Transport module | ||||||||||||||||||||||
| │ ├── transport_trait.rs # Transport trait definition | ||||||||||||||||||||||
| │ ├── hid_transport.rs # HID transport implementation | ||||||||||||||||||||||
| │ └── common.rs # Common transport utilities | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
Comment on lines
+25
to
+50
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's remove this since it definitely won't be kept in sync with the code |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Core Components | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #### RemoteWalletManager | ||||||||||||||||||||||
| The main entry point for hardware wallet operations. It manages: | ||||||||||||||||||||||
| - Device discovery and connection | ||||||||||||||||||||||
| - Device lifecycle management | ||||||||||||||||||||||
| - Concurrent access to multiple devices | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #### RemoteWallet Trait | ||||||||||||||||||||||
| Defines the interface that all hardware wallet implementations must provide: | ||||||||||||||||||||||
| - Device initialization and information reading | ||||||||||||||||||||||
| - Public key derivation | ||||||||||||||||||||||
| - Transaction and message signing | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #### WalletProbe Trait | ||||||||||||||||||||||
| Handles device discovery and connection for specific wallet types: | ||||||||||||||||||||||
| - Device identification | ||||||||||||||||||||||
| - Connection establishment | ||||||||||||||||||||||
| - Device validation | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #### Transport Layer | ||||||||||||||||||||||
| Abstracts the communication protocol: | ||||||||||||||||||||||
| - USB HID transport for Ledger devices | ||||||||||||||||||||||
| - Extensible for other communication methods | ||||||||||||||||||||||
|
Comment on lines
+66
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's remove these since they probably won't go in |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Device Locators | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Hardware wallets are identified using URI-style locators: | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
| usb://ledger | ||||||||||||||||||||||
| usb://ledger?key=0 | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Security Considerations | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| - **Private Keys**: Private keys never leave the hardware wallet | ||||||||||||||||||||||
| - **User Confirmation**: Critical operations require physical confirmation on the device | ||||||||||||||||||||||
| - **Secure Communication**: All communication uses encrypted USB HID protocol | ||||||||||||||||||||||
| - **Input Validation**: All inputs are validated before being sent to the device | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ## Development | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Building | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||
| # Build the library | ||||||||||||||||||||||
| cargo build | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Build with specific features | ||||||||||||||||||||||
| cargo build --features "hidapi,linux-static-hidraw" | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Run tests | ||||||||||||||||||||||
| cargo test | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Build documentation | ||||||||||||||||||||||
| cargo doc --open | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ### Adding New Wallet Types | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| To add support for a new hardware wallet: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| 1. Create a new module in `src/wallet/` | ||||||||||||||||||||||
| 2. Implement the `RemoteWallet` trait | ||||||||||||||||||||||
| 3. Implement the `WalletProbe` trait | ||||||||||||||||||||||
| 4. Add the wallet type to `RemoteWalletType` enum | ||||||||||||||||||||||
| 5. Register the probe in `create_wallet_probes()` | ||||||||||||||||||||||
| 6. Update error handling and keypair generation | ||||||||||||||||||||||
|
Comment on lines
+114
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's also update this for a simpler flow
Suggested change
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Example structure for a new wallet: | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ```rust | ||||||||||||||||||||||
| // src/wallet/new_wallet/mod.rs | ||||||||||||||||||||||
| pub mod wallet; | ||||||||||||||||||||||
| pub mod probe; | ||||||||||||||||||||||
| pub mod error; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // src/wallet/new_wallet/wallet.rs | ||||||||||||||||||||||
| pub struct NewWallet { | ||||||||||||||||||||||
| // Implementation | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| impl RemoteWallet<hidapi::DeviceInfo> for NewWallet { | ||||||||||||||||||||||
| // Implement required methods | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // src/wallet/new_wallet/probe.rs | ||||||||||||||||||||||
| pub struct NewWalletProbe; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| impl WalletProbe for NewWalletProbe { | ||||||||||||||||||||||
| // Implement device discovery | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| ``` | ||||||||||||||||||||||
|
Comment on lines
+121
to
+144
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can just remove this too |
||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: let's call the file |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| use crate::locator::LocatorError; | ||
| use crate::wallet::ledger::error::LedgerError; | ||
| use solana_derivation_path::DerivationPathError; | ||
| use solana_signer::SignerError; | ||
| use thiserror::Error; | ||
|
|
||
| /// Remote wallet error. | ||
| #[derive(Error, Debug, Clone)] | ||
| pub enum RemoteWalletError { | ||
| #[error("hidapi error")] | ||
| Hid(String), | ||
|
|
||
| #[error("device type mismatch")] | ||
| DeviceTypeMismatch, | ||
|
|
||
| #[error("device with non-supported product ID or vendor ID was detected")] | ||
| InvalidDevice, | ||
|
|
||
| #[error(transparent)] | ||
| DerivationPathError(#[from] DerivationPathError), | ||
|
|
||
| #[error("invalid input: {0}")] | ||
| InvalidInput(String), | ||
|
|
||
| #[error("invalid path: {0}")] | ||
| InvalidPath(String), | ||
|
|
||
| #[error(transparent)] | ||
| LedgerError(#[from] LedgerError), | ||
|
|
||
| #[error("no device found")] | ||
| NoDeviceFound, | ||
|
|
||
| #[error("protocol error: {0}")] | ||
| Protocol(&'static str), | ||
|
|
||
| #[error("pubkey not found for given address")] | ||
| PubkeyNotFound, | ||
|
|
||
| #[error("remote wallet operation rejected by the user")] | ||
| UserCancel, | ||
|
|
||
| #[error(transparent)] | ||
| LocatorError(#[from] LocatorError), | ||
| } | ||
|
|
||
| #[cfg(feature = "hidapi")] | ||
| 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::Connection(hid_error), | ||
| RemoteWalletError::DeviceTypeMismatch => SignerError::Connection(err.to_string()), | ||
| RemoteWalletError::InvalidDevice => SignerError::Connection(err.to_string()), | ||
| RemoteWalletError::InvalidInput(input) => SignerError::InvalidInput(input), | ||
| RemoteWalletError::LedgerError(e) => SignerError::Protocol(e.to_string()), | ||
| RemoteWalletError::NoDeviceFound => SignerError::NoDeviceFound, | ||
| RemoteWalletError::Protocol(e) => SignerError::Protocol(e.to_string()), | ||
| RemoteWalletError::UserCancel => { | ||
| SignerError::UserCancel("remote wallet operation rejected by the user".to_string()) | ||
| } | ||
| _ => SignerError::Custom(err.to_string()), | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,9 @@ | ||
| #![allow(clippy::arithmetic_side_effects)] | ||
| #![allow(dead_code)] | ||
| pub mod ledger; | ||
| pub mod ledger_error; | ||
|
|
||
| pub mod errors; | ||
| pub mod locator; | ||
| pub mod remote_keypair; | ||
| pub mod remote_wallet; | ||
| pub mod transport; | ||
| pub mod wallet; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does also support Trezor now!