Add jade hardware wallet

This commit is contained in:
edouardparis 2024-03-08 18:25:07 +01:00
parent b7a72e064d
commit 6b0c93c5c3
6 changed files with 345 additions and 112 deletions

124
gui/Cargo.lock generated
View File

@ -2,27 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "CoreFoundation-sys"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0e9889e6db118d49d88d84728d0e964d973a5680befb5f85f55141beea5c20b"
dependencies = [
"libc",
"mach 0.1.2",
]
[[package]]
name = "IOKit-sys"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99696c398cbaf669d2368076bdb3d627fb0ce51a26899d7c61228c5c0af3bf4a"
dependencies = [
"CoreFoundation-sys",
"libc",
"mach 0.1.2",
]
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
version = "0.2.20" version = "0.2.20"
@ -214,9 +193,9 @@ dependencies = [
[[package]] [[package]]
name = "async-hwi" name = "async-hwi"
version = "0.0.16" version = "0.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "912663643d018301fb5534949e8430515c7cdc9bf453bfefba2dc70dc854dd31" checksum = "647a2513ce55652719759c6416e98de94b19a349dc94e1be4924b0487f73ce9b"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bitbox-api", "bitbox-api",
@ -227,10 +206,17 @@ dependencies = [
"ledger-apdu", "ledger-apdu",
"ledger-transport-hidapi", "ledger-transport-hidapi",
"ledger_bitcoin_client", "ledger_bitcoin_client",
"prost 0.12.2",
"prost-derive 0.12.2",
"regex", "regex",
"reqwest",
"serde",
"serde_bytes",
"serde_cbor",
"serialport", "serialport",
"tokio", "tokio",
"tokio-serial", "tokio-serial",
"zeroize",
] ]
[[package]] [[package]]
@ -1353,7 +1339,7 @@ checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4"
dependencies = [ dependencies = [
"bit_field", "bit_field",
"flume", "flume",
"half", "half 2.2.1",
"lebe", "lebe",
"miniz_oxide", "miniz_oxide",
"rayon-core", "rayon-core",
@ -1853,6 +1839,12 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "half"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403"
[[package]] [[package]]
name = "half" name = "half"
version = "2.2.1" version = "2.2.1"
@ -2132,7 +2124,7 @@ dependencies = [
"bitflags 2.4.2", "bitflags 2.4.2",
"bytemuck", "bytemuck",
"cosmic-text", "cosmic-text",
"half", "half 2.2.1",
"iced_core", "iced_core",
"iced_futures", "iced_futures",
"image", "image",
@ -2337,6 +2329,16 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "io-kit-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b"
dependencies = [
"core-foundation-sys",
"mach2",
]
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.11" version = "1.0.11"
@ -2810,19 +2812,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "mach" name = "mach2"
version = "0.1.2" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd13ee2dd61cc82833ba05ade5a30bb3d63f7ced605ef827063c63078302de9" checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
dependencies = [
"libc",
]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -2957,7 +2950,7 @@ checksum = "20a4c60ca5c9c0e114b3bd66ff4aa5f9b2b175442be51ca6c4365d687a97a2ac"
dependencies = [ dependencies = [
"log", "log",
"mio", "mio",
"nix 0.26.2", "nix",
"serialport", "serialport",
"winapi", "winapi",
] ]
@ -3033,17 +3026,6 @@ dependencies = [
"jni-sys", "jni-sys",
] ]
[[package]]
name = "nix"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
]
[[package]] [[package]]
name = "nix" name = "nix"
version = "0.26.2" version = "0.26.2"
@ -4253,6 +4235,25 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde_bytes"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
dependencies = [
"serde",
]
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half 1.8.3",
"serde",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.186" version = "1.0.186"
@ -4289,18 +4290,20 @@ dependencies = [
[[package]] [[package]]
name = "serialport" name = "serialport"
version = "4.2.0" version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aab92efb5cf60ad310548bc3f16fa6b0d950019cb7ed8ff41968c3d03721cf12" checksum = "8f5a15d0be940df84846264b09b51b10b931fb2f275becb80934e3568a016828"
dependencies = [ dependencies = [
"CoreFoundation-sys", "bitflags 2.4.2",
"IOKit-sys",
"bitflags 1.3.2",
"cfg-if", "cfg-if",
"core-foundation-sys",
"io-kit-sys",
"libudev", "libudev",
"mach 0.3.2", "mach2",
"nix 0.24.3", "nix",
"regex", "regex",
"scopeguard",
"unescaper",
"winapi", "winapi",
] ]
@ -4942,6 +4945,15 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unescaper"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0adf6ad32eb5b3cadff915f7b770faaac8f7ff0476633aa29eb0d9584d889d34"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.13" version = "0.3.13"

View File

@ -15,7 +15,7 @@ path = "src/main.rs"
[dependencies] [dependencies]
async-trait = "0.1" async-trait = "0.1"
async-hwi = "0.0.16" async-hwi = "0.0.17"
liana = { git = "https://github.com/wizardsardine/liana", branch = "master", default-features = false, features = ["nonblocking_shutdown"] } liana = { git = "https://github.com/wizardsardine/liana", branch = "master", default-features = false, features = ["nonblocking_shutdown"] }
liana_ui = { path = "ui" } liana_ui = { path = "ui" }
backtrace = "0.3" backtrace = "0.3"

View File

@ -53,6 +53,9 @@ pub fn hw_list_view(
UnsupportedReason::NotPartOfWallet(fg) => { UnsupportedReason::NotPartOfWallet(fg) => {
hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg) hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg)
} }
UnsupportedReason::WrongNetwork => {
hw::wrong_network_hardware_wallet(&kind.to_string(), version.as_ref())
}
_ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()), _ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()),
}, },
HardwareWallet::Locked { HardwareWallet::Locked {
@ -111,6 +114,9 @@ pub fn hw_list_view_for_registration(
UnsupportedReason::NotPartOfWallet(fg) => { UnsupportedReason::NotPartOfWallet(fg) => {
hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg) hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg)
} }
UnsupportedReason::WrongNetwork => {
hw::wrong_network_hardware_wallet(&kind.to_string(), version.as_ref())
}
_ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()), _ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()),
}, },
HardwareWallet::Locked { HardwareWallet::Locked {
@ -180,6 +186,9 @@ pub fn hw_list_view_verify_address(
UnsupportedReason::NotPartOfWallet(fg) => { UnsupportedReason::NotPartOfWallet(fg) => {
hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg) hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg)
} }
UnsupportedReason::WrongNetwork => {
hw::wrong_network_hardware_wallet(&kind.to_string(), version.as_ref())
}
_ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()), _ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()),
}, },
false, false,

