installer: Participate in a new wallet section

This commit is contained in:
edouard 2023-01-19 18:00:45 +01:00
parent dfc10eba61
commit a2ac34e6b0
6 changed files with 315 additions and 22 deletions

View File

@ -10,8 +10,9 @@ use crate::hw::HardwareWallet;
#[derive(Debug, Clone)]
pub enum Message {
CreateWallet,
ParticipateWallet,
ImportWallet,
BackupDone(bool),
UserActionDone(bool),
Exit(PathBuf),
Clibpboard(String),
Next,
@ -24,6 +25,7 @@ pub enum Message {
Network(Network),
DefineBitcoind(DefineBitcoind),
DefineDescriptor(DefineDescriptor),
ImportXpub(Result<DescriptorPublicKey, Error>),
ConnectedHardwareWallets(Vec<HardwareWallet>),
WalletRegistered(Result<(Fingerprint, Option<[u8; 32]>), Error>),
}

View File

@ -18,7 +18,7 @@ use crate::{
pub use message::Message;
use step::{
BackupDescriptor, Context, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor,
RegisterDescriptor, Step, Welcome,
ParticipateXpub, RegisterDescriptor, Step, Welcome,
};
pub struct Installer {
@ -100,10 +100,22 @@ impl Installer {
];
self.next()
}
Message::ParticipateWallet => {
self.steps = vec![
Welcome::default().into(),
ParticipateXpub::new().into(),
ImportDescriptor::new(false).into(),
BackupDescriptor::default().into(),
RegisterDescriptor::default().into(),
DefineBitcoind::new().into(),
Final::new().into(),
];
self.next()
}
Message::ImportWallet => {
self.steps = vec![
Welcome::default().into(),
ImportDescriptor::new().into(),
ImportDescriptor::new(true).into(),
RegisterDescriptor::default().into(),
DefineBitcoind::new().into(),
Final::new().into(),

View File

@ -611,17 +611,156 @@ async fn get_extended_pubkey(
}))
}
pub struct ParticipateXpub {
network: Network,
network_valid: bool,
data_dir: Option<PathBuf>,
xpub: Option<String>,
shared: bool,
processing: bool,
chosen_hw: Option<usize>,
hws: Vec<(HardwareWallet, bool)>,
error: Option<Error>,
}
impl ParticipateXpub {
pub fn new() -> Self {
Self {
network: Network::Bitcoin,
network_valid: true,
data_dir: None,
processing: false,
xpub: None,
chosen_hw: None,
hws: Vec::new(),
shared: false,
error: None,
}
}
}
impl Step for ParticipateXpub {
// form value is set as valid each time it is edited.
// Verification of the values is happening when the user click on Next button.
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Network(network) => {
self.network = network;
let mut network_datadir = self.data_dir.clone().unwrap();
network_datadir.push(self.network.to_string());
self.network_valid = !network_datadir.exists();
}
Message::UserActionDone(shared) => self.shared = shared,
Message::ImportXpub(res) => {
self.processing = false;
match res {
Err(e) => {
self.error = e.into();
self.chosen_hw = None;
}
Ok(xpub) => {
self.error = None;
self.xpub = Some(xpub.to_string().trim_end_matches("/<0;1>/*").to_string());
for (i, (_, imported)) in self.hws.iter_mut().enumerate() {
*imported = Some(i) == self.chosen_hw;
}
self.chosen_hw = None;
}
}
}
Message::Select(i) => {
if let Some((hw, _)) = self.hws.get(i) {
let device = hw.device.clone();
self.chosen_hw = Some(i);
self.processing = true;
self.error = None;
return Command::perform(
get_extended_pubkey(device, hw.fingerprint, self.network),
Message::ImportXpub,
);
}
}
Message::ConnectedHardwareWallets(hws) => {
for hw in hws {
if !self
.hws
.iter()
.any(|(h, _)| h.fingerprint == hw.fingerprint)
{
self.hws.push((hw, false));
}
}
}
Message::Reload => {
return self.load();
}
_ => {}
};
Command::none()
}
fn load_context(&mut self, ctx: &Context) {
self.network = ctx.bitcoin_config.network;
self.data_dir = Some(ctx.data_dir.clone());
let mut network_datadir = ctx.data_dir.clone();
network_datadir.push(self.network.to_string());
self.network_valid = !network_datadir.exists();
}
fn load(&self) -> Command<Message> {
Command::perform(
list_hardware_wallets(&[], None),
Message::ConnectedHardwareWallets,
)
}
fn apply(&mut self, ctx: &mut Context) -> bool {
ctx.bitcoin_config.network = self.network;
true
}
fn view(&self, progress: (usize, usize)) -> Element<Message> {
view::participate_xpub(
progress,
self.network,
self.network_valid,
&self.hws,
self.processing,
self.chosen_hw,
self.xpub.as_ref(),
self.shared,
self.error.as_ref(),
)
}
}
impl Default for ParticipateXpub {
fn default() -> Self {
Self::new()
}
}
impl From<ParticipateXpub> for Box<dyn Step> {
fn from(s: ParticipateXpub) -> Box<dyn Step> {
Box::new(s)
}
}
pub struct ImportDescriptor {
network: Network,
network_valid: bool,
change_network: bool,
data_dir: Option<PathBuf>,
imported_descriptor: form::Value<String>,
error: Option<String>,
}
impl ImportDescriptor {
pub fn new() -> Self {
pub fn new(change_network: bool) -> Self {
Self {
change_network,
network: Network::Bitcoin,
network_valid: true,
data_dir: None,
@ -679,6 +818,7 @@ impl Step for ImportDescriptor {
fn view(&self, progress: (usize, usize)) -> Element<Message> {
view::import_descriptor(
progress,
self.change_network,
self.network,
self.network_valid,
&self.imported_descriptor,
@ -687,12 +827,6 @@ impl Step for ImportDescriptor {
}
}
impl Default for ImportDescriptor {
fn default() -> Self {
Self::new()
}
}
impl From<ImportDescriptor> for Box<dyn Step> {
fn from(s: ImportDescriptor) -> Box<dyn Step> {
Box::new(s)
@ -817,7 +951,7 @@ pub struct BackupDescriptor {
impl Step for BackupDescriptor {
fn update(&mut self, message: Message) -> Command<Message> {
if let Message::BackupDone(done) = message {
if let Message::UserActionDone(done) = message {
self.done = done;
}
Command::none()

View File

@ -1,5 +1,7 @@
mod descriptor;
pub use descriptor::{BackupDescriptor, DefineDescriptor, ImportDescriptor, RegisterDescriptor};
pub use descriptor::{
BackupDescriptor, DefineDescriptor, ImportDescriptor, ParticipateXpub, RegisterDescriptor,
};
use std::path::PathBuf;
use std::str::FromStr;

View File

@ -82,12 +82,12 @@ pub fn welcome<'a>() -> Element<'a, Message> {
Button::new(
Container::new(
Column::new()
.width(Length::Units(200))
.width(Length::Units(250))
.push(icon::wallet_icon().size(50).width(Length::Units(100)))
.push(text("Create new wallet"))
.push(text("Create a new wallet"))
.align_items(Alignment::Center),
)
.padding(50),
.padding(20),
)
.style(button::Style::Border.into())
.on_press(Message::CreateWallet),
@ -96,12 +96,26 @@ pub fn welcome<'a>() -> Element<'a, Message> {
Button::new(
Container::new(
Column::new()
.width(Length::Units(200))
.push(icon::import_icon().size(50).width(Length::Units(100)))
.push(text("Import wallet"))
.width(Length::Units(250))
.push(icon::people_icon().size(50).width(Length::Units(100)))
.push(text("Participate in a new wallet"))
.align_items(Alignment::Center),
)
.padding(50),
.padding(20),
)
.style(button::Style::Border.into())
.on_press(Message::ParticipateWallet),
)
.push(
Button::new(
Container::new(
Column::new()
.width(Length::Units(250))
.push(icon::import_icon().size(50).width(Length::Units(100)))
.push(text("Import a wallet backup"))
.align_items(Alignment::Center),
)
.padding(20),
)
.style(button::Style::Border.into())
.on_press(Message::ImportWallet),
@ -109,7 +123,6 @@ pub fn welcome<'a>() -> Element<'a, Message> {
)
.width(Length::Fill)
.height(Length::Fill)
.padding(100)
.spacing(50)
.align_items(Alignment::Center),
))
@ -327,6 +340,7 @@ pub fn define_descriptor<'a>(
pub fn import_descriptor<'a>(
progress: (usize, usize),
change_network: bool,
network: bitcoin::Network,
network_valid: bool,
imported_descriptor: &form::Value<String>,
@ -367,7 +381,11 @@ pub fn import_descriptor<'a>(
.push(
Column::new()
.spacing(20)
.push(row_network)
.push_maybe(if change_network {
Some(row_network)
} else {
None
})
.push(col_descriptor),
)
.push(if imported_descriptor.value.is_empty() {
@ -386,6 +404,127 @@ pub fn import_descriptor<'a>(
)
}
#[allow(clippy::too_many_arguments)]
pub fn participate_xpub<'a>(
progress: (usize, usize),
network: bitcoin::Network,
network_valid: bool,
hws: &[(HardwareWallet, bool)],
processing: bool,
chosen_hw: Option<usize>,
xpub: Option<&'a String>,
shared: bool,
error: Option<&Error>,
) -> Element<'a, Message> {
let row_network = Row::new()
.spacing(10)
.align_items(Alignment::Center)
.push(text("Network:").bold())
.push(Container::new(
PickList::new(&NETWORKS[..], Some(Network::from(network)), |net| {
Message::Network(net.into())
})
.padding(10),
))
.push_maybe(if network_valid {
None
} else {
Some(card::warning(
"A data directory already exists for this network".to_string(),
))
});
layout(
progress,
Column::new()
.push(text("Share your public key").bold().size(50))
.push(
Column::new()
.spacing(20)
.width(Length::Fill)
.push(row_network),
)
.push(
Column::new()
.push(
Row::new()
.spacing(10)
.align_items(Alignment::Center)
.push(
Container::new(
text(format!("Select your hardware wallet:")).bold(),
)
.width(Length::Fill),
)
.push(
button::border(Some(icon::reload_icon()), "Refresh")
.on_press(Message::Reload),
),
)
.spacing(10)
.push(
hws.iter()
.enumerate()
.fold(Column::new().spacing(10), |col, (i, hw)| {
col.push(hw_list_view(
i,
&hw.0,
Some(i) == chosen_hw,
processing,
hw.1,
))
}),
)
.width(Length::Fill),
)
.push_maybe(xpub.map(|xpub| {
Column::new()
.spacing(5)
.push(text("Your extended pubkey:").bold())
.push(
Row::new()
.spacing(5)
.align_items(Alignment::Center)
.push(
Container::new(
Scrollable::new(Container::new(text(xpub)).padding(10))
.horizontal_scroll(
Properties::new().width(2).scroller_width(2),
),
)
.width(Length::Fill),
)
.push(
Container::new(
button::border(Some(icon::clipboard_icon()), "Copy")
.on_press(Message::Clibpboard(xpub.clone()))
.width(Length::Shrink),
)
.padding(10),
),
)
}))
.push(Checkbox::new(
"I have shared my xpub",
shared,
Message::UserActionDone,
))
.push(if shared {
button::primary(None, "Next")
.width(Length::Units(200))
.on_press(Message::Next)
} else {
button::primary(None, "Next").width(Length::Units(200))
})
.push_maybe(error.map(|e| card::error("Hardware error", e.to_string())))
.width(Length::Fill)
.height(Length::Fill)
.padding(100)
.spacing(50)
.align_items(Alignment::Center),
)
}
pub fn register_descriptor<'a>(
progress: (usize, usize),
descriptor: String,
@ -518,7 +657,7 @@ pub fn backup_descriptor<'a>(
.push(Checkbox::new(
"I have backed up my descriptor",
done,
Message::BackupDone,
Message::UserActionDone,
))
.push(if done {
button::primary(None, "Next")

View File

@ -218,3 +218,7 @@ pub fn down_icon() -> Text<'static> {
pub fn up_icon() -> Text<'static> {
icon('\u{F27C}')
}
pub fn people_icon() -> Text<'static> {
icon('\u{F4CF}')
}