Remove product string from device keypair URL (#8942)
* Remove product string from device url * Update docs
This commit is contained in:
@ -26,12 +26,9 @@ Solana key on any hardware wallet connected to your computer.
|
|||||||
The URL has the following form, where square brackets denote optional fields:
|
The URL has the following form, where square brackets denote optional fields:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
usb://<MANUFACTURER>[/<PRODUCT>[/<WALLET_KEY>]][?key=<DERIVATION_PATH>]
|
usb://<MANUFACTURER>[/<WALLET_KEY>][?key=<DERIVATION_PATH>]
|
||||||
```
|
```
|
||||||
|
|
||||||
`PRODUCT` is optional and defaults to an auto-detected value. When using a Ledger
|
|
||||||
Nano S, for example, it defaults to "nano-s" without quotes.
|
|
||||||
|
|
||||||
`WALLET_KEY` is used to disambiguate multiple devices. Each device has a unique
|
`WALLET_KEY` is used to disambiguate multiple devices. Each device has a unique
|
||||||
master key and from that key derives a separate unique key per app.
|
master key and from that key derives a separate unique key per app.
|
||||||
|
|
||||||
@ -49,7 +46,7 @@ unnecessary.
|
|||||||
For example, a fully qualified URL for a Ledger device might be:
|
For example, a fully qualified URL for a Ledger device might be:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
usb://ledger/nano-s/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
|
usb://ledger/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Manage Multiple Hardware Wallets
|
## Manage Multiple Hardware Wallets
|
||||||
@ -72,7 +69,7 @@ solana resolve-signer usb://ledger?key=0/0
|
|||||||
You will see output similar to:
|
You will see output similar to:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
usb://ledger/nano-s/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
|
usb://ledger/BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK?key=0/0
|
||||||
```
|
```
|
||||||
|
|
||||||
but where `BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK` is your `WALLET_KEY`.
|
but where `BsNsvfXqQTtJnagwFWdBS7FBXgnsK8VZ5CmuznN85swK` is your `WALLET_KEY`.
|
||||||
|
@ -83,7 +83,7 @@ you want to use. Run the same command again, but this time, with its fully
|
|||||||
qualified URL:
|
qualified URL:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
solana address --keypair usb://ledger/nano-s/<WALLET_KEY>
|
solana address --keypair usb://ledger/<WALLET_KEY>
|
||||||
```
|
```
|
||||||
|
|
||||||
Confirm it prints the same key as when you entered just `usb://ledger`.
|
Confirm it prints the same key as when you entered just `usb://ledger`.
|
||||||
|
@ -239,9 +239,8 @@ impl RemoteWalletInfo {
|
|||||||
wallet_info.manufacturer = wallet_path.host_str().unwrap().to_string();
|
wallet_info.manufacturer = wallet_path.host_str().unwrap().to_string();
|
||||||
|
|
||||||
if let Some(wallet_id) = wallet_path.path_segments().map(|c| c.collect::<Vec<_>>()) {
|
if let Some(wallet_id) = wallet_path.path_segments().map(|c| c.collect::<Vec<_>>()) {
|
||||||
wallet_info.model = wallet_id[0].to_string();
|
if wallet_id[0] != "" {
|
||||||
if wallet_id.len() > 1 {
|
wallet_info.pubkey = Pubkey::from_str(wallet_id[0]).map_err(|e| {
|
||||||
wallet_info.pubkey = Pubkey::from_str(wallet_id[1]).map_err(|e| {
|
|
||||||
RemoteWalletError::InvalidDerivationPath(format!(
|
RemoteWalletError::InvalidDerivationPath(format!(
|
||||||
"pubkey from_str error: {:?}",
|
"pubkey from_str error: {:?}",
|
||||||
e
|
e
|
||||||
@ -292,15 +291,11 @@ impl RemoteWalletInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pretty_path(&self) -> String {
|
pub fn get_pretty_path(&self) -> String {
|
||||||
format!(
|
format!("usb://{}/{:?}", self.manufacturer, self.pubkey,)
|
||||||
"usb://{}/{}/{:?}",
|
|
||||||
self.manufacturer, self.model, self.pubkey,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn matches(&self, other: &Self) -> bool {
|
pub(crate) fn matches(&self, other: &Self) -> bool {
|
||||||
self.manufacturer == other.manufacturer
|
self.manufacturer == other.manufacturer
|
||||||
&& (self.model == other.model || self.model == "" || other.model == "")
|
|
||||||
&& (self.pubkey == other.pubkey
|
&& (self.pubkey == other.pubkey
|
||||||
|| self.pubkey == Pubkey::default()
|
|| self.pubkey == Pubkey::default()
|
||||||
|| other.pubkey == Pubkey::default())
|
|| other.pubkey == Pubkey::default())
|
||||||
@ -373,8 +368,7 @@ mod tests {
|
|||||||
fn test_parse_path() {
|
fn test_parse_path() {
|
||||||
let pubkey = Pubkey::new_rand();
|
let pubkey = Pubkey::new_rand();
|
||||||
let (wallet_info, derivation_path) =
|
let (wallet_info, derivation_path) =
|
||||||
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}?key=1/2", pubkey))
|
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/2", pubkey)).unwrap();
|
||||||
.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: "ledger".to_string(),
|
||||||
@ -390,8 +384,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let (wallet_info, derivation_path) =
|
let (wallet_info, derivation_path) =
|
||||||
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}?key=1'/2'", pubkey))
|
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1'/2'", pubkey)).unwrap();
|
||||||
.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: "ledger".to_string(),
|
||||||
@ -407,8 +400,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let (wallet_info, derivation_path) =
|
let (wallet_info, derivation_path) =
|
||||||
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}?key=1\'/2\'", pubkey))
|
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1\'/2\'", pubkey)).unwrap();
|
||||||
.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: "ledger".to_string(),
|
||||||
@ -424,8 +416,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let (wallet_info, derivation_path) =
|
let (wallet_info, derivation_path) =
|
||||||
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}?key=1/2/", pubkey))
|
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/2/", pubkey)).unwrap();
|
||||||
.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: "ledger".to_string(),
|
||||||
@ -441,8 +432,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
let (wallet_info, derivation_path) =
|
let (wallet_info, derivation_path) =
|
||||||
RemoteWalletInfo::parse_path(format!("usb://ledger/nano-s/{:?}?key=1/", pubkey))
|
RemoteWalletInfo::parse_path(format!("usb://ledger/{:?}?key=1/", pubkey)).unwrap();
|
||||||
.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: "ledger".to_string(),
|
||||||
@ -460,7 +450,7 @@ mod tests {
|
|||||||
|
|
||||||
// Test that wallet id need not be complete for key derivation to work
|
// Test that wallet id need not be complete for key derivation to work
|
||||||
let (wallet_info, derivation_path) =
|
let (wallet_info, derivation_path) =
|
||||||
RemoteWalletInfo::parse_path("usb://ledger/nano-s?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: "ledger".to_string(),
|
||||||
@ -494,8 +484,7 @@ mod tests {
|
|||||||
|
|
||||||
// Failure cases
|
// Failure cases
|
||||||
assert!(
|
assert!(
|
||||||
RemoteWalletInfo::parse_path("usb://ledger/nano-s/bad-pubkey?key=1/2".to_string())
|
RemoteWalletInfo::parse_path("usb://ledger/bad-pubkey?key=1/2".to_string()).is_err()
|
||||||
.is_err()
|
|
||||||
);
|
);
|
||||||
assert!(RemoteWalletInfo::parse_path("usb://?key=1/2".to_string()).is_err());
|
assert!(RemoteWalletInfo::parse_path("usb://?key=1/2".to_string()).is_err());
|
||||||
assert!(RemoteWalletInfo::parse_path("usb:/ledger?key=1/2".to_string()).is_err());
|
assert!(RemoteWalletInfo::parse_path("usb:/ledger?key=1/2".to_string()).is_err());
|
||||||
@ -525,7 +514,7 @@ mod tests {
|
|||||||
test_info.manufacturer = "Ledger".to_string();
|
test_info.manufacturer = "Ledger".to_string();
|
||||||
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));
|
||||||
test_info.model = "Nano S".to_string();
|
test_info.model = "Nano S".to_string();
|
||||||
assert!(info.matches(&test_info));
|
assert!(info.matches(&test_info));
|
||||||
let another_pubkey = Pubkey::new_rand();
|
let another_pubkey = Pubkey::new_rand();
|
||||||
@ -548,7 +537,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
remote_wallet_info.get_pretty_path(),
|
remote_wallet_info.get_pretty_path(),
|
||||||
format!("usb://ledger/nano-s/{}", pubkey_str)
|
format!("usb://ledger/{}", pubkey_str)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user