diff --git a/liana-gui/src/app/state/export.rs b/liana-gui/src/app/state/export.rs index 7de48c93..7f9944ca 100644 --- a/liana-gui/src/app/state/export.rs +++ b/liana-gui/src/app/state/export.rs @@ -49,6 +49,7 @@ impl ExportModal { ImportExportType::ImportPsbt => "Import PSBT", ImportExportType::ImportDescriptor => "Import Descriptor", ImportExportType::ImportBackup(..) => "Restore Backup", + ImportExportType::WalletFromBackup => "Import existing wallet from backup", } } @@ -71,9 +72,12 @@ impl ExportModal { ImportExportType::ImportPsbt => "psbt.psbt".into(), ImportExportType::ImportDescriptor => "descriptor.descriptor".into(), ImportExportType::ExportLabels => format!("liana-labels-{date}.jsonl"), - ImportExportType::ExportBackup(_) | ImportExportType::ImportBackup(_, _) => { + ImportExportType::ExportBackup(_) => { format!("liana-backup-{date}.json") } + ImportExportType::WalletFromBackup | ImportExportType::ImportBackup(_, _) => { + "liana-backup.json".to_string() + } } } @@ -132,6 +136,7 @@ impl ExportModal { ImportExportMessage::UpdateAliases(map.clone()).into() }); } + Progress::WalletFromBackup(_) => {} }, ImportExportMessage::TimedOut => { self.stop(ImportExportState::TimedOut); diff --git a/liana-gui/src/export.rs b/liana-gui/src/export.rs index c50b5bdd..91479803 100644 --- a/liana-gui/src/export.rs +++ b/liana-gui/src/export.rs @@ -16,7 +16,7 @@ use async_hwi::bitbox::api::btc::Fingerprint; use chrono::{DateTime, Duration, Utc}; use liana::{ descriptors::LianaDescriptor, - miniscript::bitcoin::{Amount, Psbt, Txid}, + miniscript::bitcoin::{Amount, Network, Psbt, Txid}, }; use lianad::{ bip329::{error::ExportError, Labels}, @@ -195,6 +195,7 @@ pub enum ImportExportType { ExportLabels, ImportPsbt, ImportDescriptor, + WalletFromBackup, } impl ImportExportType { @@ -207,6 +208,7 @@ impl ImportExportType { | ImportExportType::ExportLabels => "Export successful!", ImportExportType::ImportBackup(_, _) | ImportExportType::ImportPsbt + | ImportExportType::WalletFromBackup | ImportExportType::ImportDescriptor => "Import successful", } } @@ -270,6 +272,14 @@ pub enum Progress { LabelsConflict(SyncSender), KeyAliasesConflict(SyncSender), UpdateAliases(HashMap), + WalletFromBackup( + ( + LianaDescriptor, + Network, + HashMap, + Backup, + ), + ), } pub struct Export { @@ -305,6 +315,7 @@ impl Export { path: PathBuf, ) { match export_type { + ImportExportType::WalletFromBackup => wallet_from_backup(sender, path).await, ImportExportType::Transactions => export_transactions(sender, daemon, path).await, ImportExportType::ExportPsbt(str) => export_string(sender, path, str), ImportExportType::Descriptor(descriptor) => export_descriptor(sender, path, descriptor), @@ -1021,6 +1032,81 @@ impl From for RestoreBackupError { } } +/// Create a wallet from a backup +/// - load backup from file +/// - extract descriptor +/// - extract network +/// - extract aliases +pub async fn wallet_from_backup(sender: Sender, path: PathBuf) { + // Load backup from file + let mut file = open_file_read!(path, sender); + + let mut backup_str = String::new(); + if let Err(e) = file.read_to_string(&mut backup_str) { + send_error!(sender, e.into()); + return; + } + + let backup: Result = serde_json::from_str(&backup_str); + let backup = match backup { + Ok(psbt) => psbt, + Err(e) => { + send_error!(sender, Error::BackupImport(format!("{:?}", e))); + return; + } + }; + + let network = backup.network; + + let account = match backup.accounts.len() { + 0 => { + send_error!( + sender, + Error::BackupImport("There is no account in the backup!".into()) + ); + return; + } + 1 => backup.accounts.first().expect("already checked"), + _ => { + send_error!( + sender, + Error::BackupImport( + "Liana is actually not supporting import of backup with several accounts!" + .into() + ) + ); + return; + } + }; + + let descriptor = match LianaDescriptor::from_str(&account.descriptor) { + Ok(d) => d, + Err(_) => { + send_error!( + sender, + Error::BackupImport( + "The backup descriptor is not a valid Liana descriptor!".into() + ) + ); + return; + } + }; + + let mut aliases: HashMap = HashMap::new(); + for (k, v) in &account.keys { + if let Some(alias) = &v.alias { + aliases.insert(*k, alias.clone()); + } + } + + send_progress!( + sender, + WalletFromBackup((descriptor, network, aliases, backup)) + ); + send_progress!(sender, Progress(100.0)); + send_progress!(sender, Ended); +} + #[allow(unused)] /// Import backup data if wallet created from a backup /// - check if networks matches