From c2ed30961c4c49826e97f6ab59eb0952ef1a9d2c Mon Sep 17 00:00:00 2001 From: edouard Date: Tue, 25 Oct 2022 17:23:19 +0200 Subject: [PATCH] installer: register wallet --- gui/Cargo.lock | 6 +- gui/src/installer/message.rs | 5 +- gui/src/installer/mod.rs | 4 +- gui/src/installer/step/descriptor.rs | 100 +++++++++++++++++++++++++++ gui/src/installer/step/mod.rs | 5 +- gui/src/installer/view.rs | 95 ++++++++++++++++++++++--- 6 files changed, 197 insertions(+), 18 deletions(-) diff --git a/gui/Cargo.lock b/gui/Cargo.lock index 281ff269..a4833a42 100644 --- a/gui/Cargo.lock +++ b/gui/Cargo.lock @@ -125,7 +125,7 @@ dependencies = [ [[package]] name = "async-hwi" version = "0.0.1" -source = "git+https://github.com/revault/async-hwi?branch=add-ledger#0eace00eca1ece4e4c49f2cd4d6fbffa2d527ce5" +source = "git+https://github.com/revault/async-hwi?branch=add-ledger#de5615ba2e2e2d3ded6a058f0193f8787d3873dc" dependencies = [ "async-trait", "base64", @@ -1380,7 +1380,7 @@ dependencies = [ [[package]] name = "ledger_bitcoin_client" version = "0.1.0" -source = "git+https://github.com/edouardparis/app-bitcoin-new/?branch=bitcoin_client_rs#4f7951ce7c464db80f241ca8cd46c45770d6eec7" +source = "git+https://github.com/edouardparis/app-bitcoin-new/?branch=bitcoin_client_rs#95ec68b546d49a77eee274880d2450fdb08f840c" dependencies = [ "async-trait", "bitcoin", @@ -2725,7 +2725,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "rand 0.8.5", "static_assertions", ] diff --git a/gui/src/installer/message.rs b/gui/src/installer/message.rs index 1ac412c7..291c144e 100644 --- a/gui/src/installer/message.rs +++ b/gui/src/installer/message.rs @@ -1,4 +1,4 @@ -use minisafe::miniscript::bitcoin; +use minisafe::miniscript::bitcoin::{util::bip32::Fingerprint, Network}; use std::path::PathBuf; use super::Error; @@ -15,10 +15,11 @@ pub enum Message { Reload, Select(usize), Installed(Result), - Network(bitcoin::Network), + Network(Network), DefineBitcoind(DefineBitcoind), DefineDescriptor(DefineDescriptor), ConnectedHardwareWallets(Vec), + WalletRegistered(Result<(Fingerprint, Option<[u8; 32]>), Error>), } #[derive(Debug, Clone)] diff --git a/gui/src/installer/mod.rs b/gui/src/installer/mod.rs index 1bc8a2f1..6199d624 100644 --- a/gui/src/installer/mod.rs +++ b/gui/src/installer/mod.rs @@ -15,7 +15,7 @@ use std::path::PathBuf; use crate::{app::config as gui_config, installer::config::DEFAULT_FILE_NAME}; pub use message::Message; -use step::{Context, DefineBitcoind, DefineDescriptor, Final, Step, Welcome}; +use step::{Context, DefineBitcoind, DefineDescriptor, Final, RegisterDescriptor, Step, Welcome}; pub struct Installer { should_exit: bool, @@ -50,6 +50,7 @@ impl Installer { steps: vec![ Welcome::new(network).into(), DefineDescriptor::new().into(), + RegisterDescriptor::default().into(), DefineBitcoind::new().into(), Final::new().into(), ], @@ -95,6 +96,7 @@ impl Installer { .get_mut(self.current) .expect("There is always a step"); current_step.load_context(&self.context); + return current_step.load(); } Command::none() } diff --git a/gui/src/installer/step/descriptor.rs b/gui/src/installer/step/descriptor.rs index 33bfe555..fb409948 100644 --- a/gui/src/installer/step/descriptor.rs +++ b/gui/src/installer/step/descriptor.rs @@ -283,3 +283,103 @@ async fn get_extended_pubkey( wildcard: Wildcard::Unhardened, })) } + +#[derive(Default)] +pub struct RegisterDescriptor { + descriptor: Option, + processing: bool, + chosen_hw: Option, + hws: Vec<(HardwareWallet, Option<[u8; 32]>)>, + error: Option, +} + +impl Step for RegisterDescriptor { + fn load_context(&mut self, ctx: &Context) { + self.descriptor = ctx.descriptor.clone(); + } + fn update(&mut self, message: Message) -> Command { + match message { + Message::Select(i) => { + if let Some((hw, hmac)) = self.hws.get(i) { + if hmac.is_none() { + let device = hw.device.clone(); + let descriptor = self.descriptor.as_ref().unwrap().to_string(); + self.chosen_hw = Some(i); + self.processing = true; + self.error = None; + return Command::perform( + register_wallet(device, hw.fingerprint, descriptor), + Message::WalletRegistered, + ); + } + } + } + Message::WalletRegistered(res) => { + self.processing = false; + self.chosen_hw = None; + match res { + Ok((fingerprint, hmac)) => { + if let Some(hw_h) = self + .hws + .iter_mut() + .find(|hw_h| hw_h.0.fingerprint == fingerprint) + { + hw_h.1 = Some(hmac.unwrap_or([0x00; 32])); + } + } + Err(e) => self.error = Some(e), + } + } + Message::ConnectedHardwareWallets(hws) => { + for hw in hws { + if !self + .hws + .iter() + .any(|(h, _)| h.fingerprint == hw.fingerprint) + { + self.hws.push((hw, None)); + } + } + } + Message::Reload => { + return self.load(); + } + _ => {} + }; + Command::none() + } + fn apply(&mut self, ctx: &mut Context) -> bool { + true + } + fn load(&self) -> Command { + Command::perform(list_hardware_wallets(), Message::ConnectedHardwareWallets) + } + fn view(&self) -> Element { + let desc = self.descriptor.as_ref().unwrap(); + view::register_descriptor( + &desc.to_string(), + &self.hws, + self.error.as_ref(), + self.processing, + self.chosen_hw, + ) + } +} + +async fn register_wallet( + hw: std::sync::Arc, + fingerprint: Fingerprint, + descriptor: String, +) -> Result<(Fingerprint, Option<[u8; 32]>), Error> { + let hmac = hw + .register_wallet("Minisafe", &descriptor) + .await + .map_err(Error::from)?; + Ok((fingerprint, hmac)) +} + +impl From for Box { + fn from(s: RegisterDescriptor) -> Box { + Box::new(s) + } +} diff --git a/gui/src/installer/step/mod.rs b/gui/src/installer/step/mod.rs index 91955994..e9e738d8 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; +pub use descriptor::{DefineDescriptor, RegisterDescriptor}; use std::path::PathBuf; use std::str::FromStr; @@ -25,6 +25,9 @@ pub trait Step { } fn view(&self) -> Element; fn load_context(&mut self, _ctx: &Context) {} + fn load(&self) -> Command { + Command::none() + } fn skip(&self, _ctx: &Context) -> bool { false } diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs index ba176938..ed057902 100644 --- a/gui/src/installer/view.rs +++ b/gui/src/installer/view.rs @@ -159,6 +159,59 @@ pub fn define_descriptor<'a>( ) } +pub fn register_descriptor<'a>( + descriptor: &str, + hws: &[(HardwareWallet, Option<[u8; 32]>)], + error: Option<&Error>, + processing: bool, + chosen_hw: Option, +) -> Element<'a, Message> { + layout( + column() + .push(text("Register descriptor").bold().size(50)) + .push(text(descriptor).small()) + .push_maybe(error.map(|e| card::error("Failed to import xpub", &e.to_string()))) + .push(if !hws.is_empty() { + column() + .push(text(&format!("{} hardware wallets connected", hws.len())).bold()) + .spacing(10) + .push( + hws.iter() + .enumerate() + .fold(column().spacing(10), |col, (i, hw)| { + col.push(hw_list_view( + i, + &hw.0, + Some(i) == chosen_hw, + processing, + hw.1.is_some(), + )) + }), + ) + .width(Length::Fill) + } else { + column().push(card::simple( + column() + .spacing(20) + .push("No hardware wallet connected") + .push(button::primary(None, "Refresh").on_press(Message::Reload)) + .align_items(Alignment::Center) + .width(Length::Fill), + )) + }) + .push( + button::primary(None, "Next") + .on_press(Message::Next) + .width(Length::Units(200)), + ) + .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, @@ -282,18 +335,30 @@ pub fn hardware_wallet_xpubs_modal<'a>( hws.iter() .enumerate() .fold(column().spacing(10), |col, (i, hw)| { - col.push(hw_list_view(i, hw, Some(i) == chosen_hw, processing)) + col.push(hw_list_view( + i, + hw, + Some(i) == chosen_hw, + processing, + false, + )) }), ) .width(Length::Fill) } else { - column().push(card::simple( - column() - .spacing(10) - .push("Please connect a hardware wallet") - .push(button::primary(None, "Refresh").on_press(Message::Reload)) - .align_items(Alignment::Center), - )) + column() + .push( + card::simple( + column() + .spacing(20) + .width(Length::Fill) + .push("Please connect a hardware wallet") + .push(button::primary(None, "Refresh").on_press(Message::Reload)) + .align_items(Alignment::Center), + ) + .width(Length::Fill), + ) + .width(Length::Fill) }) .width(Length::Fill) .height(Length::Fill) @@ -308,6 +373,7 @@ fn hw_list_view<'a>( hw: &HardwareWallet, chosen: bool, processing: bool, + registered: bool, ) -> Element<'a, Message> { let mut bttn = iced::pure::button( row() @@ -327,6 +393,12 @@ fn hw_list_view<'a>( } else { None }) + .push_maybe(if registered { + Some(column().push(icon::circle_check_icon().color(color::SUCCESS))) + } else { + None + }) + .align_items(Alignment::Center) .width(Length::Fill), ) .padding(10) @@ -353,6 +425,7 @@ fn layout<'a>(content: impl Into>) -> Element<'a, Message> .center_x() .height(Length::Fill) .width(Length::Fill) + .style(BackgroundStyle) .into() } @@ -372,12 +445,12 @@ fn modal<'a>(content: impl Into>) -> Element<'a, Message> { .center_x() .height(Length::Fill) .width(Length::Fill) - .style(ModalStyle) + .style(BackgroundStyle) .into() } -pub struct ModalStyle; -impl widget::container::StyleSheet for ModalStyle { +pub struct BackgroundStyle; +impl widget::container::StyleSheet for BackgroundStyle { fn style(&self) -> widget::container::Style { widget::container::Style { background: color::BACKGROUND.into(),