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.hws,
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.chosen_hw,
&self.signed,

View File

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

View File

@ -18,7 +18,7 @@ use liana_ui::{
component::{
badge, button, card,
collapse::Collapse,
form, separation,
form, hw, separation,
text::{text, Text},
},
icon, theme,
@ -710,6 +710,7 @@ pub fn sign_action<'a>(
warning: Option<&Error>,
hws: &'a [HardwareWallet],
signer: Option<Fingerprint>,
signer_alias: Option<&'a String>,
processing: bool,
chosen_hw: Option<usize>,
signed: &[Fingerprint],
@ -746,35 +747,11 @@ pub fn sign_action<'a>(
},
))
.push_maybe(signer.map(|fingerprint| {
Button::new(
Row::new()
.align_items(Alignment::Center)
.push(
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
}),
)
Button::new(if signed.contains(&fingerprint) {
hw::sign_success_hot_signer(fingerprint, signer_alias)
} else {
hw::hot_signer(fingerprint, signer_alias)
})
.on_press(Message::Spend(SpendTxMessage::SelectHotSigner))
.padding(10)
.style(theme::Button::Secondary)

View File

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

View File

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

View File

@ -15,7 +15,7 @@ use liana::{
pub struct Signer {
curve: secp256k1::Secp256k1<secp256k1::SignOnly>,
key: HotSigner,
fingerprint: Fingerprint,
pub fingerprint: Fingerprint,
}
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)
}
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)
}