View File

@ -8,7 +8,9 @@ use std::{
use crate::app::{settings, wallet::Wallet}; use crate::app::{settings, wallet::Wallet};
use async_hwi::{ use async_hwi::{
bitbox::{api::runtime, BitBox02, PairingBitbox02}, bitbox::{api::runtime, BitBox02, PairingBitbox02},
coldcard, ledger, specter, DeviceKind, Error as HWIError, Version, HWI, coldcard,
jade::{self, Jade},
ledger, specter, DeviceKind, Error as HWIError, Version, HWI,
}; };
use liana::miniscript::bitcoin::{bip32::Fingerprint, hashes::hex::FromHex, Network}; use liana::miniscript::bitcoin::{bip32::Fingerprint, hashes::hex::FromHex, Network};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -21,6 +23,7 @@ pub enum UnsupportedReason {
}, },
Method(&'static str), Method(&'static str),
NotPartOfWallet(Fingerprint), NotPartOfWallet(Fingerprint),
WrongNetwork,
} }
// Todo drop the Clone, to remove the Mutex on HardwareWallet::Locked // Todo drop the Clone, to remove the Mutex on HardwareWallet::Locked
@ -51,7 +54,8 @@ pub enum HardwareWallet {
} }
pub enum LockedDevice { pub enum LockedDevice {
BitBox02(PairingBitbox02<runtime::TokioRuntime>), BitBox02(Box<PairingBitbox02<runtime::TokioRuntime>>),
Jade(Jade<jade::SerialTransport>),
} }
impl std::fmt::Debug for LockedDevice { impl std::fmt::Debug for LockedDevice {
@ -221,25 +225,47 @@ impl HardwareWallets {
*alias = self.aliases.get(fingerprint).cloned(); *alias = self.aliases.get(fingerprint).cloned();
} }
HardwareWallet::Locked { device, id, .. } => { HardwareWallet::Locked { device, id, .. } => {
if let Some(LockedDevice::BitBox02(bb)) = device.lock().unwrap().take() match device.lock().unwrap().take() {
{ None => {}
let id = id.to_string(); Some(LockedDevice::BitBox02(bb)) => {
let id_cloned = id.clone(); let id = id.to_string();
let network = self.network; let id_cloned = id.clone();
let wallet = self.wallet.clone(); let network = self.network;
cmds.push(Command::perform( let wallet = self.wallet.clone();
async move { cmds.push(Command::perform(
let paired_bb = bb.wait_confirm().await?; async move {
let mut bitbox2 = let paired_bb = bb.wait_confirm().await?;
BitBox02::from(paired_bb).with_network(network); let mut bitbox2 =
let fingerprint = bitbox2.get_master_fingerprint().await?; BitBox02::from(paired_bb).with_network(network);
let mut registered = false; let fingerprint =
if let Some(wallet) = &wallet { bitbox2.get_master_fingerprint().await?;
let desc = wallet.main_descriptor.to_string(); let mut registered = false;
bitbox2 = bitbox2.with_policy(&desc)?; if let Some(wallet) = &wallet {
registered = let desc = wallet.main_descriptor.to_string();
bitbox2.is_policy_registered(&desc).await?; bitbox2 = bitbox2.with_policy(&desc)?;
if wallet.descriptor_keys().contains(&fingerprint) { registered =
bitbox2.is_policy_registered(&desc).await?;
if wallet.descriptor_keys().contains(&fingerprint) {
Ok(HardwareWallet::Supported {
id: id.clone(),
kind: DeviceKind::BitBox02,
fingerprint,
device: bitbox2.into(),
version: None,
registered: Some(registered),
alias: None,
})
} else {
Ok(HardwareWallet::Unsupported {
id: id.clone(),
kind: DeviceKind::BitBox02,
version: None,
reason: UnsupportedReason::NotPartOfWallet(
fingerprint,
),
})
}
} else {
Ok(HardwareWallet::Supported { Ok(HardwareWallet::Supported {
id: id.clone(), id: id.clone(),
kind: DeviceKind::BitBox02, kind: DeviceKind::BitBox02,
@ -249,30 +275,31 @@ impl HardwareWallets {
registered: Some(registered), registered: Some(registered),
alias: None, alias: None,
}) })
} else {
Ok(HardwareWallet::Unsupported {
id: id.clone(),
kind: DeviceKind::BitBox02,
version: None,
reason: UnsupportedReason::NotPartOfWallet(
fingerprint,
),
})
} }
} else { },
Ok(HardwareWallet::Supported { |res| HardwareWalletMessage::Unlocked(id_cloned, res),
id: id.clone(), ));
kind: DeviceKind::BitBox02, }
fingerprint, Some(LockedDevice::Jade(device)) => {
device: bitbox2.into(), let id = id.clone();
version: None, let id_cloned = id.clone();
registered: Some(registered), let network = self.network;
alias: None, let wallet = self.wallet.clone();
}) cmds.push(Command::perform(
} async move {
}, device.auth().await?;
|res| HardwareWalletMessage::Unlocked(id_cloned, res), handle_jade_device(
)); id,
network,
device,
wallet.as_ref().map(|w| w.as_ref()),
None,
)
.await
},
|res| HardwareWalletMessage::Unlocked(id_cloned, res),
));
}
} }
} }
_ => {} _ => {}
@ -286,8 +313,8 @@ impl HardwareWallets {
} }
HardwareWalletMessage::Unlocked(id, res) => { HardwareWalletMessage::Unlocked(id, res) => {
match res { match res {
Err(_) => { Err(e) => {
warn!("Pairing failed with an external device"); warn!("Pairing failed with an external device {}", e);
self.list.retain(|hw| hw.id() != &id); self.list.retain(|hw| hw.id() != &id);
} }
Ok(hw) => { Ok(hw) => {
@ -317,6 +344,7 @@ impl HardwareWallets {
iced::subscription::unfold( iced::subscription::unfold(
format!("refresh-{}", self.network), format!("refresh-{}", self.network),
State { State {
network: self.network,
keys_aliases: self.aliases.clone(), keys_aliases: self.aliases.clone(),
wallet: self.wallet.clone(), wallet: self.wallet.clone(),
connected_supported_hws: Vec::new(), connected_supported_hws: Vec::new(),
@ -329,6 +357,7 @@ impl HardwareWallets {
} }
struct State { struct State {
network: Network,
keys_aliases: HashMap<Fingerprint, String>, keys_aliases: HashMap<Fingerprint, String>,
wallet: Option<Arc<Wallet>>, wallet: Option<Arc<Wallet>>,
connected_supported_hws: Vec<String>, connected_supported_hws: Vec<String>,
@ -406,6 +435,38 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) {
} }
Err(e) => warn!("Error while listing specter wallets: {}", e), Err(e) => warn!("Error while listing specter wallets: {}", e),
} }
match jade::SerialTransport::enumerate_potential_ports() {
Ok(ports) => {
for port in ports {
let id = format!("jade-{}", port);
if state.connected_supported_hws.contains(&id) {
still.push(id);
} else {
let device =
Jade::new(jade::SerialTransport::new(port)).with_network(state.network);
match handle_jade_device(
id,
state.network,
device,
state.wallet.as_ref().map(|w| w.as_ref()),
Some(&state.keys_aliases),
)
.await
{
Ok(hw) => {
hws.push(hw);
}
Err(e) => {
warn!("{:?}", e);
}
}
}
}
}
Err(e) => warn!("Error while listing jade devices: {}", e),
}
match ledger::LedgerSimulator::try_connect().await { match ledger::LedgerSimulator::try_connect().await {
Ok(mut device) => { Ok(mut device) => {
let id = "ledger-simulator".to_string(); let id = "ledger-simulator".to_string();
@ -497,7 +558,9 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) {
id, id,
kind: DeviceKind::BitBox02, kind: DeviceKind::BitBox02,
pairing_code: device.pairing_code().map(|s| s.replace('\n', " ")), pairing_code: device.pairing_code().map(|s| s.replace('\n', " ")),
device: Arc::new(Mutex::new(Some(LockedDevice::BitBox02(device)))), device: Arc::new(Mutex::new(Some(LockedDevice::BitBox02(Box::new(
device,
))))),
}); });
} }
} }
@ -648,6 +711,94 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) {
) )
} }
async fn handle_jade_device(
id: String,
network: Network,
device: Jade<async_hwi::jade::SerialTransport>,
wallet: Option<&Wallet>,
keys_aliases: Option<&HashMap<Fingerprint, String>>,
) -> Result<HardwareWallet, HWIError> {
let info = device.get_info().await?;
let version = async_hwi::parse_version(&info.jade_version).ok();
// Jade may not be setup for the current network
if (network == Network::Bitcoin
&& info.jade_networks != jade::api::JadeNetworks::Main
&& info.jade_networks != jade::api::JadeNetworks::All)
|| (network != Network::Bitcoin && info.jade_networks == jade::api::JadeNetworks::Main)
{
Ok(HardwareWallet::Unsupported {
id,
kind: device.device_kind(),
version,
reason: UnsupportedReason::WrongNetwork,
})
} else {
match info.jade_state {
jade::api::JadeState::Locked
| jade::api::JadeState::Temp
| jade::api::JadeState::Uninit
| jade::api::JadeState::Unsaved => Ok(HardwareWallet::Locked {
id,
kind: DeviceKind::Jade,
pairing_code: None,
device: Arc::new(Mutex::new(Some(LockedDevice::Jade(device)))),
}),
jade::api::JadeState::Ready => {
let kind = device.device_kind();
let version = device.get_version().await.ok();
let fingerprint = match device.get_master_fingerprint().await {
Err(HWIError::NetworkMismatch) => {
return Ok(HardwareWallet::Unsupported {
id: id.clone(),
kind,
version,
reason: UnsupportedReason::WrongNetwork,
});
}
Err(e) => {
return Err(e);
}
Ok(fingerprint) => fingerprint,
};
let alias = keys_aliases.and_then(|aliases| aliases.get(&fingerprint).cloned());
if let Some(wallet) = &wallet {
if wallet.descriptor_keys().contains(&fingerprint) {
let desc = wallet.main_descriptor.to_string();
let device = device.with_wallet(wallet.name.clone());
let registered = device.is_wallet_registered(&wallet.name, &desc).await?;
Ok(HardwareWallet::Supported {
id: id.clone(),
kind,
fingerprint,
device: Arc::new(device),
version,
registered: Some(registered),
alias,
})
} else {
Ok(HardwareWallet::Unsupported {
id: id.clone(),
kind,
version,
reason: UnsupportedReason::NotPartOfWallet(fingerprint),
})
}
} else {
Ok(HardwareWallet::Supported {
id: id.clone(),
kind,
fingerprint,
device: Arc::new(device),
version,
registered: Some(false),
alias,
})
}
}
}
}
}
struct AsRefWrap<'a, T> { struct AsRefWrap<'a, T> {
inner: &'a T, inner: &'a T,
} }

