Merge #1714: fix import backup for existing wallet

904fc728de20cdd283a4273093e6b59c5609630c fix import backup for existing wallet (edouardparis)

Pull request description:

  We pass the wallet to the import_backup method
  so the wallet id is used to find the correct wallet settings to update in the settings file.

ACKs for top commit:
  jp1ac4:
    utACK 904fc728de20cdd283a4273093e6b59c5609630c.

Tree-SHA512: 0a2a6c77d333460cc06ad141a7685d88911bc40bc3cd221111dd6611ca45e8c5552f46f7a694e44f0b47b4a5c1cd6946e25c755de83cbfe6259de93d6b9b65e0
This commit is contained in:
edouardparis 2025-05-21 10:27:53 +02:00
commit 63b32ff3a7
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
5 changed files with 95 additions and 78 deletions

View File

@ -83,7 +83,7 @@ impl ExportModal {
} }
ImportExportType::ImportPsbt(_) => "Import PSBT", ImportExportType::ImportPsbt(_) => "Import PSBT",
ImportExportType::ImportDescriptor => "Import Descriptor", ImportExportType::ImportDescriptor => "Import Descriptor",
ImportExportType::ImportBackup(..) => "Restore Backup", ImportExportType::ImportBackup { .. } => "Restore Backup",
ImportExportType::WalletFromBackup => "Import existing wallet from backup", ImportExportType::WalletFromBackup => "Import existing wallet from backup",
} }
} }
@ -111,7 +111,7 @@ impl ExportModal {
ImportExportType::ExportBackup(_) | ImportExportType::ExportProcessBackup(..) => { ImportExportType::ExportBackup(_) | ImportExportType::ExportProcessBackup(..) => {
format!("liana-backup-{date}.json") format!("liana-backup-{date}.json")
} }
ImportExportType::WalletFromBackup | ImportExportType::ImportBackup(_, _) => { ImportExportType::WalletFromBackup | ImportExportType::ImportBackup { .. } => {
"liana-backup.json".to_string() "liana-backup.json".to_string()
} }
} }
@ -140,15 +140,33 @@ impl ExportModal {
} }
Progress::Finished | Progress::Ended => self.state = ImportExportState::Ended, Progress::Finished | Progress::Ended => self.state = ImportExportState::Ended,
Progress::KeyAliasesConflict(ref sender) => { Progress::KeyAliasesConflict(ref sender) => {
if let ImportExportType::ImportBackup(_, None) = &self.import_export_type { if let ImportExportType::ImportBackup {
self.import_export_type = network_dir,
ImportExportType::ImportBackup(None, Some(sender.clone())); wallet,
..
} = &self.import_export_type
{
self.import_export_type = ImportExportType::ImportBackup {
network_dir: network_dir.clone(),
wallet: wallet.clone(),
overwrite_labels: None,
overwrite_aliases: Some(sender.clone()),
};
} }
} }
Progress::LabelsConflict(ref sender) => { Progress::LabelsConflict(ref sender) => {
if let ImportExportType::ImportBackup(None, _) = &self.import_export_type { if let ImportExportType::ImportBackup {
self.import_export_type = network_dir,
ImportExportType::ImportBackup(Some(sender.clone()), None); wallet,
..
} = &self.import_export_type
{
self.import_export_type = ImportExportType::ImportBackup {
network_dir: network_dir.clone(),
wallet: wallet.clone(),
overwrite_labels: Some(sender.clone()),
overwrite_aliases: None,
};
} }
} }
Progress::Error(e) => { Progress::Error(e) => {
@ -164,7 +182,7 @@ impl ExportModal {
}); });
} }
Progress::Descriptor(_) => { Progress::Descriptor(_) => {
if self.import_export_type == ImportExportType::ImportDescriptor { if matches!(self.import_export_type, ImportExportType::ImportDescriptor) {
self.state = ImportExportState::Ended; self.state = ImportExportState::Ended;
} }
} }
@ -192,10 +210,13 @@ impl ExportModal {
} }
ImportExportMessage::Close | ImportExportMessage::Open => { /* unreachable */ } ImportExportMessage::Close | ImportExportMessage::Open => { /* unreachable */ }
ImportExportMessage::Overwrite => { ImportExportMessage::Overwrite => {
if let ImportExportType::ImportBackup(labels, aliases) = if let ImportExportType::ImportBackup {
&mut self.import_export_type overwrite_labels,
overwrite_aliases,
..
} = &mut self.import_export_type
{ {
if let Some(sender) = labels.take() { if let Some(sender) = overwrite_labels.take() {
return Task::perform( return Task::perform(
async move { async move {
if sender.send(true).await.is_err() { if sender.send(true).await.is_err() {
@ -206,7 +227,7 @@ impl ExportModal {
}, },
|_| ImportExportMessage::Ignore.into(), |_| ImportExportMessage::Ignore.into(),
); );
} else if let Some(sender) = aliases.take() { } else if let Some(sender) = overwrite_aliases.take() {
return Task::perform( return Task::perform(
async move { async move {
if sender.send(true).await.is_err() { if sender.send(true).await.is_err() {
@ -221,10 +242,13 @@ impl ExportModal {
} }
} }
ImportExportMessage::Ignore => { ImportExportMessage::Ignore => {
if let ImportExportType::ImportBackup(labels, aliases) = if let ImportExportType::ImportBackup {
&mut self.import_export_type overwrite_labels,
overwrite_aliases,
..
} = &mut self.import_export_type
{ {
if let Some(sender) = labels.take() { if let Some(sender) = overwrite_labels.take() {
return Task::perform( return Task::perform(
async move { async move {
if sender.send(false).await.is_err() { if sender.send(false).await.is_err() {
@ -235,7 +259,7 @@ impl ExportModal {
}, },
|_| ImportExportMessage::Ignore.into(), |_| ImportExportMessage::Ignore.into(),
); );
} else if let Some(sender) = aliases.take() { } else if let Some(sender) = overwrite_aliases.take() {
return Task::perform( return Task::perform(
async move { async move {
if sender.send(false).await.is_err() { if sender.send(false).await.is_err() {

View File

@ -269,8 +269,15 @@ impl State for ImportExportSettingsState {
} }
Message::View(view::Message::Settings(view::SettingsMessage::ImportWallet)) => { Message::View(view::Message::Settings(view::SettingsMessage::ImportWallet)) => {
if self.modal.is_none() { if self.modal.is_none() {
let modal = let modal = ExportModal::new(
ExportModal::new(Some(daemon), ImportExportType::ImportBackup(None, None)); Some(daemon),
ImportExportType::ImportBackup {
network_dir: cache.datadir_path.network_directory(cache.network),
wallet: self.wallet.clone(),
overwrite_labels: None,
overwrite_aliases: None,
},
);
launch!(self, modal, false); launch!(self, modal, false);
} }
} }

View File

@ -289,8 +289,15 @@ impl State for WalletSettingsState {
} }
Message::View(view::Message::Settings(view::SettingsMessage::ImportWallet)) => { Message::View(view::Message::Settings(view::SettingsMessage::ImportWallet)) => {
if self.modal.is_none() { if self.modal.is_none() {
let modal = let modal = ExportModal::new(
ExportModal::new(Some(daemon), ImportExportType::ImportBackup(None, None)); Some(daemon),
ImportExportType::ImportBackup {
network_dir: cache.datadir_path.network_directory(cache.network),
wallet: self.wallet.clone(),
overwrite_labels: None,
overwrite_aliases: None,
},
);
let launch = modal.launch(false); let launch = modal.launch(false);
self.modal = Modal::ImportExport(modal); self.modal = Modal::ImportExport(modal);
launch launch

View File

@ -86,7 +86,11 @@ pub fn export_modal<'a, Message: From<ImportExportMessage> + Clone + 'static>(
)), )),
); );
let (msg, button) = match import_export_type { let (msg, button) = match import_export_type {
ImportExportType::ImportBackup(labels, aliases) => match (labels, aliases) { ImportExportType::ImportBackup {
overwrite_labels,
overwrite_aliases,
..
} => match (overwrite_labels, overwrite_aliases) {
(Some(_), _) => labels_btn, (Some(_), _) => labels_btn,
(_, Some(_)) => aliases_btn, (_, Some(_)) => aliases_btn,

View File

@ -165,10 +165,12 @@ pub enum ImportExportType {
ExportXpub(String), ExportXpub(String),
ExportBackup(String), ExportBackup(String),
ExportProcessBackup(LianaDirectory, Network, Arc<Config>, Arc<Wallet>), ExportProcessBackup(LianaDirectory, Network, Arc<Config>, Arc<Wallet>),
ImportBackup( ImportBackup {
Option<Sender<bool>>, /*overwrite_labels*/ network_dir: NetworkDirectory,
Option<Sender<bool>>, /*overwrite_aliases*/ wallet: Arc<Wallet>,
), overwrite_labels: Option<Sender<bool>>,
overwrite_aliases: Option<Sender<bool>>,
},
WalletFromBackup, WalletFromBackup,
Descriptor(LianaDescriptor), Descriptor(LianaDescriptor),
ExportLabels, ExportLabels,
@ -187,7 +189,7 @@ impl ImportExportType {
| ImportExportType::ExportProcessBackup(..) | ImportExportType::ExportProcessBackup(..)
| ImportExportType::ExportXpub(_) | ImportExportType::ExportXpub(_)
| ImportExportType::ExportLabels => "Export successful!", | ImportExportType::ExportLabels => "Export successful!",
ImportExportType::ImportBackup(_, _) ImportExportType::ImportBackup { .. }
| ImportExportType::ImportPsbt(_) | ImportExportType::ImportPsbt(_)
| ImportExportType::ImportXpub(_) | ImportExportType::ImportXpub(_)
| ImportExportType::WalletFromBackup | ImportExportType::WalletFromBackup
@ -196,20 +198,6 @@ impl ImportExportType {
} }
} }
impl PartialEq for ImportExportType {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::ExportPsbt(l0), Self::ExportPsbt(r0)) => l0 == r0,
(Self::ExportBackup(l0), Self::ExportBackup(r0)) => l0 == r0,
(Self::ImportBackup(l0, l1), Self::ImportBackup(r0, r1)) => {
l0.is_some() == r0.is_some() && l1.is_some() == r1.is_some()
}
(Self::Descriptor(l0), Self::Descriptor(r0)) => l0 == r0,
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl From<JoinError> for Error { impl From<JoinError> for Error {
fn from(value: JoinError) -> Self { fn from(value: JoinError) -> Self {
Error::JoinError(format!("{:?}", value)) Error::JoinError(format!("{:?}", value))
@ -321,7 +309,11 @@ impl Export {
) )
.await .await
} }
ImportExportType::ImportBackup(..) => import_backup(&sender, path, daemon).await, ImportExportType::ImportBackup {
network_dir,
wallet,
..
} => import_backup(&network_dir, wallet, &sender, path, daemon).await,
ImportExportType::WalletFromBackup => wallet_from_backup(&sender, path).await, ImportExportType::WalletFromBackup => wallet_from_backup(&sender, path).await,
} { } {
if let Err(e) = sender.send(Progress::Error(e)) { if let Err(e) = sender.send(Progress::Error(e)) {
@ -694,6 +686,8 @@ pub async fn import_xpub(
/// - import labels if no conflict or user ACK /// - import labels if no conflict or user ACK
/// - update aliases if no conflict or user ACK /// - update aliases if no conflict or user ACK
pub async fn import_backup( pub async fn import_backup(
network_dir: &NetworkDirectory,
wallet: Arc<Wallet>,
sender: &UnboundedSender<Progress>, sender: &UnboundedSender<Progress>,
path: PathBuf, path: PathBuf,
daemon: Option<Arc<dyn Daemon + Sync + Send>>, daemon: Option<Arc<dyn Daemon + Sync + Send>>,
@ -810,32 +804,16 @@ pub async fn import_backup(
Vec::new() Vec::new()
}; };
let lianad_datadir = daemon
.config()
.and_then(|c| c.data_directory())
.ok_or(Error::BackupImport("Failed to get Daemon config".into()))?;
let descriptor_checksum = descriptor
.to_string()
.split_once('#')
.map(|(_, checksum)| checksum)
.unwrap()
.to_string();
// check if key aliases can be imported w/o conflict // check if key aliases can be imported w/o conflict
let mut write_aliases = true; let mut write_aliases = true;
let settings = if !account.keys.is_empty() { let settings = if !account.keys.is_empty() {
// TODO: change lianad_datadir is common to gui datadir only for legacy wallet before let wallet_settings =
// multiple wallet match WalletSettings::from_file(network_dir, |w| w.wallet_id() == wallet.id()) {
let network_dir = NetworkDirectory::new(lianad_datadir.path().to_path_buf()); Ok(Some(s)) => s,
let wallet_settings = match WalletSettings::from_file(&network_dir, |w| { _ => {
w.descriptor_checksum == descriptor_checksum return Err(Error::BackupImport("Failed to get App Settings".into()));
}) { }
Ok(Some(s)) => s, };
_ => {
return Err(Error::BackupImport("Failed to get App Settings".into()));
}
};
let settings_aliases: HashMap<_, _> = wallet_settings let settings_aliases: HashMap<_, _> = wallet_settings
.keys .keys
@ -937,19 +915,16 @@ pub async fn import_backup(
} }
} }
if let Err(e) = update_settings_file( if let Err(e) = update_settings_file(network_dir, |mut settings| {
&NetworkDirectory::new(lianad_datadir.path().to_path_buf()), if let Some(wallet) = settings
|mut settings| { .wallets
if let Some(wallet) = settings .iter_mut()
.wallets .find(|w| w.wallet_id() == wallet.id())
.iter_mut() {
.find(|w| w.descriptor_checksum == descriptor_checksum) wallet.keys = settings_aliases.clone().into_values().collect();
{ }
wallet.keys = settings_aliases.clone().into_values().collect(); settings
} })
settings
},
)
.await .await
{ {
return Err(Error::BackupImport(format!( return Err(Error::BackupImport(format!(