Append wallet settings to file during creation

This commit is contained in:
edouardparis 2025-05-08 17:01:28 +02:00
parent e85f89dc7f
commit 58bccef4cd
6 changed files with 89 additions and 93 deletions

View File

@ -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);

View File

@ -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<Bitcoind>, /* remove log */ bool),
Exit(
Box<settings::WalletSettings>,
Option<Bitcoind>,
/* remove log */ bool,
),
Clibpboard(String),
Next,
Skip,
@ -39,7 +43,7 @@ pub enum Message {
Reload,
Select(usize),
UseHotSigner,
Installed(Result<PathBuf, Error>),
Installed(Result<settings::WalletSettings, Error>),
CreateTaprootDescriptor(bool),
SelectDescriptorTemplate(context::DescriptorTemplate),
SelectBackend(SelectBackend),

View File

@ -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<Mutex<Signer>>,
) -> Result<PathBuf, Error> {
) -> Result<WalletSettings, Error> {
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<Mutex<Signer>>,
remote_backend: BackendClient,
) -> Result<PathBuf, Error> {
) -> Result<WalletSettings, Error> {
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<PathBuf, Error> {
) -> Result<WalletSettings, Error> {
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()),
}
}

View File

@ -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<Bitcoind>,
warning: Option<String>,
config_path: Option<PathBuf>,
wallet_settings: Option<WalletSettings>,
key_redemptions: HashMap<ProviderKey, Option<Result<(), services::keys::Error>>>,
}
@ -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<Message> {
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(),
)
}

View File

@ -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)

View File

@ -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)))