diff --git a/liana-gui/src/backup.rs b/liana-gui/src/backup.rs index 5ff66f63..cb6664d5 100644 --- a/liana-gui/src/backup.rs +++ b/liana-gui/src/backup.rs @@ -148,11 +148,6 @@ impl Backup { }; let name = if let Some(settings) = settings { - assert_eq!(settings.wallets.len(), 1); - if settings.wallets.len() != 1 { - return Err(Error::NotSingleWallet); - } - let settings = settings.wallets.first().expect("only one wallet"); let name = settings.name.clone(); if let Ok(settings) = serde_json::to_value(settings) { proprietary.insert(SETTINGS_KEY.to_string(), settings); diff --git a/liana-gui/src/installer/message.rs b/liana-gui/src/installer/message.rs index c1ee32ca..804d8616 100644 --- a/liana-gui/src/installer/message.rs +++ b/liana-gui/src/installer/message.rs @@ -2,7 +2,7 @@ use liana::miniscript::{ bitcoin::{bip32::Fingerprint, Network}, DescriptorPublicKey, }; -use std::{collections::HashMap, path::PathBuf}; +use std::collections::HashMap; use super::{context, Error}; use crate::{ @@ -28,7 +28,11 @@ use crate::{ #[derive(Debug, Clone)] pub enum Message { UserActionDone(bool), - Exit(PathBuf, Option, /* remove log */ bool), + Exit( + Box, + Option, + /* remove log */ bool, + ), Clibpboard(String), Next, Skip, @@ -39,7 +43,7 @@ pub enum Message { Reload, Select(usize), UseHotSigner, - Installed(Result), + Installed(Result), CreateTaprootDescriptor(bool), SelectDescriptorTemplate(context::DescriptorTemplate), SelectBackend(SelectBackend), diff --git a/liana-gui/src/installer/mod.rs b/liana-gui/src/installer/mod.rs index 1ca0d102..959791ab 100644 --- a/liana-gui/src/installer/mod.rs +++ b/liana-gui/src/installer/mod.rs @@ -23,7 +23,7 @@ use std::sync::{Arc, Mutex}; use crate::{ app::{ config as gui_config, - settings::{self as gui_settings, AuthConfig, Settings, SettingsError, WalletSettings}, + settings::{update_settings_file, AuthConfig, SettingsError, WalletSettings}, wallet::wallet_name, }, backup, @@ -362,7 +362,7 @@ pub fn daemon_check(cfg: lianad::config::Config) -> Result<(), Error> { pub async fn install_local_wallet( ctx: Context, signer: Arc>, -) -> Result { +) -> Result { let network_datadir = ctx .liana_directory .network_directory(ctx.bitcoin_config.network); @@ -412,7 +412,7 @@ pub async fn install_local_wallet( } // create liana GUI configuration file - let gui_config_path = create_and_write_file( + let _gui_config_path = create_and_write_file( &network_datadir, gui_config::DEFAULT_FILE_NAME, toml::to_string(&gui_config::Config::new( @@ -426,25 +426,24 @@ pub async fn install_local_wallet( info!("Gui configuration file created"); // create liana GUI settings file - let settings: gui_settings::Settings = extract_local_gui_settings(&ctx); - create_and_write_file( - &network_datadir, - gui_settings::DEFAULT_FILE_NAME, - serde_json::to_string_pretty(&settings) - .map_err(|e| Error::Unexpected(format!("Failed to serialize settings: {}", e)))? - .as_bytes(), - )?; + let wallet_settings = extract_local_gui_settings(&ctx); + update_settings_file(&network_datadir, |mut settings| { + settings.wallets.push(wallet_settings.clone()); + settings + }) + .await + .map_err(|e| Error::Unexpected(e.to_string()))?; info!("Settings file created"); - Ok(gui_config_path) + Ok(wallet_settings) } pub async fn create_remote_wallet( ctx: Context, signer: Arc>, remote_backend: BackendClient, -) -> Result { +) -> Result { let network_datadir = ctx.liana_directory.network_directory(ctx.network); network_datadir .init() @@ -477,7 +476,7 @@ pub async fn create_remote_wallet( } // create liana GUI configuration file - let gui_config_path = create_and_write_file( + let _gui_config_path = create_and_write_file( &network_datadir, gui_config::DEFAULT_FILE_NAME, toml::to_string(&gui_config::Config { @@ -540,14 +539,13 @@ pub async fn create_remote_wallet( let remote_backend = remote_backend.connect_wallet(wallet).0; // create liana GUI settings file - let settings: gui_settings::Settings = extract_remote_gui_settings(&ctx, &remote_backend).await; - create_and_write_file( - &network_datadir, - gui_settings::DEFAULT_FILE_NAME, - serde_json::to_string_pretty(&settings) - .map_err(|e| Error::Unexpected(format!("Failed to serialize settings: {}", e)))? - .as_bytes(), - )?; + let wallet_settings = extract_remote_gui_settings(&ctx, &remote_backend).await; + update_settings_file(&network_datadir, |mut settings| { + settings.wallets.push(wallet_settings.clone()); + settings + }) + .await + .map_err(|e| Error::Unexpected(e.to_string()))?; info!("Settings file created"); @@ -567,13 +565,13 @@ pub async fn create_remote_wallet( info!("Liana-Connect cache updated"); }; - Ok(gui_config_path) + Ok(wallet_settings) } pub async fn import_remote_wallet( ctx: Context, backend: BackendWalletClient, -) -> Result { +) -> Result { tracing::info!("Importing wallet from remote backend"); if let Some(signer) = &ctx.recovered_signer { @@ -590,19 +588,18 @@ pub async fn import_remote_wallet( .map_err(|e| Error::Unexpected(format!("Failed to create datadir path: {}", e)))?; // create liana GUI settings file - let settings: gui_settings::Settings = extract_remote_gui_settings(&ctx, &backend).await; - create_and_write_file( - &network_datadir, - gui_settings::DEFAULT_FILE_NAME, - serde_json::to_string_pretty(&settings) - .map_err(|e| Error::Unexpected(format!("Failed to serialize settings: {}", e)))? - .as_bytes(), - )?; + let wallet_settings = extract_remote_gui_settings(&ctx, &backend).await; + update_settings_file(&network_datadir, |mut settings| { + settings.wallets.push(wallet_settings.clone()); + settings + }) + .await + .map_err(|e| Error::Unexpected(e.to_string()))?; info!("Settings file created"); // create liana GUI configuration file - let gui_config_path = create_and_write_file( + let _gui_config_path = create_and_write_file( &network_datadir, gui_config::DEFAULT_FILE_NAME, toml::to_string(&gui_config::Config { @@ -632,7 +629,7 @@ pub async fn import_remote_wallet( info!("Liana-Connect cache updated"); }; - Ok(gui_config_path) + Ok(wallet_settings) } pub fn create_and_write_file( @@ -651,7 +648,10 @@ pub fn create_and_write_file( // if the wallet is using the remote backend, then the hardware wallet settings and // keys will be store on the remote backend side and not in the settings file. -pub async fn extract_remote_gui_settings(ctx: &Context, backend: &BackendWalletClient) -> Settings { +pub async fn extract_remote_gui_settings( + ctx: &Context, + backend: &BackendWalletClient, +) -> WalletSettings { let descriptor = ctx .descriptor .as_ref() @@ -664,23 +664,21 @@ pub async fn extract_remote_gui_settings(ctx: &Context, backend: &BackendWalletC .expect("LianaDescriptor.to_string() always include the checksum") .to_string(); - Settings { - wallets: vec![WalletSettings { - name: wallet_name(descriptor), - descriptor_checksum, - pinned_at: Some(chrono::Utc::now().timestamp()), - keys: Vec::new(), - hardware_wallets: Vec::new(), - remote_backend_auth: Some(AuthConfig::new( - backend.user_email().to_string(), - backend.wallet_id(), - )), - start_internal_bitcoind: None, - }], + WalletSettings { + name: wallet_name(descriptor), + descriptor_checksum, + pinned_at: Some(chrono::Utc::now().timestamp()), + keys: Vec::new(), + hardware_wallets: Vec::new(), + remote_backend_auth: Some(AuthConfig::new( + backend.user_email().to_string(), + backend.wallet_id(), + )), + start_internal_bitcoind: None, } } -pub fn extract_local_gui_settings(ctx: &Context) -> Settings { +pub fn extract_local_gui_settings(ctx: &Context) -> WalletSettings { let descriptor = ctx .descriptor .as_ref() @@ -702,16 +700,14 @@ pub fn extract_local_gui_settings(ctx: &Context) -> Settings { .map(|token| HardwareWalletConfig::new(kind, *fingerprint, token)) }) .collect(); - Settings { - wallets: vec![WalletSettings { - name: wallet_name(descriptor), - pinned_at: Some(chrono::Utc::now().timestamp()), - descriptor_checksum, - keys: ctx.keys.values().cloned().collect(), - hardware_wallets, - remote_backend_auth: None, - start_internal_bitcoind: Some(ctx.internal_bitcoind.is_some()), - }], + WalletSettings { + name: wallet_name(descriptor), + pinned_at: Some(chrono::Utc::now().timestamp()), + descriptor_checksum, + keys: ctx.keys.values().cloned().collect(), + hardware_wallets, + remote_backend_auth: None, + start_internal_bitcoind: Some(ctx.internal_bitcoind.is_some()), } } diff --git a/liana-gui/src/installer/step/mod.rs b/liana-gui/src/installer/step/mod.rs index 98fc4568..616869c3 100644 --- a/liana-gui/src/installer/step/mod.rs +++ b/liana-gui/src/installer/step/mod.rs @@ -21,14 +21,14 @@ pub use mnemonic::{BackupMnemonic, RecoverMnemonic}; pub use share_xpubs::ShareXpubs; use tracing::warn; -use std::{collections::HashMap, path::PathBuf}; +use std::collections::HashMap; use iced::{Subscription, Task}; use liana_ui::widget::*; use crate::{ - app::settings::ProviderKey, + app::settings::{ProviderKey, WalletSettings}, hw::HardwareWallets, installer::{context::Context, message::Message, view}, node::bitcoind::Bitcoind, @@ -67,7 +67,7 @@ pub struct Final { generating: bool, internal_bitcoind: Option, warning: Option, - config_path: Option, + wallet_settings: Option, key_redemptions: HashMap>>, } @@ -77,7 +77,7 @@ impl Final { internal_bitcoind: None, generating: false, warning: None, - config_path: None, + wallet_settings: None, key_redemptions: HashMap::new(), } } @@ -99,7 +99,7 @@ impl Step for Final { .collect(); } fn load(&self) -> Task { - if !self.generating && self.config_path.is_none() { + if !self.generating && self.wallet_settings.is_none() { Task::perform(async {}, |_| Message::Install) } else { Task::none() @@ -142,30 +142,30 @@ impl Step for Final { } // Now exit the installer whether or not any redemption errors occurred. let internal_bitcoind = self.internal_bitcoind.clone(); - let path = self.config_path.clone().expect("config path already set"); + let settings = self.wallet_settings.clone().expect("Install is done"); // If there were any errors, don't remove the installer log. return Task::perform( - async move { (path, internal_bitcoind, has_error) }, - |(path, internal_bitcoind, has_error)| { - Message::Exit(path, internal_bitcoind, !has_error) + async move { (settings, internal_bitcoind, has_error) }, + |(settings, internal_bitcoind, has_error)| { + Message::Exit(Box::new(settings), internal_bitcoind, !has_error) }, ); } Message::Installed(res) => match res { Err(e) => { self.generating = false; - self.config_path = None; + self.wallet_settings = None; self.warning = Some(e.to_string()); } - Ok(path) => { - self.config_path = Some(path.clone()); + Ok(wallet_settings) => { + self.wallet_settings = Some(wallet_settings); // Now redeem any provider keys. return Task::perform(async move {}, |_| Message::RedeemNextKey); } }, Message::Install => { self.generating = true; - self.config_path = None; + self.wallet_settings = None; self.warning = None; } _ => {} @@ -183,7 +183,7 @@ impl Step for Final { progress, email, self.generating, - self.config_path.as_ref(), + self.wallet_settings.is_some(), self.warning.as_ref(), ) } diff --git a/liana-gui/src/installer/view/mod.rs b/liana-gui/src/installer/view/mod.rs index 2b8a6b2e..19fdfb4b 100644 --- a/liana-gui/src/installer/view/mod.rs +++ b/liana-gui/src/installer/view/mod.rs @@ -1485,7 +1485,7 @@ pub fn install<'a>( progress: (usize, usize), email: Option<&'a str>, generating: bool, - config_path: Option<&std::path::PathBuf>, + installed: bool, warning: Option<&'a String>, ) -> Element<'a, Message> { let prev_msg = if !generating && warning.is_some() { @@ -1501,7 +1501,7 @@ pub fn install<'a>( .push_maybe(warning.map(|e| card::invalid(text(e)))) .push(if generating { Container::new(text("Installing...")) - } else if config_path.is_some() { + } else if installed { Container::new( Row::new() .spacing(10) diff --git a/liana-gui/src/main.rs b/liana-gui/src/main.rs index e1b9445d..fd942403 100644 --- a/liana-gui/src/main.rs +++ b/liana-gui/src/main.rs @@ -266,19 +266,20 @@ impl GUI { _ => l.update(*msg).map(|msg| Message::Login(Box::new(msg))), }, (State::Installer(i), Message::Install(msg)) => { - if let installer::Message::Exit(path, internal_bitcoind, remove_log) = *msg { - let network_dir = i.datadir.network_directory(i.network); - // We get the first one created. - let settings = app::settings::WalletSettings::from_file(&network_dir, |_| true) - .expect("A settings file was created") - .expect("A wallet was created"); - if let Some(setting) = settings.remote_backend_auth { + if let installer::Message::Exit(settings, internal_bitcoind, remove_log) = *msg { + if let Some(auth) = settings.remote_backend_auth { let (login, command) = - login::LianaLiteLogin::new(i.datadir.clone(), i.network, setting); + login::LianaLiteLogin::new(i.datadir.clone(), i.network, auth); self.state = State::Login(Box::new(login)); command.map(|msg| Message::Login(Box::new(msg))) } else { - let cfg = app::Config::from_file(&path).expect("A config file was created"); + let cfg = app::Config::from_file( + &i.datadir + .network_directory(i.network) + .path() + .join(app::config::DEFAULT_FILE_NAME), + ) + .expect("A gui configuration file must be present"); self.logger.set_running_mode( i.datadir.clone(), @@ -296,7 +297,7 @@ impl GUI { i.network, internal_bitcoind, i.context.backup.take(), - settings, + *settings, ); self.state = State::Loader(Box::new(loader)); command.map(|msg| Message::Load(Box::new(msg)))