installer: Participate in a new wallet section
This commit is contained in:
parent
dfc10eba61
commit
a2ac34e6b0
@ -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>),
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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}')
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user