Merge #1607: Hint user if the key of signing device is not part of the wallet descriptor
c8e455c8ceb2a7b5408380dbe3cd30ce3959ff57 lianad: add a test for LianaDescriptor::contains_fingerprint() (pythcoiner) aead34cc8eaa3e74d2c76c7e1e8341d756d779a2 installer: hint the user if the plugged signing device is not part of the descriptor (pythcoiner) 5ae13dd025c2e038e17746f5bfc8cf72ec5120c7 liana-ui: make it more clear when a device key is not part of the wallet descriptor (pythcoiner) fa8fd4782f97b8969f6a655e0511bf8f96924232 liana: implement LianaDescriptor::contains_fingerprint() (pythcoiner) Pull request description: This PR: - [x] Add `LianaDescriptor::contains_fingerprint()` method - [x] make more explicit when a signing device is not part of the wallet descriptor before (user had to hover the button to see the tooltip):  after:  - [x] Hint the user in the installer if the plugged device is not part of the created/imported descriptor:  ACKs for top commit: jp1ac4: tACK c8e455c8ceb2a7b5408380dbe3cd30ce3959ff57. Tree-SHA512: 092b8cb6efe6b6a6dc4f8505649783cb7c83c231242ab14f479b573e12cd61bab4e6e27a173bee19097d3ad90a8f1e578ecf4b699a4a481b40588f5f60cb9c71
This commit is contained in:
commit
9fc7c093ff
@ -501,6 +501,7 @@ impl super::DescriptorEditModal for EditXpubModal {
|
|||||||
hw.fingerprint() == chosen_signer,
|
hw.fingerprint() == chosen_signer,
|
||||||
self.processing,
|
self.processing,
|
||||||
hw.fingerprint() == chosen_signer,
|
hw.fingerprint() == chosen_signer,
|
||||||
|
None,
|
||||||
self.device_must_support_tapminiscript,
|
self.device_must_support_tapminiscript,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -306,10 +306,11 @@ impl Step for RegisterDescriptor {
|
|||||||
email: Option<&'a str>,
|
email: Option<&'a str>,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let desc = self.descriptor.as_ref().unwrap();
|
let desc = self.descriptor.as_ref().unwrap();
|
||||||
|
|
||||||
view::register_descriptor(
|
view::register_descriptor(
|
||||||
progress,
|
progress,
|
||||||
email,
|
email,
|
||||||
desc.to_string(),
|
desc,
|
||||||
&hws.list,
|
&hws.list,
|
||||||
&self.registered,
|
&self.registered,
|
||||||
self.error.as_ref(),
|
self.error.as_ref(),
|
||||||
|
|||||||
@ -589,7 +589,7 @@ pub fn share_xpubs<'a>(
|
|||||||
pub fn register_descriptor<'a>(
|
pub fn register_descriptor<'a>(
|
||||||
progress: (usize, usize),
|
progress: (usize, usize),
|
||||||
email: Option<&'a str>,
|
email: Option<&'a str>,
|
||||||
descriptor: String,
|
descriptor: &'a LianaDescriptor,
|
||||||
hws: &'a [HardwareWallet],
|
hws: &'a [HardwareWallet],
|
||||||
registered: &HashSet<bitcoin::bip32::Fingerprint>,
|
registered: &HashSet<bitcoin::bip32::Fingerprint>,
|
||||||
error: Option<&Error>,
|
error: Option<&Error>,
|
||||||
@ -598,8 +598,9 @@ pub fn register_descriptor<'a>(
|
|||||||
done: bool,
|
done: bool,
|
||||||
created_desc: bool,
|
created_desc: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
|
let descriptor_str = descriptor.to_string();
|
||||||
let displayed_descriptor =
|
let displayed_descriptor =
|
||||||
if let Ok((template, keys)) = extract_keys_and_template::<String>(&descriptor) {
|
if let Ok((template, keys)) = extract_keys_and_template::<String>(&descriptor_str) {
|
||||||
let mut col = Column::new()
|
let mut col = Column::new()
|
||||||
.push(
|
.push(
|
||||||
card::simple(
|
card::simple(
|
||||||
@ -656,7 +657,7 @@ pub fn register_descriptor<'a>(
|
|||||||
.push(
|
.push(
|
||||||
scrollable(
|
scrollable(
|
||||||
Column::new()
|
Column::new()
|
||||||
.push(text(descriptor.to_owned()).small())
|
.push(text(descriptor_str.to_owned()).small())
|
||||||
.push(Space::with_height(Length::Fixed(5.0))),
|
.push(Space::with_height(Length::Fixed(5.0))),
|
||||||
)
|
)
|
||||||
.direction(scrollable::Direction::Horizontal(
|
.direction(scrollable::Direction::Horizontal(
|
||||||
@ -666,7 +667,7 @@ pub fn register_descriptor<'a>(
|
|||||||
.push(
|
.push(
|
||||||
Row::new().push(Column::new().width(Length::Fill)).push(
|
Row::new().push(Column::new().width(Length::Fill)).push(
|
||||||
button::secondary(Some(icon::clipboard_icon()), "Copy")
|
button::secondary(Some(icon::clipboard_icon()), "Copy")
|
||||||
.on_press(Message::Clibpboard(descriptor)),
|
.on_press(Message::Clibpboard(descriptor_str)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.spacing(10),
|
.spacing(10),
|
||||||
@ -710,6 +711,7 @@ pub fn register_descriptor<'a>(
|
|||||||
hw.fingerprint()
|
hw.fingerprint()
|
||||||
.map(|fg| registered.contains(&fg))
|
.map(|fg| registered.contains(&fg))
|
||||||
.unwrap_or(false),
|
.unwrap_or(false),
|
||||||
|
Some(descriptor),
|
||||||
false,
|
false,
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
@ -1648,14 +1650,16 @@ pub fn defined_sequence<'a>(
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hw_list_view(
|
pub fn hw_list_view<'a>(
|
||||||
i: usize,
|
i: usize,
|
||||||
hw: &HardwareWallet,
|
hw: &'a HardwareWallet,
|
||||||
chosen: bool,
|
chosen: bool,
|
||||||
processing: bool,
|
processing: bool,
|
||||||
selected: bool,
|
selected: bool,
|
||||||
|
descriptor: Option<&'a LianaDescriptor>,
|
||||||
device_must_support_taproot: bool,
|
device_must_support_taproot: bool,
|
||||||
) -> Element<Message> {
|
) -> Element<'a, Message> {
|
||||||
|
let mut unrelated = false;
|
||||||
let mut bttn = Button::new(match hw {
|
let mut bttn = Button::new(match hw {
|
||||||
HardwareWallet::Supported {
|
HardwareWallet::Supported {
|
||||||
kind,
|
kind,
|
||||||
@ -1664,9 +1668,15 @@ pub fn hw_list_view(
|
|||||||
alias,
|
alias,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
|
let device_in_descriptor = descriptor
|
||||||
|
.map(|d| d.contains_fingerprint(*fingerprint))
|
||||||
|
.unwrap_or(true);
|
||||||
let not_tapminiscript = device_must_support_taproot
|
let not_tapminiscript = device_must_support_taproot
|
||||||
&& !is_compatible_with_tapminiscript(kind, version.as_ref());
|
&& !is_compatible_with_tapminiscript(kind, version.as_ref());
|
||||||
if chosen && processing {
|
if !device_in_descriptor {
|
||||||
|
unrelated = true;
|
||||||
|
hw::unrelated_hardware_wallet(kind.to_string(), version.as_ref(), fingerprint)
|
||||||
|
} else if chosen && processing {
|
||||||
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
|
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
|
||||||
} else if selected {
|
} else if selected {
|
||||||
hw::selected_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref(), {
|
hw::selected_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref(), {
|
||||||
@ -1715,7 +1725,7 @@ pub fn hw_list_view(
|
|||||||
})
|
})
|
||||||
.style(theme::button::secondary)
|
.style(theme::button::secondary)
|
||||||
.width(Length::Fill);
|
.width(Length::Fill);
|
||||||
if !processing && hw.is_supported() {
|
if !processing && hw.is_supported() && !unrelated {
|
||||||
bttn = bttn.on_press(Message::Select(i));
|
bttn = bttn.on_press(Message::Select(i));
|
||||||
}
|
}
|
||||||
bttn.into()
|
bttn.into()
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use crate::{color, component::text, icon, image, theme, widget::*};
|
use crate::{color, component::text, icon, image, theme, widget::*};
|
||||||
use iced::{
|
use iced::{
|
||||||
widget::{column, container, row, tooltip},
|
alignment::Vertical,
|
||||||
|
widget::{column, container, row, tooltip, Space},
|
||||||
Alignment, Length,
|
Alignment, Length,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@ -129,24 +130,28 @@ pub fn unrelated_hardware_wallet<'a, T: 'a, K: Display, V: Display, F: Display>(
|
|||||||
version: Option<V>,
|
version: Option<V>,
|
||||||
fingerprint: F,
|
fingerprint: F,
|
||||||
) -> Container<'a, T> {
|
) -> Container<'a, T> {
|
||||||
container(
|
let key = column(vec![
|
||||||
tooltip::Tooltip::new(
|
|
||||||
container(
|
|
||||||
column(vec![
|
|
||||||
text::p1_regular(format!("#{}", fingerprint)).into(),
|
text::p1_regular(format!("#{}", fingerprint)).into(),
|
||||||
Row::new()
|
Row::new()
|
||||||
.spacing(5)
|
.spacing(5)
|
||||||
.push(text::caption(kind.to_string()))
|
.push(text::caption(kind.to_string()))
|
||||||
.push_maybe(version.map(|v| text::caption(v.to_string())))
|
.push_maybe(version.map(|v| text::caption(v.to_string())))
|
||||||
.into(),
|
.into(),
|
||||||
])
|
]);
|
||||||
.width(Length::Fill),
|
container(
|
||||||
|
container(
|
||||||
|
Row::new()
|
||||||
|
.push(key)
|
||||||
|
.push(Space::with_width(15))
|
||||||
|
.push(Space::with_width(Length::Fill))
|
||||||
|
.push(text::text(
|
||||||
|
"This signing device is not related to this Liana wallet.",
|
||||||
|
))
|
||||||
|
.push(Space::with_width(Length::Fill))
|
||||||
|
.align_y(Vertical::Center),
|
||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.padding(10),
|
.padding(10)
|
||||||
"This signer does not have a key in this wallet.",
|
|
||||||
tooltip::Position::Bottom,
|
|
||||||
)
|
|
||||||
.style(theme::card::simple),
|
.style(theme::card::simple),
|
||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use miniscript::{
|
use miniscript::{
|
||||||
bitcoin::{
|
bitcoin::{
|
||||||
self, bip32,
|
self,
|
||||||
|
bip32::{self, Fingerprint},
|
||||||
constants::WITNESS_SCALE_FACTOR,
|
constants::WITNESS_SCALE_FACTOR,
|
||||||
psbt::{Input as PsbtIn, Output as PsbtOut, Psbt},
|
psbt::{Input as PsbtIn, Output as PsbtOut, Psbt},
|
||||||
secp256k1,
|
secp256k1,
|
||||||
@ -210,6 +211,12 @@ impl LianaDescriptor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a key matching this fingerprint is part of this descriptor
|
||||||
|
pub fn contains_fingerprint(&self, fg: Fingerprint) -> bool {
|
||||||
|
self.multi_desc
|
||||||
|
.for_any_key(|k| k.master_fingerprint() == fg)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the descriptor for receiving addresses.
|
/// Get the descriptor for receiving addresses.
|
||||||
pub fn receive_descriptor(&self) -> &SinglePathLianaDesc {
|
pub fn receive_descriptor(&self) -> &SinglePathLianaDesc {
|
||||||
&self.receive_desc
|
&self.receive_desc
|
||||||
@ -2202,5 +2209,15 @@ mod tests {
|
|||||||
LianaDescriptor::from_str("wsh(0)").unwrap_err();
|
LianaDescriptor::from_str("wsh(0)").unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn descriptor_contains_fingerprint() {
|
||||||
|
let descr = LianaDescriptor::from_str("wsh(or_d(multi(3,[aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/0/<0;1>/*,[aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/0/<0;1>/*,[aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/0/<0;1>/*),and_v(v:thresh(2,pkh([aabb0011/48'/0'/0'/2']xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/1/<0;1>/*),a:pkh([aabb0012/48'/0'/0'/2']xpub6Bw79HbNSeS2xXw1sngPE3ehnk1U3iSPCgLYzC9LpN8m9nDuaKLZvkg8QXxL5pDmEmQtYscmUD8B9MkAAZbh6vxPzNXMaLfGQ9Sb3z85qhR/1/<0;1>/*),a:pkh([aabb0013/48'/0'/0'/2']xpub67zuTXF9Ln4731avKTBSawoVVNRuMfmRvkL7kLUaLBRqma9ZqdHBJg9qx8cPUm3oNQMiXT4TmGovXNoQPuwg17RFcVJ8YrnbcooN7pxVJqC/1/<0;1>/*)),older(26352))))").unwrap();
|
||||||
|
|
||||||
|
assert!(descr.contains_fingerprint(Fingerprint::from_str("aabb0011").unwrap()));
|
||||||
|
assert!(descr.contains_fingerprint(Fingerprint::from_str("aabb0012").unwrap()));
|
||||||
|
assert!(descr.contains_fingerprint(Fingerprint::from_str("aabb0013").unwrap()));
|
||||||
|
assert!(!descr.contains_fingerprint(Fingerprint::from_str("aabb0014").unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: test error conditions of deserialization.
|
// TODO: test error conditions of deserialization.
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user