Merge #421: Add alias to hotsigner component

997829bdfe30a950ca40412fc1d273c2f1ca7e43 fix unregistered hardware wallet view (edouard)
53d32085147fe6f8c0094c26008314eddfcc3bd0 Add alias to hot signer (edouard)

Pull request description:

  When a hot signer is suggested to user and an alias was entered then the alias must be displayed with the "This computer" label as signer kind.

ACKs for top commit:
  edouardparis:
    Self-ACK 997829bdfe30a950ca40412fc1d273c2f1ca7e43

Tree-SHA512: 68cd8f6ca8bdbd439f6392fddf4f34f1d8a1a8b312003f219e20519dabff43b95af496051eef857fed132f80b3b49ee5cd3034361f0af7fdfe9ef666b2c16c2e
This commit is contained in:
edouard 2023-04-07 16:52:02 +02:00
commit e55432d2d6
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
7 changed files with 166 additions and 89 deletions

View File

@ -379,6 +379,10 @@ impl Action for SignAction {
self.error.as_ref(), self.error.as_ref(),
&self.hws, &self.hws,
self.wallet.signer.as_ref().map(|s| s.fingerprint()), self.wallet.signer.as_ref().map(|s| s.fingerprint()),
self.wallet
.signer
.as_ref()
.and_then(|signer| self.wallet.keys_aliases.get(&signer.fingerprint)),
self.processing, self.processing,
self.chosen_hw, self.chosen_hw,
&self.signed, &self.signed,

View File

@ -17,6 +17,7 @@ pub fn hw_list_view(
version, version,
fingerprint, fingerprint,
alias, alias,
registered,
.. ..
} => { } => {
if chosen && processing { if chosen && processing {
@ -28,6 +29,13 @@ pub fn hw_list_view(
fingerprint, fingerprint,
alias.as_ref(), alias.as_ref(),
) )
} else if *registered == Some(false) {
hw::unregistered_hardware_wallet(
kind,
version.as_ref(),
fingerprint,
alias.as_ref(),
)
} else { } else {
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref()) hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} }

View File