View File

@ -23,7 +23,7 @@ use liana_ui::{
use crate::{ use crate::{
bitcoind::{ConfigField, RpcAuthType, RpcAuthValues, StartInternalBitcoindError}, bitcoind::{ConfigField, RpcAuthType, RpcAuthValues, StartInternalBitcoindError},
hw::{is_compatible_with_tapminiscript, HardwareWallet}, hw::{is_compatible_with_tapminiscript, HardwareWallet, UnsupportedReason},
installer::{ installer::{
message::{self, Message}, message::{self, Message},
prompt, prompt,
@ -614,9 +614,20 @@ pub fn hardware_wallet_xpubs<'a>(
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref()) hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} }
} }
HardwareWallet::Unsupported { version, kind, .. } => { HardwareWallet::Unsupported {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()) version,
} kind,
reason,
..
} => match reason {
UnsupportedReason::NotPartOfWallet(fg) => {
hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg)
}
UnsupportedReason::WrongNetwork => {
hw::wrong_network_hardware_wallet(&kind.to_string(), version.as_ref())
}
_ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()),
},
HardwareWallet::Locked { HardwareWallet::Locked {
kind, pairing_code, .. kind, pairing_code, ..
} => hw::locked_hardware_wallet(kind, pairing_code.as_ref()), } => hw::locked_hardware_wallet(kind, pairing_code.as_ref()),
@ -1745,9 +1756,20 @@ pub fn hw_list_view(
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref()) hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} }
} }
HardwareWallet::Unsupported { version, kind, .. } => { HardwareWallet::Unsupported {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()) version,
} kind,
reason,
..
} => match reason {
UnsupportedReason::NotPartOfWallet(fg) => {
hw::unrelated_hardware_wallet(&kind.to_string(), version.as_ref(), fg)
}
UnsupportedReason::WrongNetwork => {
hw::wrong_network_hardware_wallet(&kind.to_string(), version.as_ref())
}
_ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()),
},
HardwareWallet::Locked { HardwareWallet::Locked {
kind, pairing_code, .. kind, pairing_code, ..
} => hw::locked_hardware_wallet(kind, pairing_code.as_ref()), } => hw::locked_hardware_wallet(kind, pairing_code.as_ref()),

