Move all network setup in launcher
This commit is contained in:
parent
be768b1a67
commit
9ae33408cb
@ -1,7 +1,4 @@
|
||||
use liana::miniscript::{
|
||||
bitcoin::{bip32::Fingerprint, Network},
|
||||
DescriptorPublicKey,
|
||||
};
|
||||
use liana::miniscript::{bitcoin::bip32::Fingerprint, DescriptorPublicKey};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::Error;
|
||||
@ -29,7 +26,6 @@ pub enum Message {
|
||||
Select(usize),
|
||||
UseHotSigner,
|
||||
Installed(Result<PathBuf, Error>),
|
||||
Network(Network),
|
||||
CreateTaprootDescriptor(bool),
|
||||
SelectBitcoindType(SelectBitcoindTypeMsg),
|
||||
InternalBitcoind(InternalBitcoindMsg),
|
||||
|
||||
@ -29,6 +29,7 @@ use step::{
|
||||
};
|
||||
|
||||
pub struct Installer {
|
||||
network: bitcoin::Network,
|
||||
current: usize,
|
||||
steps: Vec<Box<dyn Step>>,
|
||||
hws: HardwareWallets,
|
||||
@ -61,6 +62,7 @@ impl Installer {
|
||||
) -> (Installer, Command<Message>) {
|
||||
(
|
||||
Installer {
|
||||
network,
|
||||
current: 0,
|
||||
hws: HardwareWallets::new(destination_path.clone(), network),
|
||||
steps: vec![Welcome::default().into()],
|
||||
@ -135,7 +137,7 @@ impl Installer {
|
||||
Message::CreateWallet => {
|
||||
self.steps = vec![
|
||||
Welcome::default().into(),
|
||||
DefineDescriptor::new(self.signer.clone()).into(),
|
||||
DefineDescriptor::new(self.network, self.signer.clone()).into(),
|
||||
BackupMnemonic::new(self.signer.clone()).into(),
|
||||
BackupDescriptor::default().into(),
|
||||
RegisterDescriptor::new_create_wallet().into(),
|
||||
@ -149,8 +151,8 @@ impl Installer {
|
||||
Message::ParticipateWallet => {
|
||||
self.steps = vec![
|
||||
Welcome::default().into(),
|
||||
ParticipateXpub::new(self.signer.clone()).into(),
|
||||
ImportDescriptor::new(false).into(),
|
||||
ParticipateXpub::new(self.network, self.signer.clone()).into(),
|
||||
ImportDescriptor::new(self.network).into(),
|
||||
BackupMnemonic::new(self.signer.clone()).into(),
|
||||
RegisterDescriptor::new_import_wallet().into(),
|
||||
SelectBitcoindTypeStep::new().into(),
|
||||
@ -163,7 +165,7 @@ impl Installer {
|
||||
Message::ImportWallet => {
|
||||
self.steps = vec![
|
||||
Welcome::default().into(),
|
||||
ImportDescriptor::new(true).into(),
|
||||
ImportDescriptor::new(self.network).into(),
|
||||
RecoverMnemonic::default().into(),
|
||||
RegisterDescriptor::new_import_wallet().into(),
|
||||
SelectBitcoindTypeStep::new().into(),
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::iter::FromIterator;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@ -196,11 +195,9 @@ impl Setup {
|
||||
}
|
||||
|
||||
pub struct DefineDescriptor {
|
||||
data_dir: Option<PathBuf>,
|
||||
setup: HashMap<Network, Setup>,
|
||||
setup: Setup,
|
||||
|
||||
network: Network,
|
||||
network_valid: bool,
|
||||
use_taproot: bool,
|
||||
|
||||
modal: Option<Box<dyn DescriptorEditModal>>,
|
||||
@ -210,13 +207,11 @@ pub struct DefineDescriptor {
|
||||
}
|
||||
|
||||
impl DefineDescriptor {
|
||||
pub fn new(signer: Arc<Mutex<Signer>>) -> Self {
|
||||
pub fn new(network: Network, signer: Arc<Mutex<Signer>>) -> Self {
|
||||
Self {
|
||||
network: Network::Bitcoin,
|
||||
network,
|
||||
use_taproot: false,
|
||||
setup: HashMap::from([(Network::Bitcoin, Setup::new())]),
|
||||
data_dir: None,
|
||||
network_valid: true,
|
||||
setup: Setup::new(),
|
||||
modal: None,
|
||||
signer,
|
||||
error: None,
|
||||
@ -224,25 +219,10 @@ impl DefineDescriptor {
|
||||
}
|
||||
|
||||
fn valid(&self) -> bool {
|
||||
self.setup[&self.network].valid()
|
||||
self.setup.valid()
|
||||
}
|
||||
fn setup_mut(&mut self) -> &mut Setup {
|
||||
self.setup
|
||||
.get_mut(&self.network)
|
||||
.expect("There is always one")
|
||||
}
|
||||
|
||||
fn set_network(&mut self, network: Network) {
|
||||
self.network = network;
|
||||
if self.setup.get(&self.network).is_none() {
|
||||
self.setup.insert(self.network, Setup::new());
|
||||
}
|
||||
self.signer.lock().unwrap().set_network(network);
|
||||
if let Some(mut network_datadir) = self.data_dir.clone() {
|
||||
network_datadir.push(self.network.to_string());
|
||||
self.network_valid = !network_datadir.exists();
|
||||
}
|
||||
self.check_setup()
|
||||
&mut self.setup
|
||||
}
|
||||
|
||||
fn check_setup(&mut self) {
|
||||
@ -257,16 +237,11 @@ impl Step for DefineDescriptor {
|
||||
// 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, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
||||
let network = self.network;
|
||||
self.error = None;
|
||||
match message {
|
||||
Message::Close => {
|
||||
self.modal = None;
|
||||
}
|
||||
Message::Network(network) => {
|
||||
hws.set_network(network);
|
||||
self.set_network(network)
|
||||
}
|
||||
Message::CreateTaprootDescriptor(use_taproot) => {
|
||||
self.use_taproot = use_taproot;
|
||||
self.check_setup();
|
||||
@ -313,6 +288,7 @@ impl Step for DefineDescriptor {
|
||||
}
|
||||
message::DefineKey::Edit => {
|
||||
let use_taproot = self.use_taproot;
|
||||
let network = self.network;
|
||||
let setup = self.setup_mut();
|
||||
let modal = EditXpubModal::new(
|
||||
use_taproot,
|
||||
@ -424,7 +400,7 @@ impl Step for DefineDescriptor {
|
||||
j,
|
||||
self.network,
|
||||
self.signer.clone(),
|
||||
self.setup[&self.network].keys.clone(),
|
||||
self.setup.keys.clone(),
|
||||
);
|
||||
let cmd = modal.load();
|
||||
self.modal = Some(Box::new(modal));
|
||||
@ -459,11 +435,6 @@ impl Step for DefineDescriptor {
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn load_context(&mut self, ctx: &Context) {
|
||||
self.data_dir = Some(ctx.data_dir.clone());
|
||||
self.set_network(ctx.bitcoin_config.network)
|
||||
}
|
||||
|
||||
fn subscription(&self, hws: &HardwareWallets) -> Subscription<Message> {
|
||||
if let Some(modal) = &self.modal {
|
||||
modal.subscription(hws)
|
||||
@ -478,9 +449,10 @@ impl Step for DefineDescriptor {
|
||||
let mut hw_is_used = false;
|
||||
let mut spending_keys: Vec<DescriptorPublicKey> = Vec::new();
|
||||
let mut key_derivation_index = HashMap::<Fingerprint, usize>::new();
|
||||
for spending_key in self.setup[&self.network].spending_keys.iter().clone() {
|
||||
for spending_key in self.setup.spending_keys.iter().clone() {
|
||||
let fingerprint = spending_key.expect("Must be present at this step");
|
||||
let key = self.setup[&self.network]
|
||||
let key = self
|
||||
.setup
|
||||
.keys
|
||||
.iter()
|
||||
.find(|key| key.key.master_fingerprint() == fingerprint)
|
||||
@ -506,11 +478,12 @@ impl Step for DefineDescriptor {
|
||||
|
||||
let mut recovery_paths = BTreeMap::new();
|
||||
|
||||
for path in &self.setup[&self.network].recovery_paths {
|
||||
for path in &self.setup.recovery_paths {
|
||||
let mut recovery_keys: Vec<DescriptorPublicKey> = Vec::new();
|
||||
for recovery_key in path.keys.iter().clone() {
|
||||
let fingerprint = recovery_key.expect("Must be present at this step");
|
||||
let key = self.setup[&self.network]
|
||||
let key = self
|
||||
.setup
|
||||
.keys
|
||||
.iter()
|
||||
.find(|key| key.key.master_fingerprint() == fingerprint)
|
||||
@ -544,14 +517,14 @@ impl Step for DefineDescriptor {
|
||||
recovery_paths.insert(path.sequence, recovery_keys);
|
||||
}
|
||||
|
||||
if !self.network_valid || spending_keys.is_empty() {
|
||||
if spending_keys.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let spending_keys = if spending_keys.len() == 1 {
|
||||
PathInfo::Single(spending_keys[0].clone())
|
||||
} else {
|
||||
PathInfo::Multi(self.setup[&self.network].spending_threshold, spending_keys)
|
||||
PathInfo::Multi(self.setup.spending_threshold, spending_keys)
|
||||
};
|
||||
|
||||
let policy = match if self.use_taproot {
|
||||
@ -576,13 +549,11 @@ impl Step for DefineDescriptor {
|
||||
hws: &'a HardwareWallets,
|
||||
progress: (usize, usize),
|
||||
) -> Element<'a, Message> {
|
||||
let aliases = self.setup[&self.network].keys_aliases();
|
||||
let aliases = self.setup.keys_aliases();
|
||||
let content = view::define_descriptor(
|
||||
progress,
|
||||
self.network,
|
||||
self.network_valid,
|
||||
self.use_taproot,
|
||||
self.setup[&self.network]
|
||||
self.setup
|
||||
.spending_keys
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -590,10 +561,8 @@ impl Step for DefineDescriptor {
|
||||
if let Some(key) = key {
|
||||
view::defined_descriptor_key(
|
||||
aliases.get(key).unwrap().to_string(),
|
||||
self.setup[&self.network].duplicate_name.contains(key),
|
||||
self.setup[&self.network]
|
||||
.incompatible_with_tapminiscript
|
||||
.contains(key),
|
||||
self.setup.duplicate_name.contains(key),
|
||||
self.setup.incompatible_with_tapminiscript.contains(key),
|
||||
)
|
||||
} else {
|
||||
view::undefined_descriptor_key()
|
||||
@ -605,16 +574,16 @@ impl Step for DefineDescriptor {
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
self.setup[&self.network].spending_threshold,
|
||||
self.setup[&self.network]
|
||||
self.setup.spending_threshold,
|
||||
self.setup
|
||||
.recovery_paths
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, path)| {
|
||||
path.view(
|
||||
&aliases,
|
||||
&self.setup[&self.network].duplicate_name,
|
||||
&self.setup[&self.network].incompatible_with_tapminiscript,
|
||||
&self.setup.duplicate_name,
|
||||
&self.setup.incompatible_with_tapminiscript,
|
||||
)
|
||||
.map(move |msg| {
|
||||
Message::DefineDescriptor(message::DefineDescriptor::RecoveryPath(i, msg))
|
||||
@ -1133,13 +1102,6 @@ pub struct HardwareWalletXpubs {
|
||||
error: Option<Error>,
|
||||
}
|
||||
|
||||
impl HardwareWalletXpubs {
|
||||
fn reset(&mut self) {
|
||||
self.error = None;
|
||||
self.xpubs = Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignerXpubs {
|
||||
signer: Arc<Mutex<Signer>>,
|
||||
xpubs: Vec<String>,
|
||||
@ -1155,11 +1117,6 @@ impl SignerXpubs {
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.xpubs = Vec::new();
|
||||
self.next_account = ChildNumber::from_hardened_idx(0).unwrap();
|
||||
}
|
||||
|
||||
fn select(&mut self, network: Network) {
|
||||
self.next_account = self.next_account.increment().unwrap();
|
||||
let signer = self.signer.lock().unwrap();
|
||||
@ -1180,8 +1137,6 @@ impl SignerXpubs {
|
||||
|
||||
pub struct ParticipateXpub {
|
||||
network: Network,
|
||||
network_valid: bool,
|
||||
data_dir: Option<PathBuf>,
|
||||
|
||||
shared: bool,
|
||||
|
||||
@ -1190,33 +1145,14 @@ pub struct ParticipateXpub {
|
||||
}
|
||||
|
||||
impl ParticipateXpub {
|
||||
pub fn new(signer: Arc<Mutex<Signer>>) -> Self {
|
||||
pub fn new(network: Network, signer: Arc<Mutex<Signer>>) -> Self {
|
||||
Self {
|
||||
network: Network::Bitcoin,
|
||||
network_valid: true,
|
||||
data_dir: None,
|
||||
network,
|
||||
hw_xpubs: Vec::new(),
|
||||
shared: false,
|
||||
xpubs_signer: SignerXpubs::new(signer),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_network(&mut self, network: Network) {
|
||||
if network != self.network {
|
||||
self.hw_xpubs.iter_mut().for_each(|hw| hw.reset());
|
||||
self.xpubs_signer.reset();
|
||||
}
|
||||
self.network = network;
|
||||
self.xpubs_signer
|
||||
.signer
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_network(network);
|
||||
if let Some(mut network_datadir) = self.data_dir.clone() {
|
||||
network_datadir.push(self.network.to_string());
|
||||
self.network_valid = !network_datadir.exists();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Step for ParticipateXpub {
|
||||
@ -1224,10 +1160,6 @@ impl Step for ParticipateXpub {
|
||||
// Verification of the values is happening when the user click on Next button.
|
||||
fn update(&mut self, hws: &mut HardwareWallets, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::Network(network) => {
|
||||
hws.set_network(network);
|
||||
self.set_network(network);
|
||||
}
|
||||
Message::UserActionDone(shared) => self.shared = shared,
|
||||
Message::ImportXpub(fg, res) => {
|
||||
if let Some(hw_xpubs) = self.hw_xpubs.iter_mut().find(|x| x.fingerprint == fg) {
|
||||
@ -1292,11 +1224,6 @@ impl Step for ParticipateXpub {
|
||||
hws.refresh().map(Message::HardwareWallets)
|
||||
}
|
||||
|
||||
fn load_context(&mut self, ctx: &Context) {
|
||||
self.data_dir = Some(ctx.data_dir.clone());
|
||||
self.set_network(ctx.bitcoin_config.network);
|
||||
}
|
||||
|
||||
fn apply(&mut self, ctx: &mut Context) -> bool {
|
||||
ctx.bitcoin_config.network = self.network;
|
||||
// Drop connections to hardware wallets.
|
||||
@ -1307,8 +1234,6 @@ impl Step for ParticipateXpub {
|
||||
fn view<'a>(&'a self, hws: &'a HardwareWallets, progress: (usize, usize)) -> Element<Message> {
|
||||
view::participate_xpub(
|
||||
progress,
|
||||
self.network,
|
||||
self.network_valid,
|
||||
hws.list
|
||||
.iter()
|
||||
.enumerate()
|
||||
@ -1344,21 +1269,15 @@ impl From<ParticipateXpub> for Box<dyn Step> {
|
||||
|
||||
pub struct ImportDescriptor {
|
||||
network: Network,
|
||||
network_valid: bool,
|
||||
change_network: bool,
|
||||
data_dir: Option<PathBuf>,
|
||||
imported_descriptor: form::Value<String>,
|
||||
wrong_network: bool,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
impl ImportDescriptor {
|
||||
pub fn new(change_network: bool) -> Self {
|
||||
pub fn new(network: Network) -> Self {
|
||||
Self {
|
||||
change_network,
|
||||
network: Network::Bitcoin,
|
||||
network_valid: true,
|
||||
data_dir: None,
|
||||
network,
|
||||
imported_descriptor: form::Value::default(),
|
||||
wrong_network: false,
|
||||
error: None,
|
||||
@ -1397,32 +1316,13 @@ 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, _hws: &mut HardwareWallets, 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();
|
||||
self.check_descriptor(self.network);
|
||||
}
|
||||
Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) => {
|
||||
self.imported_descriptor.value = desc;
|
||||
self.check_descriptor(self.network);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn load_context(&mut self, ctx: &Context) {
|
||||
if ctx.bitcoin_config.network != self.network {
|
||||
self.check_descriptor(ctx.bitcoin_config.network);
|
||||
if let Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) =
|
||||
message
|
||||
{
|
||||
self.imported_descriptor.value = desc;
|
||||
self.check_descriptor(self.network);
|
||||
}
|
||||
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();
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn apply(&mut self, ctx: &mut Context) -> bool {
|
||||
@ -1441,9 +1341,6 @@ impl Step for ImportDescriptor {
|
||||
fn view(&self, _hws: &HardwareWallets, progress: (usize, usize)) -> Element<Message> {
|
||||
view::import_descriptor(
|
||||
progress,
|
||||
self.change_network,
|
||||
self.network,
|
||||
self.network_valid,
|
||||
&self.imported_descriptor,
|
||||
self.wrong_network,
|
||||
self.error.as_ref(),
|
||||
@ -1650,6 +1547,7 @@ impl From<BackupDescriptor> for Box<dyn Step> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use iced_runtime::command::Action;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub struct Sandbox<S: Step> {
|
||||
@ -1686,9 +1584,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_define_descriptor_use_hotkey() {
|
||||
let mut ctx = Context::new(Network::Signet, PathBuf::from_str("/").unwrap());
|
||||
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(Arc::new(
|
||||
Mutex::new(Signer::generate(Network::Bitcoin).unwrap()),
|
||||
)));
|
||||
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(
|
||||
Network::Bitcoin,
|
||||
Arc::new(Mutex::new(Signer::generate(Network::Bitcoin).unwrap())),
|
||||
));
|
||||
|
||||
// Edit primary key
|
||||
sandbox
|
||||
@ -1767,9 +1666,10 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_define_descriptor_stores_if_hw_is_used() {
|
||||
let mut ctx = Context::new(Network::Testnet, PathBuf::from_str("/").unwrap());
|
||||
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(Arc::new(
|
||||
Mutex::new(Signer::generate(Network::Testnet).unwrap()),
|
||||
)));
|
||||
let sandbox: Sandbox<DefineDescriptor> = Sandbox::new(DefineDescriptor::new(
|
||||
Network::Testnet,
|
||||
Arc::new(Mutex::new(Signer::generate(Network::Testnet).unwrap())),
|
||||
));
|
||||
sandbox.load(&ctx).await;
|
||||
|
||||
let specter_key = message::DefinePath::Key(
|
||||
|
||||
@ -32,55 +32,6 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Network {
|
||||
Mainnet,
|
||||
Testnet,
|
||||
Regtest,
|
||||
Signet,
|
||||
}
|
||||
|
||||
impl From<bitcoin::Network> for Network {
|
||||
fn from(n: bitcoin::Network) -> Self {
|
||||
match n {
|
||||
bitcoin::Network::Bitcoin => Network::Mainnet,
|
||||
bitcoin::Network::Testnet => Network::Testnet,
|
||||
bitcoin::Network::Regtest => Network::Regtest,
|
||||
bitcoin::Network::Signet => Network::Signet,
|
||||
_ => Network::Mainnet,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Network> for bitcoin::Network {
|
||||
fn from(network: Network) -> bitcoin::Network {
|
||||
match network {
|
||||
Network::Mainnet => bitcoin::Network::Bitcoin,
|
||||
Network::Testnet => bitcoin::Network::Testnet,
|
||||
Network::Regtest => bitcoin::Network::Regtest,
|
||||
Network::Signet => bitcoin::Network::Signet,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Network {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Mainnet => write!(f, "Bitcoin mainnet"),
|
||||
Self::Testnet => write!(f, "Bitcoin testnet"),
|
||||
Self::Regtest => write!(f, "Bitcoin regtest"),
|
||||
Self::Signet => write!(f, "Bitcoin signet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const NETWORKS: [Network; 4] = [
|
||||
Network::Mainnet,
|
||||
Network::Testnet,
|
||||
Network::Signet,
|
||||
Network::Regtest,
|
||||
];
|
||||
|
||||
pub fn welcome<'a>() -> Element<'a, Message> {
|
||||
Container::new(
|
||||
Column::new()
|
||||
@ -191,31 +142,7 @@ impl std::fmt::Display for DescriptorKind {
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn define_descriptor_advanced_settings<'a>(
|
||||
network: bitcoin::Network,
|
||||
network_valid: bool,
|
||||
use_taproot: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let col_network = Column::new()
|
||||
.spacing(10)
|
||||
.push(text("Network").bold())
|
||||
.push(container(
|
||||
pick_list(&NETWORKS[..], Some(Network::from(network)), |net| {
|
||||
Message::Network(net.into())
|
||||
})
|
||||
.style(if network_valid {
|
||||
theme::PickList::Secondary
|
||||
} else {
|
||||
theme::PickList::Invalid
|
||||
})
|
||||
.padding(10),
|
||||
))
|
||||
.push_maybe(if network_valid {
|
||||
None
|
||||
} else {
|
||||
Some(text("A data directory already exists for this network").style(color::RED))
|
||||
});
|
||||
|
||||
pub fn define_descriptor_advanced_settings<'a>(use_taproot: bool) -> Element<'a, Message> {
|
||||
let col_wallet = Column::new()
|
||||
.spacing(10)
|
||||
.push(text("Descriptor type").bold())
|
||||
@ -238,12 +165,7 @@ pub fn define_descriptor_advanced_settings<'a>(
|
||||
.spacing(20)
|
||||
.push(Space::with_height(0))
|
||||
.push(separation().width(500))
|
||||
.push(
|
||||
Row::new()
|
||||
.push(col_network)
|
||||
.push(Space::with_width(100))
|
||||
.push(col_wallet),
|
||||
)
|
||||
.push(Row::new().push(col_wallet))
|
||||
.push_maybe(if use_taproot {
|
||||
Some(
|
||||
p1_regular("Taproot is only supported by Liana version 5.0 and above")
|
||||
@ -259,8 +181,6 @@ pub fn define_descriptor_advanced_settings<'a>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn define_descriptor<'a>(
|
||||
progress: (usize, usize),
|
||||
network: bitcoin::Network,
|
||||
network_valid: bool,
|
||||
use_taproot: bool,
|
||||
spending_keys: Vec<Element<'a, Message>>,
|
||||
spending_threshold: usize,
|
||||
@ -329,34 +249,29 @@ pub fn define_descriptor<'a>(
|
||||
progress,
|
||||
"Create the wallet",
|
||||
Column::new()
|
||||
.push(
|
||||
collapse::Collapse::new(
|
||||
|| {
|
||||
Button::new(
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.push(text("Advanced settings").small().bold())
|
||||
.push(icon::collapse_icon()),
|
||||
)
|
||||
.style(theme::Button::Transparent)
|
||||
},
|
||||
|| {
|
||||
Button::new(
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.push(text("Advanced settings").small().bold())
|
||||
.push(icon::collapsed_icon()),
|
||||
)
|
||||
.style(theme::Button::Transparent)
|
||||
},
|
||||
move || {
|
||||
define_descriptor_advanced_settings(network, network_valid, use_taproot)
|
||||
},
|
||||
)
|
||||
.collapsed(!network_valid),
|
||||
)
|
||||
.push(collapse::Collapse::new(
|
||||
|| {
|
||||
Button::new(
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.push(text("Advanced settings").small().bold())
|
||||
.push(icon::collapse_icon()),
|
||||
)
|
||||
.style(theme::Button::Transparent)
|
||||
},
|
||||
|| {
|
||||
Button::new(
|
||||
Row::new()
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(10)
|
||||
.push(text("Advanced settings").small().bold())
|
||||
.push(icon::collapsed_icon()),
|
||||
)
|
||||
.style(theme::Button::Transparent)
|
||||
},
|
||||
move || define_descriptor_advanced_settings(use_taproot),
|
||||
))
|
||||
.push(
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
@ -384,7 +299,7 @@ pub fn define_descriptor<'a>(
|
||||
))
|
||||
.width(Length::Fixed(200.0)),
|
||||
)
|
||||
.push(if !valid || !network_valid {
|
||||
.push(if !valid {
|
||||
button::primary(None, "Next").width(Length::Fixed(200.0))
|
||||
} else {
|
||||
button::primary(None, "Next")
|
||||
@ -455,33 +370,10 @@ pub fn recovery_path_view(
|
||||
|
||||
pub fn import_descriptor<'a>(
|
||||
progress: (usize, usize),
|
||||
change_network: bool,
|
||||
network: bitcoin::Network,
|
||||
network_valid: bool,
|
||||
imported_descriptor: &form::Value<String>,
|
||||
wrong_network: bool,
|
||||
error: Option<&String>,
|
||||
) -> Element<'a, Message> {
|
||||
let row_network = Row::new()
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center)
|
||||
.push(text("Network:").bold())
|
||||
.push(Container::new(
|
||||
pick_list(&NETWORKS[..], Some(Network::from(network)), |net| {
|
||||
Message::Network(net.into())
|
||||
})
|
||||
.style(if network_valid {
|
||||
theme::PickList::Simple
|
||||
} else {
|
||||
theme::PickList::Invalid
|
||||
})
|
||||
.padding(10),
|
||||
))
|
||||
.push_maybe(if network_valid {
|
||||
None
|
||||
} else {
|
||||
Some(text("A data directory already exists for this network").style(color::RED))
|
||||
});
|
||||
let col_descriptor = Column::new()
|
||||
.push(text("Descriptor:").bold())
|
||||
.push(
|
||||
@ -501,28 +393,13 @@ pub fn import_descriptor<'a>(
|
||||
progress,
|
||||
"Import the wallet",
|
||||
Column::new()
|
||||
.push(
|
||||
Column::new()
|
||||
.spacing(20)
|
||||
.push_maybe(if change_network {
|
||||
Some(row_network)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.push(col_descriptor)
|
||||
.push_maybe(if change_network {
|
||||
// only show message when importing a descriptor
|
||||
Some(text(
|
||||
"After creating the wallet, \
|
||||
.push(Column::new().spacing(20).push(col_descriptor).push(text(
|
||||
"After creating the wallet, \
|
||||
you will need to perform a rescan of \
|
||||
the blockchain in order to see your \
|
||||
coins and past transactions. This can \
|
||||
be done in Settings > Bitcoin Core.",
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
)
|
||||
)))
|
||||
.push(
|
||||
if imported_descriptor.value.is_empty() || !imported_descriptor.valid {
|
||||
button::primary(None, "Next").width(Length::Fixed(200.0))
|
||||
@ -684,43 +561,14 @@ pub fn hardware_wallet_xpubs<'a>(
|
||||
|
||||
pub fn participate_xpub<'a>(
|
||||
progress: (usize, usize),
|
||||
network: bitcoin::Network,
|
||||
network_valid: bool,
|
||||
hws: Vec<Element<'a, Message>>,
|
||||
signer: Element<'a, Message>,
|
||||
shared: bool,
|
||||
) -> Element<'a, Message> {
|
||||
let row_network = Row::new()
|
||||
.spacing(10)
|
||||
.align_items(Alignment::Center)
|
||||
.push(text("Network:").bold())
|
||||
.push(Container::new(
|
||||
pick_list(&NETWORKS[..], Some(Network::from(network)), |net| {
|
||||
Message::Network(net.into())
|
||||
})
|
||||
.style(if network_valid {
|
||||
theme::PickList::Simple
|
||||
} else {
|
||||
theme::PickList::Invalid
|
||||
})
|
||||
.padding(10),
|
||||
))
|
||||
.push_maybe(if network_valid {
|
||||
None
|
||||
} else {
|
||||
Some(text("A data directory already exists for this network").style(color::RED))
|
||||
});
|
||||
|
||||
layout(
|
||||
progress,
|
||||
"Share your public keys",
|
||||
Column::new()
|
||||
.push(
|
||||
Column::new()
|
||||
.spacing(20)
|
||||
.width(Length::Fill)
|
||||
.push(row_network),
|
||||
)
|
||||
.push(
|
||||
Column::new()
|
||||
.push(
|
||||
@ -739,7 +587,7 @@ pub fn participate_xpub<'a>(
|
||||
checkbox("I have shared my extended public key", shared)
|
||||
.on_toggle(Message::UserActionDone),
|
||||
)
|
||||
.push(if shared && network_valid {
|
||||
.push(if shared {
|
||||
button::primary(None, "Next")
|
||||
.width(Length::Fixed(200.0))
|
||||
.on_press(Message::Next)
|
||||
|
||||
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use iced::{
|
||||
alignment::Horizontal,
|
||||
widget::{tooltip, Space},
|
||||
widget::{scrollable, tooltip},
|
||||
Alignment, Command, Length, Subscription,
|
||||
};
|
||||
|
||||
@ -28,33 +28,37 @@ fn wallet_name(network: &Network) -> String {
|
||||
}
|
||||
|
||||
pub struct Launcher {
|
||||
choices: Vec<Network>,
|
||||
// true if installed
|
||||
choices: Vec<(Network, bool)>,
|
||||
datadir_path: PathBuf,
|
||||
error: Option<String>,
|
||||
delete_wallet_modal: Option<DeleteWalletModal>,
|
||||
collapsed: bool,
|
||||
}
|
||||
|
||||
impl Launcher {
|
||||
pub fn new(datadir_path: PathBuf) -> Self {
|
||||
let mut choices = Vec::new();
|
||||
for network in [
|
||||
Network::Bitcoin,
|
||||
Network::Testnet,
|
||||
Network::Signet,
|
||||
Network::Regtest,
|
||||
] {
|
||||
if datadir_path.join(network.to_string()).exists() {
|
||||
choices.push(network)
|
||||
}
|
||||
}
|
||||
Self {
|
||||
choices: [
|
||||
Network::Bitcoin,
|
||||
Network::Testnet,
|
||||
Network::Signet,
|
||||
Network::Regtest,
|
||||
]
|
||||
.iter()
|
||||
.map(|net| (*net, datadir_path.join(net.to_string()).exists()))
|
||||
.collect(),
|
||||
datadir_path,
|
||||
choices,
|
||||
error: None,
|
||||
delete_wallet_modal: None,
|
||||
collapsed: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_fresh_install(&self) -> bool {
|
||||
!self.choices.iter().any(|(_, installed)| *installed)
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
@ -63,9 +67,15 @@ impl Launcher {
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Command<Message> {
|
||||
match message {
|
||||
Message::View(ViewMessage::StartInstall) => {
|
||||
Message::View(ViewMessage::ShowUninstalledNetworks) => {
|
||||
self.collapsed = true;
|
||||
Command::none()
|
||||
}
|
||||
Message::View(ViewMessage::StartInstall(net)) => {
|
||||
let datadir_path = self.datadir_path.clone();
|
||||
Command::perform(async move { datadir_path }, Message::Install)
|
||||
Command::perform(async move { (datadir_path, net) }, |(d, n)| {
|
||||
Message::Install(d, n)
|
||||
})
|
||||
}
|
||||
Message::View(ViewMessage::Check(network)) => Command::perform(
|
||||
check_network_datadir(self.datadir_path.clone(), network),
|
||||
@ -88,11 +98,9 @@ impl Launcher {
|
||||
}
|
||||
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Deleted)) => {
|
||||
if let Some(modal) = &self.delete_wallet_modal {
|
||||
let choices = self.choices.clone();
|
||||
self.choices = choices
|
||||
.into_iter()
|
||||
.filter(|c| c != &modal.network)
|
||||
.collect();
|
||||
if let Some(choice) = self.choices.iter_mut().find(|c| c.0 == modal.network) {
|
||||
choice.1 = false;
|
||||
}
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
@ -126,7 +134,7 @@ impl Launcher {
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<Message> {
|
||||
let content = Into::<Element<ViewMessage>>::into(
|
||||
let content = Into::<Element<ViewMessage>>::into(scrollable(
|
||||
Column::new()
|
||||
.push(
|
||||
Container::new(image::liana_brand_grey().width(Length::Fixed(200.0)))
|
||||
@ -136,17 +144,107 @@ impl Launcher {
|
||||
Container::new(
|
||||
Column::new()
|
||||
.spacing(30)
|
||||
.push(text("Welcome back").size(50).bold())
|
||||
.push(if !self.is_fresh_install() {
|
||||
text("Welcome back").size(50).bold()
|
||||
} else {
|
||||
text("Welcome").size(50).bold()
|
||||
})
|
||||
.push_maybe(self.error.as_ref().map(|e| card::simple(text(e))))
|
||||
.push(
|
||||
self.choices
|
||||
.iter()
|
||||
.fold(
|
||||
Column::new()
|
||||
.push(text("Select network:").small().bold())
|
||||
.spacing(10),
|
||||
|col, choice| {
|
||||
col.push(
|
||||
.push(if self.is_fresh_install() {
|
||||
Column::new()
|
||||
.spacing(10)
|
||||
.push(
|
||||
Button::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.push(
|
||||
badge::Badge::new(icon::bitcoin_icon())
|
||||
.style(theme::Badge::Bitcoin),
|
||||
)
|
||||
.push(text(format!(
|
||||
"Create wallet on {}",
|
||||
wallet_name(&Network::Bitcoin)
|
||||
))),
|
||||
)
|
||||
.on_press(ViewMessage::StartInstall(Network::Bitcoin))
|
||||
.padding(10)
|
||||
.width(Length::Fixed(400.0))
|
||||
.style(theme::Button::Border),
|
||||
)
|
||||
.push(if !self.collapsed {
|
||||
Column::new().push(
|
||||
Button::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.push(badge::Badge::new(icon::plus_icon()))
|
||||
.push(text("Create wallet on another network")),
|
||||
)
|
||||
.on_press(ViewMessage::ShowUninstalledNetworks)
|
||||
.padding(10)
|
||||
.width(Length::Fixed(400.0))
|
||||
.style(theme::Button::TransparentBorder),
|
||||
)
|
||||
} else {
|
||||
self.choices
|
||||
.iter()
|
||||
.filter_map(|(net, installed)| {
|
||||
if *installed || *net == Network::Bitcoin {
|
||||
None
|
||||
} else {
|
||||
Some(net)
|
||||
}
|
||||
})
|
||||
.fold(Column::new().spacing(10), |col, choice| {
|
||||
col.push(
|
||||
Button::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.push(
|
||||
badge::Badge::new(
|
||||
icon::bitcoin_icon(),
|
||||
)
|
||||
.style(theme::Badge::Standard),
|
||||
)
|
||||
.push(text(format!(
|
||||
"Create wallet on {}",
|
||||
wallet_name(choice)
|
||||
))),
|
||||
)
|
||||
.on_press(ViewMessage::StartInstall(*choice))
|
||||
.padding(10)
|
||||
.width(Length::Fixed(400.0))
|
||||
.style(theme::Button::Border),
|
||||
)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
Column::new()
|
||||
.spacing(10)
|
||||
.push(
|
||||
self.choices
|
||||
.iter()
|
||||
.filter_map(
|
||||
|(net, installed)| {
|
||||
if *installed {
|
||||
Some(net)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.fold(
|
||||
Column::new()
|
||||
.push(
|
||||
text("Open an existing wallet:")
|
||||
.small()
|
||||
.bold(),
|
||||
)
|
||||
.spacing(10),
|
||||
|col, choice| {
|
||||
col.push(
|
||||
Row::new()
|
||||
.spacing(10)
|
||||
.push(
|
||||
@ -169,7 +267,7 @@ impl Launcher {
|
||||
)
|
||||
.on_press(ViewMessage::Check(*choice))
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.width(Length::Fixed(400.0))
|
||||
.style(theme::Button::Border),
|
||||
)
|
||||
.push(tooltip::Tooltip::new(
|
||||
@ -187,35 +285,89 @@ impl Launcher {
|
||||
))
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
.push(
|
||||
Button::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.push(badge::Badge::new(icon::plus_icon()))
|
||||
.push(text("Install Liana on another network")),
|
||||
)
|
||||
.on_press(ViewMessage::StartInstall)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.style(theme::Button::TransparentBorder),
|
||||
),
|
||||
)
|
||||
.max_width(500)
|
||||
.align_items(Alignment::Center),
|
||||
if !self.collapsed
|
||||
&& self.choices.iter().any(|(_, installed)| !installed)
|
||||
{
|
||||
Column::new().push(
|
||||
Button::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.push(badge::Badge::new(icon::plus_icon()))
|
||||
.push(text("Create a new wallet")),
|
||||
)
|
||||
.on_press(ViewMessage::ShowUninstalledNetworks)
|
||||
.padding(10)
|
||||
.width(Length::Fixed(400.0))
|
||||
.style(theme::Button::TransparentBorder),
|
||||
)
|
||||
} else if self.collapsed {
|
||||
self.choices
|
||||
.iter()
|
||||
.filter_map(|(net, installed)| {
|
||||
if *installed {
|
||||
None
|
||||
} else {
|
||||
Some(net)
|
||||
}
|
||||
})
|
||||
.fold(
|
||||
Column::new()
|
||||
.push(
|
||||
text("Create a new wallet:")
|
||||
.small()
|
||||
.bold(),
|
||||
)
|
||||
.spacing(10),
|
||||
|col, choice| {
|
||||
col.push(
|
||||
Button::new(
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Alignment::Center)
|
||||
.push(
|
||||
badge::Badge::new(
|
||||
icon::bitcoin_icon(),
|
||||
)
|
||||
.style(match choice {
|
||||
Network::Bitcoin => {
|
||||
theme::Badge::Bitcoin
|
||||
}
|
||||
_ => theme::Badge::Standard,
|
||||
}),
|
||||
)
|
||||
.push(text(wallet_name(choice))),
|
||||
)
|
||||
.on_press(ViewMessage::StartInstall(*choice))
|
||||
.padding(10)
|
||||
.width(Length::Fixed(400.0))
|
||||
.style(theme::Button::Border),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Column::new()
|
||||
},
|
||||
)
|
||||
})
|
||||
.align_items(if self.is_fresh_install() {
|
||||
Alignment::Center
|
||||
} else {
|
||||
Alignment::Start
|
||||
})
|
||||
.max_width(500),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y(),
|
||||
)
|
||||
.push(Space::with_height(Length::Fixed(100.0))),
|
||||
)
|
||||
.center_x(),
|
||||
),
|
||||
))
|
||||
.map(Message::View);
|
||||
if let Some(modal) = &self.delete_wallet_modal {
|
||||
Modal::new(content, modal.view())
|
||||
Modal::new(Container::new(content).height(Length::Fill), modal.view())
|
||||
.on_blur(Some(Message::View(ViewMessage::DeleteWallet(
|
||||
DeleteWalletMessage::CloseModal,
|
||||
))))
|
||||
@ -229,14 +381,15 @@ impl Launcher {
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
View(ViewMessage),
|
||||
Install(PathBuf),
|
||||
Install(PathBuf, Network),
|
||||
Checked(Result<Network, String>),
|
||||
Run(PathBuf, app::config::Config, Network),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ViewMessage {
|
||||
StartInstall,
|
||||
StartInstall(Network),
|
||||
ShowUninstalledNetworks,
|
||||
Check(Network),
|
||||
DeleteWallet(DeleteWalletMessage),
|
||||
}
|
||||
|
||||
@ -198,13 +198,12 @@ impl Application for GUI {
|
||||
}
|
||||
}
|
||||
(State::Launcher(l), Message::Launch(msg)) => match *msg {
|
||||
launcher::Message::Install(datadir_path) => {
|
||||
launcher::Message::Install(datadir_path, network) => {
|
||||
self.logger.set_installer_mode(
|
||||
datadir_path.clone(),
|
||||
self.log_level.unwrap_or(LevelFilter::INFO),
|
||||
);
|
||||
let (install, command) =
|
||||
Installer::new(datadir_path, bitcoin::Network::Bitcoin);
|
||||
let (install, command) = Installer::new(datadir_path, network);
|
||||
self.state = State::Installer(Box::new(install));
|
||||
command.map(|msg| Message::Install(Box::new(msg)))
|
||||
}
|
||||
@ -364,13 +363,6 @@ impl Config {
|
||||
Err(ConfigError::NotFound) => Ok(Config::Install(datadir_path, network)),
|
||||
Err(e) => Err(format!("Failed to read configuration file: {}", e).into()),
|
||||
}
|
||||
} else if !datadir_path.exists()
|
||||
|| (!datadir_path.join("bitcoin").exists()
|
||||
&& !datadir_path.join("testnet").exists()
|
||||
&& !datadir_path.join("signet").exists()
|
||||
&& !datadir_path.join("regtest").exists())
|
||||
{
|
||||
Ok(Config::Install(datadir_path, bitcoin::Network::Bitcoin))
|
||||
} else {
|
||||
Ok(Config::Launcher(datadir_path))
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user