From 233f9b1886078ad2a94704537df8dacfeb5fe06a Mon Sep 17 00:00:00 2001 From: edouard Date: Fri, 10 Feb 2023 13:18:22 +0100 Subject: [PATCH] installer: add backup mnemonic step --- gui/src/installer/mod.rs | 25 ++++++++++++++-- gui/src/installer/prompt.rs | 1 + gui/src/installer/step/mnemonic.rs | 35 +++++++++++++++++++++++ gui/src/installer/step/mod.rs | 4 +++ gui/src/installer/view.rs | 46 ++++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 gui/src/installer/step/mnemonic.rs diff --git a/gui/src/installer/mod.rs b/gui/src/installer/mod.rs index 316f8844..86dfebd7 100644 --- a/gui/src/installer/mod.rs +++ b/gui/src/installer/mod.rs @@ -15,8 +15,8 @@ use crate::app::{config as gui_config, settings as gui_settings}; pub use message::Message; use step::{ - BackupDescriptor, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor, ParticipateXpub, - RegisterDescriptor, Step, Welcome, + BackupDescriptor, BackupMnemonic, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor, + ParticipateXpub, RegisterDescriptor, Step, Welcome, }; pub struct Installer { @@ -91,6 +91,7 @@ impl Installer { self.steps = vec![ Welcome::default().into(), DefineDescriptor::new().into(), + BackupMnemonic::default().into(), BackupDescriptor::default().into(), RegisterDescriptor::default().into(), DefineBitcoind::new().into(), @@ -103,6 +104,7 @@ impl Installer { Welcome::default().into(), ParticipateXpub::new().into(), ImportDescriptor::new(false).into(), + BackupMnemonic::default().into(), BackupDescriptor::default().into(), RegisterDescriptor::default().into(), DefineBitcoind::new().into(), @@ -165,11 +167,28 @@ impl Installer { } } + /// Some steps are skipped because of contextual choice of the user, this + /// code is giving a correct progress summary to the user. + fn progress(&self) -> (usize, usize) { + let mut current = self.current; + let mut total = 0; + for (i, step) in self.steps.iter().enumerate() { + if step.skip(&self.context) { + if i < self.current { + current -= 1; + } + } else { + total += 1 + } + } + (current, total - 1) + } + pub fn view(&self) -> Element { self.steps .get(self.current) .expect("There is always a step") - .view((self.current, self.steps.len() - 1)) + .view(self.progress()) } } diff --git a/gui/src/installer/prompt.rs b/gui/src/installer/prompt.rs index c08e0069..f2055479 100644 --- a/gui/src/installer/prompt.rs +++ b/gui/src/installer/prompt.rs @@ -7,3 +7,4 @@ pub const DEFINE_DESCRIPTOR_SEQUENCE_TOOLTIP: &str = pub const DEFINE_DESCRIPTOR_FINGERPRINT_TOOLTIP: &str = "The alias is applied on all the keys derived from the same seed"; pub const REGISTER_DESCRIPTOR_HELP: &str = "To be used with the wallet, a device needs the descriptor. Registration on a device is not a substitute for backing up the descriptor."; +pub const MNEMONIC_HELP: &str = "A hot key generated on this computer was used for creating this wallet. It needs to be backed up. \n Keep it in a safe place. Never share it with anyone."; diff --git a/gui/src/installer/step/mnemonic.rs b/gui/src/installer/step/mnemonic.rs new file mode 100644 index 00000000..de3cb7af --- /dev/null +++ b/gui/src/installer/step/mnemonic.rs @@ -0,0 +1,35 @@ +use crate::installer::{context::Context, message::Message, step::Step, view}; + +use iced::{Command, Element}; + +#[derive(Default)] +pub struct BackupMnemonic { + words: [&'static str; 12], + done: bool, +} + +impl From for Box { + fn from(s: BackupMnemonic) -> Box { + Box::new(s) + } +} + +impl Step for BackupMnemonic { + fn load_context(&mut self, ctx: &Context) { + if let Some(signer) = &ctx.signer { + self.words = signer.mnemonic(); + } + } + fn update(&mut self, message: Message) -> Command { + if let Message::UserActionDone(done) = message { + self.done = done; + } + Command::none() + } + fn skip(&self, ctx: &Context) -> bool { + ctx.signer.is_none() + } + fn view(&self, progress: (usize, usize)) -> Element { + view::backup_mnemonic(progress, &self.words, self.done) + } +} diff --git a/gui/src/installer/step/mod.rs b/gui/src/installer/step/mod.rs index 198cce20..91efff56 100644 --- a/gui/src/installer/step/mod.rs +++ b/gui/src/installer/step/mod.rs @@ -1,8 +1,12 @@ mod descriptor; +mod mnemonic; + pub use descriptor::{ BackupDescriptor, DefineDescriptor, ImportDescriptor, ParticipateXpub, RegisterDescriptor, }; +pub use mnemonic::BackupMnemonic; + use std::path::PathBuf; use std::str::FromStr; diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs index 60d9b833..6d13faae 100644 --- a/gui/src/installer/view.rs +++ b/gui/src/installer/view.rs @@ -1319,6 +1319,52 @@ fn hw_list_view( .into() } +pub fn backup_mnemonic<'a>( + progress: (usize, usize), + words: &'a [&'static str; 12], + done: bool, +) -> Element<'a, Message> { + layout( + progress, + Column::new() + .push(text("Backup your mnemonic").bold().size(50)) + .push(text(prompt::MNEMONIC_HELP)) + .push( + words + .iter() + .enumerate() + .fold(Column::new().spacing(5), |acc, (i, w)| { + acc.push( + Row::new() + .align_items(Alignment::End) + .push( + Container::new(text(format!("#{}", i + 1)).small()) + .width(Length::Units(50)), + ) + .push(text(*w).bold()), + ) + }), + ) + .push(Checkbox::new( + "I have backed up my mnemonic", + done, + Message::UserActionDone, + )) + .push(if done { + button::primary(None, "Next") + .on_press(Message::Next) + .width(Length::Units(200)) + } else { + button::primary(None, "Next").width(Length::Units(200)) + }) + .width(Length::Fill) + .height(Length::Fill) + .padding(100) + .spacing(50) + .align_items(Alignment::Center), + ) +} + fn layout<'a>( progress: (usize, usize), content: impl Into>,