installer: create or import wallet
This commit is contained in:
parent
c89b8d88f5
commit
92699c645b
@ -18,7 +18,7 @@ impl TryFrom<Context> for LianaConfig {
|
||||
daemon: false,
|
||||
log_level: log::LevelFilter::Info,
|
||||
main_descriptor: ctx.descriptor.unwrap(),
|
||||
data_dir: ctx.data_dir,
|
||||
data_dir: Some(ctx.data_dir),
|
||||
bitcoin_config: ctx.bitcoin_config,
|
||||
bitcoind_config: ctx.bitcoind_config,
|
||||
})
|
||||
|
||||
@ -6,6 +6,8 @@ use crate::hw::HardwareWallet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
CreateWallet,
|
||||
ImportWallet,
|
||||
Event(iced_native::Event),
|
||||
Exit(PathBuf),
|
||||
Clibpboard(String),
|
||||
|
||||
@ -16,7 +16,10 @@ use crate::{
|
||||
};
|
||||
|
||||
pub use message::Message;
|
||||
use step::{Context, DefineBitcoind, DefineDescriptor, Final, RegisterDescriptor, Step, Welcome};
|
||||
use step::{
|
||||
Context, DefineBitcoind, DefineDescriptor, Final, ImportDescriptor, RegisterDescriptor, Step,
|
||||
Welcome,
|
||||
};
|
||||
|
||||
pub struct Installer {
|
||||
should_exit: bool,
|
||||
@ -28,12 +31,6 @@ pub struct Installer {
|
||||
}
|
||||
|
||||
impl Installer {
|
||||
fn next(&mut self) {
|
||||
if self.current < self.steps.len() - 1 {
|
||||
self.current += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn previous(&mut self) {
|
||||
if self.current > 0 {
|
||||
self.current -= 1;
|
||||
@ -48,14 +45,8 @@ impl Installer {
|
||||
Installer {
|
||||
should_exit: false,
|
||||
current: 0,
|
||||
steps: vec![
|
||||
Welcome::new(network, destination_path.clone()).into(),
|
||||
DefineDescriptor::new().into(),
|
||||
RegisterDescriptor::default().into(),
|
||||
DefineBitcoind::new().into(),
|
||||
Final::new().into(),
|
||||
],
|
||||
context: Context::new(network, Some(destination_path)),
|
||||
steps: vec![Welcome::default().into()],
|
||||
context: Context::new(network, destination_path),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
@ -73,35 +64,61 @@ impl Installer {
|
||||
self.should_exit = true;
|
||||
}
|
||||
|
||||
fn next(&mut self) -> Command<Message> {
|
||||
let current_step = self
|
||||
.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step");
|
||||
if current_step.apply(&mut self.context) {
|
||||
if self.current < self.steps.len() - 1 {
|
||||
self.current += 1;
|
||||
}
|
||||
// skip the step according to the current context.
|
||||
while self
|
||||
.steps
|
||||
.get(self.current)
|
||||
.expect("There is always a step")
|
||||
.skip(&self.context)
|
||||
{
|
||||
if self.current < self.steps.len() - 1 {
|
||||
self.current += 1;
|
||||
}
|
||||
}
|
||||
// calculate new current_step.
|
||||
let current_step = self
|
||||
.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step");
|
||||
current_step.load_context(&self.context);
|
||||
return current_step.load();
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Clibpboard(s) => clipboard::write(s),
|
||||
Message::Next => {
|
||||
let current_step = self
|
||||
.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step");
|
||||
if current_step.apply(&mut self.context) {
|
||||
self.next();
|
||||
// skip the step according to the current context.
|
||||
while self
|
||||
.steps
|
||||
.get(self.current)
|
||||
.expect("There is always a step")
|
||||
.skip(&self.context)
|
||||
{
|
||||
self.next();
|
||||
}
|
||||
// calculate new current_step.
|
||||
let current_step = self
|
||||
.steps
|
||||
.get_mut(self.current)
|
||||
.expect("There is always a step");
|
||||
current_step.load_context(&self.context);
|
||||
return current_step.load();
|
||||
}
|
||||
Command::none()
|
||||
Message::CreateWallet => {
|
||||
self.steps = vec![
|
||||
Welcome::default().into(),
|
||||
DefineDescriptor::new().into(),
|
||||
RegisterDescriptor::default().into(),
|
||||
DefineBitcoind::new().into(),
|
||||
Final::new().into(),
|
||||
];
|
||||
self.next()
|
||||
}
|
||||
Message::ImportWallet => {
|
||||
self.steps = vec![
|
||||
Welcome::default().into(),
|
||||
ImportDescriptor::new().into(),
|
||||
RegisterDescriptor::default().into(),
|
||||
DefineBitcoind::new().into(),
|
||||
Final::new().into(),
|
||||
];
|
||||
self.next()
|
||||
}
|
||||
Message::Clibpboard(s) => clipboard::write(s),
|
||||
Message::Next => self.next(),
|
||||
Message::Previous => {
|
||||
self.previous();
|
||||
Command::none()
|
||||
@ -114,7 +131,7 @@ impl Installer {
|
||||
Command::perform(install(self.context.clone()), Message::Installed)
|
||||
}
|
||||
Message::Installed(Err(e)) => {
|
||||
let mut data_dir = self.context.data_dir.clone().unwrap();
|
||||
let mut data_dir = self.context.data_dir.clone();
|
||||
data_dir.push(self.context.bitcoin_config.network.to_string());
|
||||
// In case of failure during install, block the thread to
|
||||
// deleted the data_dir/network directory in order to start clean again.
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use iced::{Command, Element};
|
||||
@ -8,7 +9,7 @@ use liana::{
|
||||
util::bip32::{DerivationPath, Fingerprint},
|
||||
Network,
|
||||
},
|
||||
descriptor::{Descriptor, DescriptorMultiXKey, DescriptorPublicKey, Wildcard},
|
||||
descriptor::{DescriptorMultiXKey, DescriptorPublicKey, Wildcard},
|
||||
},
|
||||
};
|
||||
|
||||
@ -24,7 +25,8 @@ use crate::{
|
||||
|
||||
pub struct DefineDescriptor {
|
||||
network: Network,
|
||||
imported_descriptor: form::Value<String>,
|
||||
network_valid: bool,
|
||||
data_dir: Option<PathBuf>,
|
||||
user_xpub: form::Value<String>,
|
||||
heir_xpub: form::Value<String>,
|
||||
sequence: form::Value<String>,
|
||||
@ -37,7 +39,8 @@ impl DefineDescriptor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
network: Network::Bitcoin,
|
||||
imported_descriptor: form::Value::default(),
|
||||
data_dir: None,
|
||||
network_valid: true,
|
||||
user_xpub: form::Value::default(),
|
||||
heir_xpub: form::Value::default(),
|
||||
sequence: form::Value::default(),
|
||||
@ -55,12 +58,14 @@ impl Step for DefineDescriptor {
|
||||
Message::Close => {
|
||||
self.modal = None;
|
||||
}
|
||||
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::DefineDescriptor(msg) => {
|
||||
match msg {
|
||||
message::DefineDescriptor::ImportDescriptor(desc) => {
|
||||
self.imported_descriptor.value = desc;
|
||||
self.imported_descriptor.valid = true;
|
||||
}
|
||||
message::DefineDescriptor::UserXpubEdited(xpub) => {
|
||||
self.user_xpub.value = xpub;
|
||||
self.user_xpub.valid = true;
|
||||
@ -107,78 +112,41 @@ impl Step for DefineDescriptor {
|
||||
|
||||
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 apply(&mut self, ctx: &mut Context) -> bool {
|
||||
ctx.bitcoin_config.network = self.network;
|
||||
// descriptor forms for import or creation cannot be both empty or filled.
|
||||
if self.imported_descriptor.value.is_empty()
|
||||
== (self.user_xpub.value.is_empty()
|
||||
|| self.heir_xpub.value.is_empty()
|
||||
|| self.sequence.value.is_empty())
|
||||
let user_key = DescriptorPublicKey::from_str(&self.user_xpub.value);
|
||||
self.user_xpub.valid = user_key.is_ok();
|
||||
if let Ok(key) = &user_key {
|
||||
self.user_xpub.valid = check_key_network(key, self.network);
|
||||
}
|
||||
|
||||
let heir_key = DescriptorPublicKey::from_str(&self.heir_xpub.value);
|
||||
self.heir_xpub.valid = heir_key.is_ok();
|
||||
if let Ok(key) = &heir_key {
|
||||
self.heir_xpub.valid = check_key_network(key, self.network);
|
||||
}
|
||||
|
||||
let sequence = self.sequence.value.parse::<u16>();
|
||||
self.sequence.valid = sequence.is_ok();
|
||||
|
||||
if !self.network_valid
|
||||
|| !self.user_xpub.valid
|
||||
|| !self.heir_xpub.valid
|
||||
|| !self.sequence.valid
|
||||
{
|
||||
if !self.user_xpub.value.is_empty() {
|
||||
let key = DescriptorPublicKey::from_str(&self.user_xpub.value);
|
||||
self.user_xpub.valid = key.is_ok();
|
||||
// Check the Network
|
||||
if let Ok(key) = &key {
|
||||
self.user_xpub.valid = check_key_network(key, ctx.bitcoin_config.network);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.heir_xpub.value.is_empty() {
|
||||
let key = DescriptorPublicKey::from_str(&self.heir_xpub.value);
|
||||
self.heir_xpub.valid = key.is_ok();
|
||||
// Check the Network
|
||||
if let Ok(key) = &key {
|
||||
self.heir_xpub.valid = check_key_network(key, ctx.bitcoin_config.network);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.sequence.value.is_empty() {
|
||||
self.sequence.valid = self.sequence.value.parse::<u32>().is_ok();
|
||||
} else {
|
||||
self.sequence.valid = false;
|
||||
}
|
||||
|
||||
if !self.imported_descriptor.value.is_empty() {
|
||||
self.imported_descriptor.valid =
|
||||
Descriptor::<DescriptorPublicKey>::from_str(&self.imported_descriptor.value)
|
||||
.is_ok();
|
||||
}
|
||||
false
|
||||
} else if !self.imported_descriptor.value.is_empty() {
|
||||
if let Ok(desc) = MultipathDescriptor::from_str(&self.imported_descriptor.value) {
|
||||
ctx.descriptor = Some(desc);
|
||||
true
|
||||
} else {
|
||||
self.imported_descriptor.valid = false;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
let user_key = DescriptorPublicKey::from_str(&self.user_xpub.value);
|
||||
self.user_xpub.valid = user_key.is_ok();
|
||||
if let Ok(key) = &user_key {
|
||||
self.user_xpub.valid = check_key_network(key, ctx.bitcoin_config.network);
|
||||
}
|
||||
|
||||
let heir_key = DescriptorPublicKey::from_str(&self.heir_xpub.value);
|
||||
self.heir_xpub.valid = heir_key.is_ok();
|
||||
if let Ok(key) = &heir_key {
|
||||
self.heir_xpub.valid = check_key_network(key, ctx.bitcoin_config.network);
|
||||
}
|
||||
|
||||
let sequence = self.sequence.value.parse::<u16>();
|
||||
self.sequence.valid = sequence.is_ok();
|
||||
|
||||
if !self.user_xpub.valid || !self.heir_xpub.valid || !self.sequence.valid {
|
||||
return false;
|
||||
}
|
||||
|
||||
let desc = match MultipathDescriptor::new(
|
||||
user_key.unwrap(),
|
||||
heir_key.unwrap(),
|
||||
sequence.unwrap(),
|
||||
) {
|
||||
let desc =
|
||||
match MultipathDescriptor::new(user_key.unwrap(), heir_key.unwrap(), sequence.unwrap())
|
||||
{
|
||||
Ok(desc) => desc,
|
||||
Err(e) => {
|
||||
self.error = Some(e.to_string());
|
||||
@ -186,9 +154,8 @@ impl Step for DefineDescriptor {
|
||||
}
|
||||
};
|
||||
|
||||
ctx.descriptor = Some(desc);
|
||||
true
|
||||
}
|
||||
ctx.descriptor = Some(desc);
|
||||
true
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
@ -197,7 +164,7 @@ impl Step for DefineDescriptor {
|
||||
} else {
|
||||
view::define_descriptor(
|
||||
self.network,
|
||||
&self.imported_descriptor,
|
||||
self.network_valid,
|
||||
&self.user_xpub,
|
||||
&self.heir_xpub,
|
||||
&self.sequence,
|
||||
@ -341,6 +308,92 @@ async fn get_extended_pubkey(
|
||||
}))
|
||||
}
|
||||
|
||||
pub struct ImportDescriptor {
|
||||
network: Network,
|
||||
network_valid: bool,
|
||||
data_dir: Option<PathBuf>,
|
||||
imported_descriptor: form::Value<String>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
impl ImportDescriptor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
network: Network::Bitcoin,
|
||||
network_valid: true,
|
||||
data_dir: None,
|
||||
imported_descriptor: form::Value::default(),
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for ImportDescriptor {
|
||||
// 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::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) => {
|
||||
self.imported_descriptor.value = desc;
|
||||
self.imported_descriptor.valid = true;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
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 apply(&mut self, ctx: &mut Context) -> bool {
|
||||
ctx.bitcoin_config.network = self.network;
|
||||
// descriptor forms for import or creation cannot be both empty or filled.
|
||||
if !self.imported_descriptor.value.is_empty() {
|
||||
if let Ok(desc) = MultipathDescriptor::from_str(&self.imported_descriptor.value) {
|
||||
ctx.descriptor = Some(desc);
|
||||
true
|
||||
} else {
|
||||
self.imported_descriptor.valid = false;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
view::import_descriptor(
|
||||
self.network,
|
||||
self.network_valid,
|
||||
&self.imported_descriptor,
|
||||
self.error.as_ref(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RegisterDescriptor {
|
||||
descriptor: Option<MultipathDescriptor>,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
mod descriptor;
|
||||
pub use descriptor::{DefineDescriptor, RegisterDescriptor};
|
||||
pub use descriptor::{DefineDescriptor, ImportDescriptor, RegisterDescriptor};
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@ -43,11 +43,11 @@ pub struct Context {
|
||||
pub bitcoind_config: Option<BitcoindConfig>,
|
||||
pub descriptor: Option<MultipathDescriptor>,
|
||||
pub hw_tokens: Vec<(DeviceKind, bitcoin::util::bip32::Fingerprint, [u8; 32])>,
|
||||
pub data_dir: Option<PathBuf>,
|
||||
pub data_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(network: bitcoin::Network, data_dir: Option<PathBuf>) -> Self {
|
||||
pub fn new(network: bitcoin::Network, data_dir: PathBuf) -> Self {
|
||||
Self {
|
||||
bitcoin_config: BitcoinConfig {
|
||||
network,
|
||||
@ -61,36 +61,12 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Welcome {
|
||||
network: bitcoin::Network,
|
||||
data_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl Welcome {
|
||||
pub fn new(network: bitcoin::Network, data_dir: PathBuf) -> Self {
|
||||
Self { network, data_dir }
|
||||
}
|
||||
|
||||
fn valid(&self) -> bool {
|
||||
let mut network_datadir = self.data_dir.clone();
|
||||
network_datadir.push(self.network.to_string());
|
||||
!network_datadir.exists()
|
||||
}
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub struct Welcome {}
|
||||
|
||||
impl Step for Welcome {
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
if let message::Message::Network(network) = message {
|
||||
self.network = network;
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
fn apply(&mut self, ctx: &mut Context) -> bool {
|
||||
ctx.bitcoin_config.network = self.network;
|
||||
true
|
||||
}
|
||||
fn view(&self) -> Element<Message> {
|
||||
view::welcome(&self.network, self.valid())
|
||||
view::welcome()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -68,24 +68,41 @@ const NETWORKS: [Network; 4] = [
|
||||
Network::Regtest,
|
||||
];
|
||||
|
||||
pub fn welcome(network: &bitcoin::Network, valid: bool) -> Element<Message> {
|
||||
pub fn welcome<'a>() -> Element<'a, Message> {
|
||||
Container::new(Container::new(
|
||||
Column::new()
|
||||
.push(Container::new(
|
||||
PickList::new(&NETWORKS[..], Some(Network::from(*network)), |net| {
|
||||
Message::Network(net.into())
|
||||
})
|
||||
.padding(10),
|
||||
))
|
||||
.push(if valid {
|
||||
Container::new(
|
||||
button::primary(None, "Start the install")
|
||||
.on_press(Message::Next)
|
||||
.width(Length::Units(200)),
|
||||
)
|
||||
} else {
|
||||
card::warning("A data directory already exists for this network".to_string())
|
||||
})
|
||||
.push(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.push(
|
||||
Button::new(
|
||||
Container::new(
|
||||
Column::new()
|
||||
.width(Length::Units(200))
|
||||
.push(icon::wallet_icon().size(50).width(Length::Units(100)))
|
||||
.push(text("Create new wallet"))
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.padding(50),
|
||||
)
|
||||
.style(button::Style::Border.into())
|
||||
.on_press(Message::CreateWallet),
|
||||
)
|
||||
.push(
|
||||
Button::new(
|
||||
Container::new(
|
||||
Column::new()
|
||||
.width(Length::Units(200))
|
||||
.push(icon::import_icon().size(50).width(Length::Units(100)))
|
||||
.push(text("Import wallet"))
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.padding(50),
|
||||
)
|
||||
.style(button::Style::Border.into())
|
||||
.on_press(Message::ImportWallet),
|
||||
),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(100)
|
||||
@ -101,26 +118,32 @@ pub fn welcome(network: &bitcoin::Network, valid: bool) -> Element<Message> {
|
||||
|
||||
pub fn define_descriptor<'a>(
|
||||
network: bitcoin::Network,
|
||||
imported_descriptor: &form::Value<String>,
|
||||
network_valid: bool,
|
||||
user_xpub: &form::Value<String>,
|
||||
heir_xpub: &form::Value<String>,
|
||||
sequence: &form::Value<String>,
|
||||
error: Option<&String>,
|
||||
) -> Element<'a, Message> {
|
||||
let col_descriptor = Column::new()
|
||||
.push(text("Descriptor:").bold())
|
||||
.push(
|
||||
form::Form::new("Descriptor", imported_descriptor, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(msg))
|
||||
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())
|
||||
})
|
||||
.warning("Please enter correct descriptor")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
)
|
||||
.spacing(10);
|
||||
))
|
||||
.push_maybe(if network_valid {
|
||||
None
|
||||
} else {
|
||||
Some(card::warning(
|
||||
"A data directory already exists for this network".to_string(),
|
||||
))
|
||||
});
|
||||
|
||||
let col_user_xpub = Column::new()
|
||||
.push(text("Your xpub:").bold())
|
||||
.push(text("Your public key:").bold())
|
||||
.push(
|
||||
Row::new()
|
||||
.push(
|
||||
@ -144,7 +167,7 @@ pub fn define_descriptor<'a>(
|
||||
.spacing(10);
|
||||
|
||||
let col_heir_xpub = Column::new()
|
||||
.push(text("Heir xpub:").bold())
|
||||
.push(text("Public key of the recovery key:").bold())
|
||||
.push(
|
||||
Row::new()
|
||||
.push(
|
||||
@ -168,7 +191,7 @@ pub fn define_descriptor<'a>(
|
||||
.spacing(10);
|
||||
|
||||
let col_sequence = Column::new()
|
||||
.push(text("Number of block:").bold())
|
||||
.push(text("Number of block before enabling recovery:").bold())
|
||||
.push(
|
||||
Container::new(
|
||||
form::Form::new("Number of block", sequence, |msg| {
|
||||
@ -184,21 +207,19 @@ pub fn define_descriptor<'a>(
|
||||
|
||||
layout(
|
||||
Column::new()
|
||||
.push(text("Create the descriptor").bold().size(50))
|
||||
.push(text("Create the wallet").bold().size(50))
|
||||
.push(
|
||||
Column::new()
|
||||
.push(row_network)
|
||||
.push(col_user_xpub)
|
||||
.push(col_sequence)
|
||||
.push(col_heir_xpub)
|
||||
.spacing(20),
|
||||
)
|
||||
.push(text("or import it").bold().size(25))
|
||||
.push(col_descriptor)
|
||||
.push(
|
||||
if !imported_descriptor.value.is_empty()
|
||||
&& (!user_xpub.value.is_empty()
|
||||
|| !heir_xpub.value.is_empty()
|
||||
|| !sequence.value.is_empty())
|
||||
if !user_xpub.value.is_empty()
|
||||
|| !heir_xpub.value.is_empty()
|
||||
|| !sequence.value.is_empty()
|
||||
{
|
||||
button::primary(None, "Next").width(Length::Units(200))
|
||||
} else {
|
||||
@ -216,6 +237,65 @@ pub fn define_descriptor<'a>(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn import_descriptor<'a>(
|
||||
network: bitcoin::Network,
|
||||
network_valid: bool,
|
||||
imported_descriptor: &form::Value<String>,
|
||||
error: Option<&String>,
|
||||
) -> 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(),
|
||||
))
|
||||
});
|
||||
let col_descriptor = Column::new()
|
||||
.push(text("Descriptor:").bold())
|
||||
.push(
|
||||
form::Form::new("Descriptor", imported_descriptor, |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(msg))
|
||||
})
|
||||
.warning("Please enter correct descriptor")
|
||||
.size(20)
|
||||
.padding(10),
|
||||
)
|
||||
.spacing(10);
|
||||
layout(
|
||||
Column::new()
|
||||
.push(text("Import the wallet").bold().size(50))
|
||||
.push(
|
||||
Column::new()
|
||||
.spacing(20)
|
||||
.push(row_network)
|
||||
.push(col_descriptor),
|
||||
)
|
||||
.push(if !imported_descriptor.value.is_empty() {
|
||||
button::primary(None, "Next").width(Length::Units(200))
|
||||
} else {
|
||||
button::primary(None, "Next")
|
||||
.width(Length::Units(200))
|
||||
.on_press(Message::Next)
|
||||
})
|
||||
.push_maybe(error.map(|e| card::error("Invalid descriptor", e.to_string())))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.padding(100)
|
||||
.spacing(50)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn register_descriptor<'a>(
|
||||
descriptor: String,
|
||||
hws: &[(HardwareWallet, Option<[u8; 32]>)],
|
||||
@ -385,9 +465,9 @@ pub fn hardware_wallet_xpubs_modal<'a>(
|
||||
Column::new()
|
||||
.push(
|
||||
text(if is_heir {
|
||||
"Import the Heir xpub"
|
||||
"Import the recovery public key"
|
||||
} else {
|
||||
"Import the user xpub"
|
||||
"Import the user public key"
|
||||
})
|
||||
.bold()
|
||||
.size(50),
|
||||
|
||||
@ -13,6 +13,14 @@ fn icon(unicode: char) -> Text<'static> {
|
||||
.size(20)
|
||||
}
|
||||
|
||||
pub fn import_icon() -> Text<'static> {
|
||||
icon('\u{F30A}')
|
||||
}
|
||||
|
||||
pub fn wallet_icon() -> Text<'static> {
|
||||
icon('\u{F615}')
|
||||
}
|
||||
|
||||
pub fn hourglass_icon() -> Text<'static> {
|
||||
icon('\u{F41F}')
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user