installer: register wallet

This commit is contained in:
edouard 2022-10-25 17:23:19 +02:00
parent a2021ca326
commit c2ed30961c
6 changed files with 197 additions and 18 deletions

6
gui/Cargo.lock generated
View File

@ -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",
]

View File

@ -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<PathBuf, Error>),
Network(bitcoin::Network),
Network(Network),
DefineBitcoind(DefineBitcoind),
DefineDescriptor(DefineDescriptor),
ConnectedHardwareWallets(Vec<HardwareWallet>),
WalletRegistered(Result<(Fingerprint, Option<[u8; 32]>), Error>),
}
#[derive(Debug, Clone)]

View File

@ -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()
}

View File

@ -283,3 +283,103 @@ async fn get_extended_pubkey(
wildcard: Wildcard::Unhardened,
}))
}
#[derive(Default)]
pub struct RegisterDescriptor {
descriptor: Option<InheritanceDescriptor>,
processing: bool,
chosen_hw: Option<usize>,
hws: Vec<(HardwareWallet, Option<[u8; 32]>)>,
error: Option<Error>,
}
impl Step for RegisterDescriptor {
fn load_context(&mut self, ctx: &Context) {
self.descriptor = ctx.descriptor.clone();
}
fn update(&mut self, message: Message) -> Command<Message> {
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<Message> {
Command::perform(list_hardware_wallets(), Message::ConnectedHardwareWallets)
}
fn view(&self) -> Element<Message> {
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<dyn async_hwi::HWI + Send + Sync>,
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<RegisterDescriptor> for Box<dyn Step> {
fn from(s: RegisterDescriptor) -> Box<dyn Step> {
Box::new(s)
}
}

View File

@ -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<Message>;
fn load_context(&mut self, _ctx: &Context) {}
fn load(&self) -> Command<Message> {
Command::none()
}
fn skip(&self, _ctx: &Context) -> bool {
false
}

View File

@ -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<usize>,
) -> 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<String>,
cookie_path: &form::Value<String>,
@ -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>>) -> 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>>) -> 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(),