diff --git a/gui/src/installer/message.rs b/gui/src/installer/message.rs index 6d8ec736..d9baf202 100644 --- a/gui/src/installer/message.rs +++ b/gui/src/installer/message.rs @@ -8,6 +8,7 @@ use crate::hw::HardwareWallet; pub enum Message { CreateWallet, ImportWallet, + BackupDone(bool), Event(iced_native::Event), Exit(PathBuf), Clibpboard(String), diff --git a/gui/src/installer/mod.rs b/gui/src/installer/mod.rs index b95563d4..625ba123 100644 --- a/gui/src/installer/mod.rs +++ b/gui/src/installer/mod.rs @@ -1,5 +1,6 @@ mod config; mod message; +mod prompt; mod step; mod view; @@ -17,8 +18,8 @@ use crate::{ pub use message::Message; use step::{ - Context, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor, RegisterDescriptor, Step, - Welcome, + BackupDescriptor, Context, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor, + RegisterDescriptor, Step, Welcome, }; pub struct Installer { @@ -101,6 +102,7 @@ impl Installer { self.steps = vec![ Welcome::default().into(), DefineDescriptor::new().into(), + BackupDescriptor::default().into(), RegisterDescriptor::default().into(), DefineBitcoind::new().into(), Final::new().into(), diff --git a/gui/src/installer/prompt.rs b/gui/src/installer/prompt.rs new file mode 100644 index 00000000..1cdc9e1c --- /dev/null +++ b/gui/src/installer/prompt.rs @@ -0,0 +1,2 @@ +pub const BACKUP_DESCRIPTOR_MESSAGE: &str = "The descriptor is necessary to recover your funds.\nThe backup of your key (via mnemonics, sometimes called 'seed words') is not enough.\nPlease make sure you have backed up both your private key and your descriptor."; +pub const BACKUP_DESCRIPTOR_HELP: &str = "In Bitcoin, the coins are locked using a Script (related to the 'address'). In order to recover your funds you need both to know the Scripts you have participated in (your 'addresses'), and be able to sign a transaction that spends from those. For the ability to sign you backup your private key, this is your mnemonics ('seed words'). For finding the coins that belongs to you you backup a template of your Script ( / 'addresses'), this is your descriptor. Note however the descriptor needs not be as securely stored as the private key. A thief that steals your descriptor but not your private key will not be able to steal your funds."; diff --git a/gui/src/installer/step/descriptor.rs b/gui/src/installer/step/descriptor.rs index ff3710a7..898e8a28 100644 --- a/gui/src/installer/step/descriptor.rs +++ b/gui/src/installer/step/descriptor.rs @@ -503,3 +503,31 @@ impl From for Box { Box::new(s) } } + +#[derive(Default)] +pub struct BackupDescriptor { + done: bool, + descriptor: Option, +} + +impl Step for BackupDescriptor { + fn update(&mut self, message: Message) -> Command { + if let Message::BackupDone(done) = message { + self.done = done; + } + Command::none() + } + fn load_context(&mut self, ctx: &Context) { + self.descriptor = ctx.descriptor.clone(); + } + fn view(&self) -> Element { + let desc = self.descriptor.as_ref().unwrap(); + view::backup_descriptor(desc.to_string(), self.done) + } +} + +impl From for Box { + fn from(s: BackupDescriptor) -> Box { + Box::new(s) + } +} diff --git a/gui/src/installer/step/mod.rs b/gui/src/installer/step/mod.rs index 00e56977..a0b404fb 100644 --- a/gui/src/installer/step/mod.rs +++ b/gui/src/installer/step/mod.rs @@ -1,5 +1,5 @@ mod descriptor; -pub use descriptor::{DefineDescriptor, ImportDescriptor, RegisterDescriptor}; +pub use descriptor::{BackupDescriptor, DefineDescriptor, ImportDescriptor, RegisterDescriptor}; use std::path::PathBuf; use std::str::FromStr; diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs index 72417d74..67d3d10a 100644 --- a/gui/src/installer/view.rs +++ b/gui/src/installer/view.rs @@ -1,4 +1,4 @@ -use iced::widget::{Button, Column, Container, PickList, Row, Scrollable}; +use iced::widget::{Button, Checkbox, Column, Container, PickList, Row, Scrollable}; use iced::{Alignment, Element, Length}; use liana::miniscript::bitcoin; @@ -217,9 +217,9 @@ pub fn define_descriptor<'a>( .spacing(20), ) .push( - if !user_xpub.value.is_empty() - || !heir_xpub.value.is_empty() - || !sequence.value.is_empty() + if user_xpub.value.is_empty() + && heir_xpub.value.is_empty() + && sequence.value.is_empty() { button::primary(None, "Next").width(Length::Units(200)) } else { @@ -364,6 +364,52 @@ pub fn register_descriptor<'a>( ) } +pub fn backup_descriptor<'a>(descriptor: String, done: bool) -> Element<'a, Message> { + layout( + Column::new() + .push( + text("Did you backup your wallet descriptor ?") + .bold() + .size(50), + ) + .push(text(super::prompt::BACKUP_DESCRIPTOR_MESSAGE)) + .push(card::simple( + Column::new() + .push(text("The descriptor:").small().bold()) + .push(text(descriptor.clone()).small()) + .push( + Row::new().push(Column::new().width(Length::Fill)).push( + button::transparent_border(Some(icon::clipboard_icon()), "Copy") + .on_press(Message::Clibpboard(descriptor)), + ), + ) + .spacing(10), + )) + .push(Checkbox::new( + done, + "I have backed up my descriptor", + Message::BackupDone, + )) + .push(if done { + button::primary(None, "Next") + .on_press(Message::Next) + .width(Length::Units(200)) + } else { + button::primary(None, "Next").width(Length::Units(200)) + }) + .push( + Column::new() + .push(text("Help:").bold()) + .push(text(super::prompt::BACKUP_DESCRIPTOR_HELP).small()), + ) + .width(Length::Fill) + .height(Length::Fill) + .padding(100) + .spacing(50) + .align_items(Alignment::Center), + ) +} + pub fn define_bitcoin<'a>( address: &form::Value, cookie_path: &form::Value,