View File

@ -14,7 +14,14 @@ pub fn locked_hardware_wallet<'a, T: 'a, K: Display>(
column(vec![ column(vec![
Row::new() Row::new()
.spacing(5) .spacing(5)
.push(text::p1_bold("Locked, check code:")) .push(text::p1_bold(format!(
"Locked{}",
if pairing_code.is_some() {
", check code:"
} else {
""
}
)))
.push_maybe(pairing_code.map(|a| text::p1_bold(a))) .push_maybe(pairing_code.map(|a| text::p1_bold(a)))
.into(), .into(),
Row::new() Row::new()
@ -277,6 +284,38 @@ pub fn registration_success_hardware_wallet<'a, T: 'a, K: Display, V: Display, F
.padding(10) .padding(10)
} }
pub fn wrong_network_hardware_wallet<'a, T: 'a, K: Display, V: Display>(
kind: K,
version: Option<V>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push(text::p1_bold("Wrong network in the device settings"))
.into(),
Row::new()
.spacing(5)
.push(text::caption(kind.to_string()))
.push_maybe(version.map(|v| text::caption(v.to_string())))
.into(),
])
.width(Length::Fill)
.into(),
tooltip::Tooltip::new(
icon::warning_icon(),
"The wrong bitcoin application is open or the device was initialized with the wrong network",
tooltip::Position::Bottom,
)
.style(theme::Container::Card(theme::Card::Simple))
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn unsupported_hardware_wallet<'a, T: 'a, K: Display, V: Display>( pub fn unsupported_hardware_wallet<'a, T: 'a, K: Display, V: Display>(
kind: K, kind: K,
version: Option<V>, version: Option<V>,