Change HardwareWallet for (Un)Supported enum

This commit is contained in:
edouard 2023-02-02 17:13:10 +01:00
parent d5baf1bd57
commit 2a9bcf92d0
8 changed files with 276 additions and 81 deletions

8
gui/Cargo.lock generated
View File

@ -145,9 +145,9 @@ dependencies = [
[[package]]
name = "async-hwi"
version = "0.0.2"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b6da8b5e813fa1393db34de31dd4ce826d7603e81867bccd64f5ee216209ee2"
checksum = "5df769ed2ee66f45f290602647bfb48f7c652d6d8bb9dd8eb104fdfb0ac48919"
dependencies = [
"async-trait",
"base64",
@ -1629,9 +1629,9 @@ dependencies = [
[[package]]
name = "ledger_bitcoin_client"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8bb8c131c03c33df548b5c45ccc5c20e3f89aa973b4c8bdd539132af7f48f"
checksum = "9a8f2e27af48417f8786467d4bd7e0e279c3320ab756391329450eedec59eab6"
dependencies = [
"async-trait",
"bitcoin",

View File

@ -14,7 +14,7 @@ name = "liana-gui"
path = "src/main.rs"
[dependencies]
async-hwi = "0.0.2"
async-hwi = "0.0.3"
liana = { git = "https://github.com/wizardsardine/liana", branch = "master", default-features = false }
backtrace = "0.3"
base64 = "0.13"

View File

@ -297,13 +297,17 @@ impl Action for SignAction {
) -> Command<Message> {
match message {
Message::View(view::Message::Spend(view::SpendTxMessage::SelectHardwareWallet(i))) => {
if let Some(hw) = self.hws.get(i) {
let device = hw.device.clone();
if let Some(HardwareWallet::Supported {
fingerprint,
device,
..
}) = self.hws.get(i)
{
self.chosen_hw = Some(i);
self.processing = true;
let psbt = tx.psbt.clone();
return Command::perform(
sign_psbt(device, hw.fingerprint, psbt),
sign_psbt(device.clone(), *fingerprint, psbt),
Message::Signed,
);
}
@ -331,7 +335,11 @@ impl Action for SignAction {
// We add the new hws without dropping the reference of the previous ones.
Message::ConnectedHardwareWallets(hws) => {
for h in hws {
if !self.hws.iter().any(|hw| hw.fingerprint == h.fingerprint) {
if !self
.hws
.iter()
.any(|hw| hw.fingerprint() == hw.fingerprint() && hw.kind() == h.kind())
{
self.hws.push(h);
}
}

View File

@ -1,5 +1,5 @@
use iced::{
widget::{Button, Column, Container, Row},
widget::{tooltip, Button, Column, Container, Row},
Alignment, Element, Length,
};
@ -17,19 +17,49 @@ use crate::{
},
};
pub fn hw_list_view<'a>(
pub fn hw_list_view(
i: usize,
hw: &HardwareWallet,
chosen: bool,
processing: bool,
signed: bool,
) -> Element<'a, Message> {
) -> Element<Message> {
let mut bttn = Button::new(
Row::new()
.push(
Column::new()
.push(text(format!("{}", hw.kind)).bold())
.push(text(format!("fingerprint: {}", hw.fingerprint)).small())
.push(text(format!("{}", hw.kind())).bold())
.push(match hw {
HardwareWallet::Supported {
fingerprint,
version,
..
} => Row::new()
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
),
HardwareWallet::Unsupported {
version, message, ..
} => Row::new()
.spacing(5)
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push(
tooltip::Tooltip::new(
icon::warning_icon(),
message,
tooltip::Position::Bottom,
)
.style(card::SimpleCardStyle),
),
})
.spacing(5)
.width(Length::Fill),
)
@ -60,7 +90,7 @@ pub fn hw_list_view<'a>(
.padding(10)
.style(button::Style::Border.into())
.width(Length::Fill);
if !processing {
if !processing && hw.is_supported() {
bttn = bttn.on_press(Message::Spend(SpendTxMessage::SelectHardwareWallet(i)));
}
Container::new(bttn)

View File

@ -672,7 +672,7 @@ pub fn inputs_and_outputs_view<'a>(
pub fn sign_action<'a>(
warning: Option<&Error>,
hws: &[HardwareWallet],
hws: &'a [HardwareWallet],
processing: bool,
chosen_hw: Option<usize>,
signed: &[Fingerprint],
@ -702,7 +702,9 @@ pub fn sign_action<'a>(
hw,
Some(i) == chosen_hw,
processing,
signed.contains(&hw.fingerprint),
hw.fingerprint()
.map(|f| signed.contains(&f))
.unwrap_or(false),
))
},
))

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use async_hwi::{ledger, specter, DeviceKind, Error as HWIError, HWI};
use async_hwi::{ledger, specter, DeviceKind, Error as HWIError, Version, HWI};
use liana::miniscript::bitcoin::{
hashes::hex::{FromHex, ToHex},
util::bip32::Fingerprint,
@ -9,22 +9,50 @@ use log::debug;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct HardwareWallet {
pub device: Arc<dyn HWI + Send + Sync>,
pub kind: DeviceKind,
pub fingerprint: Fingerprint,
pub enum HardwareWallet {
Unsupported {
kind: DeviceKind,
version: Option<Version>,
message: String,
},
Supported {
device: Arc<dyn HWI + Send + Sync>,
kind: DeviceKind,
fingerprint: Fingerprint,
version: Option<Version>,
},
}
impl HardwareWallet {
async fn new(device: Arc<dyn HWI + Send + Sync>) -> Result<Self, HWIError> {
let kind = device.device_kind();
let fingerprint = device.get_master_fingerprint().await?;
Ok(Self {
let version = device.get_version().await.ok();
Ok(Self::Supported {
device,
kind,
fingerprint,
version,
})
}
pub fn kind(&self) -> &DeviceKind {
match self {
Self::Unsupported { kind, .. } => kind,
Self::Supported { kind, .. } => kind,
}
}
pub fn fingerprint(&self) -> Option<Fingerprint> {
match self {
Self::Unsupported { .. } => None,
Self::Supported { fingerprint, .. } => Some(*fingerprint),
}
}
pub fn is_supported(&self) -> bool {
matches!(self, Self::Supported { .. })
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
@ -94,14 +122,28 @@ pub async fn list_hardware_wallets(
.expect("Configuration must be correct");
}
hws.push(HardwareWallet {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
});
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
hws.push(HardwareWallet::Supported {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
version,
});
} else {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
}
Err(e) => {
debug!("{}", e);
Err(_) => {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version: None,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
},
Err(HWIError::DeviceNotFound) => {}
@ -124,14 +166,28 @@ pub async fn list_hardware_wallets(
.expect("Configuration must be correct");
}
hws.push(HardwareWallet {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
});
let version = device.get_version().await.ok();
if ledger_version_supported(version.as_ref()) {
hws.push(HardwareWallet::Supported {
kind: device.device_kind(),
fingerprint,
device: Arc::new(device),
version,
});
} else {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
}
Err(e) => {
debug!("{}", e);
Err(_) => {
hws.push(HardwareWallet::Unsupported {
kind: device.device_kind(),
version: None,
message: "Minimal supported app version is 2.1.0".to_string(),
});
}
},
Err(HWIError::DeviceNotFound) => {}
@ -141,3 +197,19 @@ pub async fn list_hardware_wallets(
}
hws
}
fn ledger_version_supported(version: Option<&Version>) -> bool {
if let Some(version) = version {
if version.major >= 2 {
if version.major == 2 {
version.minor >= 1
} else {
true
}
} else {
false
}
} else {
false
}
}

View File

@ -602,18 +602,27 @@ impl DescriptorKeyModal for EditXpubModal {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Select(i) => {
if let Some(hw) = self.hws.get(i) {
let device = hw.device.clone();
if let Some(HardwareWallet::Supported {
device,
fingerprint,
..
}) = self.hws.get(i)
{
self.chosen_hw = Some(i);
self.processing = true;
// If another account n exists, the key is retrieved for the account n+1
let account_index = self
.account_indexes
.get(&hw.fingerprint)
.get(fingerprint)
.map(|account_index| account_index.increment().unwrap())
.unwrap_or_else(|| ChildNumber::from_hardened_idx(0).unwrap());
return Command::perform(
get_extended_pubkey(device, hw.fingerprint, self.network, account_index),
get_extended_pubkey(
device.clone(),
*fingerprint,
self.network,
account_index,
),
|res| {
Message::DefineDescriptor(message::DefineDescriptor::HWXpubImported(
res,
@ -781,20 +790,29 @@ impl HardwareWalletXpubs {
}
fn select(&mut self, i: usize, network: Network) -> Command<Message> {
let device = self.hw.device.clone();
self.processing = true;
self.error = None;
let fingerprint = self.hw.fingerprint;
let next_account = self.next_account;
Command::perform(
async move {
(
i,
get_extended_pubkey(device, fingerprint, network, next_account).await,
)
},
|(i, res)| Message::ImportXpub(i, res),
)
if let HardwareWallet::Supported {
device,
fingerprint,
..
} = &self.hw
{
let device = device.clone();
let fingerprint = *fingerprint;
self.processing = true;
self.error = None;
let next_account = self.next_account;
Command::perform(
async move {
(
i,
get_extended_pubkey(device, fingerprint, network, next_account).await,
)
},
|(i, res)| Message::ImportXpub(i, res),
)
} else {
Command::none()
}
}
pub fn view(&self, i: usize) -> Element<Message> {
@ -854,11 +872,9 @@ impl Step for ParticipateXpub {
}
Message::ConnectedHardwareWallets(hws) => {
for hw in hws {
if let Some(xpub_hw) = self
.xpubs_hw
.iter_mut()
.find(|h| h.hw.fingerprint == hw.fingerprint)
{
if let Some(xpub_hw) = self.xpubs_hw.iter_mut().find(|h| {
h.hw.fingerprint() == hw.fingerprint() && h.hw.kind() == hw.kind()
}) {
xpub_hw.hw = hw;
} else {
self.xpubs_hw.push(HardwareWalletXpubs::new(hw));
@ -1021,15 +1037,23 @@ impl Step for RegisterDescriptor {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Select(i) => {
if let Some((hw, hmac, _)) = self.hws.get(i) {
if let Some((
HardwareWallet::Supported {
device,
fingerprint,
..
},
hmac,
_,
)) = self.hws.get(i)
{
if hmac.is_none() {
let device = hw.device.clone();
let descriptor = self.descriptor.as_ref().unwrap().to_string();
self.chosen_hw = Some(i);
self.processing = true;
self.error = None;
return Command::perform(
register_wallet(device, hw.fingerprint, descriptor),
register_wallet(device.clone(), *fingerprint, descriptor),
Message::WalletRegistered,
);
}
@ -1043,7 +1067,7 @@ impl Step for RegisterDescriptor {
if let Some(hw_h) = self
.hws
.iter_mut()
.find(|hw_h| hw_h.0.fingerprint == fingerprint)
.find(|hw_h| hw_h.0.fingerprint() == Some(fingerprint))
{
hw_h.1 = hmac;
hw_h.2 = true;
@ -1054,11 +1078,9 @@ impl Step for RegisterDescriptor {
}
Message::ConnectedHardwareWallets(hws) => {
for hw in hws {
if !self
.hws
.iter()
.any(|(h, _, _)| h.fingerprint == hw.fingerprint)
{
if !self.hws.iter().any(|(h, _, _)| {
h.fingerprint() == hw.fingerprint() && h.kind() == hw.kind()
}) {
self.hws.push((hw, None, false));
}
}
@ -1073,7 +1095,8 @@ impl Step for RegisterDescriptor {
fn apply(&mut self, ctx: &mut Context) -> bool {
for (hw, token, registered) in &self.hws {
if *registered {
ctx.hws.push((hw.kind, hw.fingerprint, *token));
ctx.hws
.push((*hw.kind(), hw.fingerprint().unwrap(), *token));
}
}
true

View File

@ -412,7 +412,7 @@ pub fn import_descriptor<'a>(
pub fn hardware_wallet_xpubs<'a>(
i: usize,
xpubs: &'a Vec<String>,
hw: &HardwareWallet,
hw: &'a HardwareWallet,
processing: bool,
error: Option<&Error>,
) -> Element<'a, Message> {
@ -421,8 +421,38 @@ pub fn hardware_wallet_xpubs<'a>(
.align_items(Alignment::Center)
.push(
Column::new()
.push(text(format!("{}", hw.kind)).bold())
.push(text(format!("fingerprint: {}", hw.fingerprint)).small())
.push(text(format!("{}", hw.kind())).bold())
.push(match hw {
HardwareWallet::Supported {
fingerprint,
version,
..
} => Row::new()
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
),
HardwareWallet::Unsupported {
version, message, ..
} => Row::new()
.spacing(5)
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push(
iced::widget::tooltip::Tooltip::new(
icon::warning_icon(),
message,
iced::widget::tooltip::Position::Bottom,
)
.style(card::SimpleCardStyle),
),
})
.spacing(5)
.width(Length::Fill),
)
@ -442,7 +472,7 @@ pub fn hardware_wallet_xpubs<'a>(
.padding(10)
.style(button::Style::TransparentBorder.into())
.width(Length::Fill);
if !processing {
if !processing && hw.is_supported() {
bttn = bttn.on_press(Message::Select(i));
}
Container::new(
@ -576,7 +606,7 @@ pub fn participate_xpub(
pub fn register_descriptor<'a>(
progress: (usize, usize),
descriptor: String,
hws: &[(HardwareWallet, Option<[u8; 32]>, bool)],
hws: &'a [(HardwareWallet, Option<[u8; 32]>, bool)],
error: Option<&Error>,
processing: bool,
chosen_hw: Option<usize>,
@ -1039,7 +1069,7 @@ pub fn defined_descriptor_key(
#[allow(clippy::too_many_arguments)]
pub fn edit_key_modal<'a>(
network: bitcoin::Network,
hws: &[HardwareWallet],
hws: &'a [HardwareWallet],
error: Option<&Error>,
processing: bool,
chosen_hw: Option<usize>,
@ -1187,19 +1217,49 @@ pub fn edit_key_modal<'a>(
.into()
}
fn hw_list_view<'a>(
fn hw_list_view(
i: usize,
hw: &HardwareWallet,
chosen: bool,
processing: bool,
registered: bool,
) -> Element<'a, Message> {
) -> Element<Message> {
let mut bttn = Button::new(
Row::new()
.push(
Column::new()
.push(text(format!("{}", hw.kind)).bold())
.push(text(format!("fingerprint: {}", hw.fingerprint)).small())
.push(text(format!("{}", hw.kind())).bold())
.push(match hw {
HardwareWallet::Supported {
fingerprint,
version,
..
} => Row::new()
.spacing(5)
.push(text(format!("fingerprint: {}", fingerprint)).small())
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
),
HardwareWallet::Unsupported {
version, message, ..
} => Row::new()
.spacing(5)
.push_maybe(
version
.as_ref()
.map(|v| text(format!("version: {}", v)).small()),
)
.push(
iced::widget::tooltip::Tooltip::new(
icon::warning_icon(),
message,
iced::widget::tooltip::Position::Bottom,
)
.style(card::SimpleCardStyle),
),
})
.spacing(5)
.width(Length::Fill),
)
@ -1223,7 +1283,7 @@ fn hw_list_view<'a>(
.padding(10)
.style(button::Style::TransparentBorder.into())
.width(Length::Fill);
if !processing {
if !processing && hw.is_supported() {
bttn = bttn.on_press(Message::Select(i));
}
Container::new(bttn)