@ -18,7 +18,7 @@ use liana_ui::{
component::{ component::{
badge, button, card, badge, button, card,
collapse::Collapse, collapse::Collapse,
form, separation, form, hw, separation,
text::{text, Text}, text::{text, Text},
}, },
icon, theme, icon, theme,
@ -710,6 +710,7 @@ pub fn sign_action<'a>(
warning: Option<&Error>, warning: Option<&Error>,
hws: &'a [HardwareWallet], hws: &'a [HardwareWallet],
signer: Option<Fingerprint>, signer: Option<Fingerprint>,
signer_alias: Option<&'a String>,
processing: bool, processing: bool,
chosen_hw: Option<usize>, chosen_hw: Option<usize>,
signed: &[Fingerprint], signed: &[Fingerprint],
@ -746,35 +747,11 @@ pub fn sign_action<'a>(
}, },
)) ))
.push_maybe(signer.map(|fingerprint| { .push_maybe(signer.map(|fingerprint| {
Button::new( Button::new(if signed.contains(&fingerprint) {
Row::new() hw::sign_success_hot_signer(fingerprint, signer_alias)
.align_items(Alignment::Center) } else {
.push( hw::hot_signer(fingerprint, signer_alias)
Column::new() })
.width(Length::Fill)
.push(text("This computer").bold())
.push(
text(format!("fingerprint: {}", fingerprint))
.small(),
)
.spacing(5)
.width(Length::Fill),
)
.push_maybe(if signed.contains(&fingerprint) {
Some(
Row::new()
.align_items(Alignment::Center)
.spacing(5)
.push(
icon::circle_check_icon()
.style(color::legacy::SUCCESS),
)
.push(text("Signed").style(color::legacy::SUCCESS)),
)
} else {
None
}),
)
.on_press(Message::Spend(SpendTxMessage::SelectHotSigner)) .on_press(Message::Spend(SpendTxMessage::SelectHotSigner))
.padding(10) .padding(10)
.style(theme::Button::Secondary) .style(theme::Button::Secondary)

View File

@ -725,11 +725,9 @@ pub struct EditXpubModal {
form_xpub: form::Value<String>, form_xpub: form::Value<String>,
edit_name: bool, edit_name: bool,
chosen_hw: Option<usize>,
hws: Vec<HardwareWallet>, hws: Vec<HardwareWallet>,
hot_signer: Arc<Signer>,
signer: Arc<Signer>, chosen_signer: Option<Fingerprint>,
chosen_signer: bool,
} }
impl EditXpubModal { impl EditXpubModal {
@ -742,7 +740,7 @@ impl EditXpubModal {
network: Network, network: Network,
account_indexes: HashMap<Fingerprint, ChildNumber>, account_indexes: HashMap<Fingerprint, ChildNumber>,
keys_aliases: HashMap<Fingerprint, String>, keys_aliases: HashMap<Fingerprint, String>,
signer: Arc<Signer>, hot_signer: Arc<Signer>,
) -> Self { ) -> Self {
Self { Self {
form_name: form::Value { form_name: form::Value {
@ -757,14 +755,13 @@ impl EditXpubModal {
account_indexes, account_indexes,
path_index, path_index,
key_index, key_index,
chosen_hw: None,
processing: false, processing: false,
hws: Vec::new(), hws: Vec::new(),
error: None, error: None,
network, network,
edit_name: false, edit_name: false,
chosen_signer: Some(signer.fingerprint()) == key.map(|k| k.master_fingerprint()), chosen_signer: key.map(|k| k.master_fingerprint()),
signer, hot_signer,
} }
} }
fn load(&self) -> Command<Message> { fn load(&self) -> Command<Message> {
@ -790,7 +787,7 @@ impl DescriptorEditModal for EditXpubModal {
.. ..
}) = self.hws.get(i) }) = self.hws.get(i)
{ {
self.chosen_hw = Some(i); self.chosen_signer = Some(*fingerprint);
self.processing = true; self.processing = true;
// If another account n exists, the key is retrieved for the account n+1 // If another account n exists, the key is retrieved for the account n+1
let account_index = self let account_index = self
@ -815,10 +812,7 @@ impl DescriptorEditModal for EditXpubModal {
Message::ConnectedHardwareWallets(hws) => { Message::ConnectedHardwareWallets(hws) => {
self.hws = hws; self.hws = hws;
if let Ok(key) = DescriptorPublicKey::from_str(&self.form_xpub.value) { if let Ok(key) = DescriptorPublicKey::from_str(&self.form_xpub.value) {
self.chosen_hw = self self.chosen_signer = Some(key.master_fingerprint());
.hws
.iter()
.position(|hw| hw.fingerprint() == Some(key.master_fingerprint()));
} }
} }
Message::Reload => { Message::Reload => {
@ -826,10 +820,9 @@ impl DescriptorEditModal for EditXpubModal {
return self.load(); return self.load();
} }
Message::UseHotSigner => { Message::UseHotSigner => {
self.chosen_hw = None; let fingerprint = self.hot_signer.fingerprint();
self.chosen_signer = true; self.chosen_signer = Some(fingerprint);
self.form_xpub.valid = true; self.form_xpub.valid = true;
let fingerprint = self.signer.fingerprint();
if let Some(alias) = self.keys_aliases.get(&fingerprint) { if let Some(alias) = self.keys_aliases.get(&fingerprint) {
self.form_name.valid = true; self.form_name.valid = true;
self.form_name.value = alias.clone(); self.form_name.value = alias.clone();
@ -848,7 +841,7 @@ impl DescriptorEditModal for EditXpubModal {
"[{}{}]{}", "[{}{}]{}",
fingerprint, fingerprint,
derivation_path.to_string().trim_start_matches('m'), derivation_path.to_string().trim_start_matches('m'),
self.signer.get_extended_pubkey(&derivation_path) self.hot_signer.get_extended_pubkey(&derivation_path)
); );
} }
Message::DefineDescriptor(message::DefineDescriptor::KeyModal(msg)) => match msg { Message::DefineDescriptor(message::DefineDescriptor::KeyModal(msg)) => match msg {
@ -863,12 +856,12 @@ impl DescriptorEditModal for EditXpubModal {
} else { } else {
self.edit_name = true; self.edit_name = true;
} }
self.chosen_signer = false; self.chosen_signer = None;
self.form_xpub.valid = true; self.form_xpub.valid = true;
self.form_xpub.value = key.to_string(); self.form_xpub.value = key.to_string();
} }
Err(e) => { Err(e) => {
self.chosen_hw = None; self.chosen_signer = None;
self.error = Some(e); self.error = Some(e);
} }
} }
@ -944,8 +937,9 @@ impl DescriptorEditModal for EditXpubModal {
&self.hws, &self.hws,
self.error.as_ref(), self.error.as_ref(),
self.processing, self.processing,
self.chosen_hw,
self.chosen_signer, self.chosen_signer,
&self.hot_signer,
self.keys_aliases.get(&self.hot_signer.fingerprint),
&self.form_xpub, &self.form_xpub,
&self.form_name, &self.form_name,
self.edit_name, self.edit_name,

View File

@ -5,7 +5,7 @@ use iced::{alignment, Alignment, Length};
use std::{collections::HashSet, str::FromStr}; use std::{collections::HashSet, str::FromStr};
use liana::miniscript::bitcoin; use liana::miniscript::bitcoin::{self, util::bip32::Fingerprint};
use liana_ui::{ use liana_ui::{
color, color,
component::{ component::{
@ -25,6 +25,7 @@ use crate::{
message::{self, Message}, message::{self, Message},
prompt, Error, prompt, Error,
}, },
signer::Signer,
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -1236,8 +1237,9 @@ pub fn edit_key_modal<'a>(
hws: &'a [HardwareWallet], hws: &'a [HardwareWallet],
error: Option<&Error>, error: Option<&Error>,
processing: bool, processing: bool,
chosen_hw: Option<usize>, chosen_signer: Option<Fingerprint>,
chosen_signer: bool, signer: &Signer,
signer_alias: Option<&'a String>,
form_xpub: &form::Value<String>, form_xpub: &form::Value<String>,
form_name: &'a form::Value<String>, form_name: &'a form::Value<String>,
edit_name: bool, edit_name: bool,
@ -1269,37 +1271,21 @@ pub fn edit_key_modal<'a>(
col.push(hw_list_view( col.push(hw_list_view(
i, i,
hw, hw,
Some(i) == chosen_hw, hw.fingerprint() == chosen_signer,
processing, processing,
!processing !processing
&& Some(i) == chosen_hw && hw.fingerprint() == chosen_signer
&& form_xpub.valid && form_xpub.valid
&& !form_xpub.value.is_empty(), && !form_xpub.value.is_empty(),
)) ))
}, },
)) ))
.push( .push(
Button::new( Button::new(if Some(signer.fingerprint) == chosen_signer {
Row::new() hw::selected_hot_signer(signer.fingerprint, signer_alias)
.padding(5) } else {
.width(Length::Fill) hw::unselected_hot_signer(signer.fingerprint, signer_alias)
.align_items(Alignment::Center) })
.push(
Column::new()
.spacing(5)
.push(text("This computer").bold())
.push(
text("Derive a key from a mnemonic stored on this computer").small(),
)
.width(Length::Fill),
)
.push_maybe(if chosen_signer {
Some(icon::circle_check_icon().style(color::legacy::SUCCESS))
} else {
None
})
.spacing(10),
)
.width(Length::Fill) .width(Length::Fill)
.on_press(Message::UseHotSigner) .on_press(Message::UseHotSigner)
.style(theme::Button::Secondary), .style(theme::Button::Secondary),
@ -1315,7 +1301,9 @@ pub fn edit_key_modal<'a>(
.push( .push(
form::Form::new("Extended public key", form_xpub, |msg| { form::Form::new("Extended public key", form_xpub, |msg| {
Message::DefineDescriptor( Message::DefineDescriptor(
message::DefineDescriptor::KeyModal(message::ImportKeyModal::XPubEdited(msg)), message::DefineDescriptor::KeyModal(
message::ImportKeyModal::XPubEdited(msg),
),
) )
}) })
.warning(if network == bitcoin::Network::Bitcoin { .warning(if network == bitcoin::Network::Bitcoin {
@ -1349,9 +1337,9 @@ pub fn edit_key_modal<'a>(
.push(text(&form_name.value)), .push(text(&form_name.value)),
) )
.push(button::border(Some(icon::pencil_icon()), "Edit").on_press( .push(button::border(Some(icon::pencil_icon()), "Edit").on_press(
Message::DefineDescriptor( Message::DefineDescriptor(message::DefineDescriptor::KeyModal(
message::DefineDescriptor::KeyModal(message::ImportKeyModal::EditName), message::ImportKeyModal::EditName,
) )),
)), )),
) )
} else if !form_xpub.value.is_empty() && form_xpub.valid { } else if !form_xpub.value.is_empty() && form_xpub.valid {
@ -1365,9 +1353,9 @@ pub fn edit_key_modal<'a>(
) )
.push( .push(
form::Form::new("Alias", form_name, |msg| { form::Form::new("Alias", form_name, |msg| {
Message::DefineDescriptor( Message::DefineDescriptor(message::DefineDescriptor::KeyModal(
message::DefineDescriptor::KeyModal(message::ImportKeyModal::NameEdited(msg)), message::ImportKeyModal::NameEdited(msg),
) ))
}) })
.warning("Please enter correct alias") .warning("Please enter correct alias")
.size(20) .size(20)
@ -1381,11 +1369,11 @@ pub fn edit_key_modal<'a>(
if form_xpub.valid && !form_xpub.value.is_empty() && !form_name.value.is_empty() if form_xpub.valid && !form_xpub.value.is_empty() && !form_name.value.is_empty()
{ {
button::primary(None, "Apply") button::primary(None, "Apply")
.on_press( .on_press(Message::DefineDescriptor(
Message::DefineDescriptor( message::DefineDescriptor::KeyModal(
message::DefineDescriptor::KeyModal(message::ImportKeyModal::ConfirmXpub), message::ImportKeyModal::ConfirmXpub,
) ),
) ))
.width(Length::Units(200)) .width(Length::Units(200))
} else { } else {
button::primary(None, "Apply").width(Length::Units(100)) button::primary(None, "Apply").width(Length::Units(100))

View File

@ -15,7 +15,7 @@ use liana::{
pub struct Signer { pub struct Signer {
curve: secp256k1::Secp256k1<secp256k1::SignOnly>, curve: secp256k1::Secp256k1<secp256k1::SignOnly>,
key: HotSigner, key: HotSigner,
fingerprint: Fingerprint, pub fingerprint: Fingerprint,
} }
impl std::fmt::Debug for Signer { impl std::fmt::Debug for Signer {

View File

@ -235,3 +235,109 @@ pub fn unsupported_hardware_wallet<'a, T: 'a, K: Display, V: Display>(
) )
.padding(10) .padding(10)
} }
pub fn sign_success_hot_signer<'a, T: 'a, F: Display>(
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text("This computer").small())
.into(),
])
.width(Length::Fill)
.into(),
row(vec![
icon::circle_check_icon()
.style(color::legacy::SUCCESS)
.into(),
text("Signed").style(color::legacy::SUCCESS).into(),
])
.align_items(Alignment::Center)
.spacing(5)
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn selected_hot_signer<'a, T: 'a, F: Display>(
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
container(
row(vec![
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text("This computer").small())
.push(text("(A derived key from a mnemonic stored locally)").small())
.into(),
])
.width(Length::Fill)
.into(),
icon::circle_check_icon()
.style(color::legacy::SUCCESS)
.into(),
])
.align_items(Alignment::Center),
)
.padding(10)
}
pub fn unselected_hot_signer<'a, T: 'a, F: Display>(
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
Container::new(
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text("This computer").small())
.push(text("(A derived key from a mnemonic stored locally)").small())
.into(),
])
.width(Length::Fill),
)
.padding(10)
}
pub fn hot_signer<'a, T: 'a, F: Display>(
fingerprint: F,
alias: Option<impl Into<Cow<'a, str>>>,
) -> Container<'a, T> {
Container::new(
column(vec![
Row::new()
.spacing(5)
.push_maybe(alias.map(|a| text(a).bold()))
.push(text(format!("#{}", fingerprint)))
.into(),
Row::new()
.spacing(5)
.push(text("This computer").small())
.into(),
])
.width(Length::Fill),
)
.padding(10)
}