installer: add existing wallet from backup

This commit is contained in:
pythcoiner 2025-03-11 19:17:30 +01:00
parent aa53c3e44e
commit 091557a8df
No known key found for this signature in database
GPG Key ID: C1048AEEDF303B88
5 changed files with 138 additions and 24 deletions

View File

@ -191,11 +191,11 @@ pub enum ImportExportType {
Option<SyncSender<bool>>, /*overwrite_labels*/
Option<SyncSender<bool>>, /*overwrite_aliases*/
),
WalletFromBackup,
Descriptor(LianaDescriptor),
ExportLabels,
ImportPsbt,
ImportDescriptor,
WalletFromBackup,
}
impl ImportExportType {
@ -276,7 +276,7 @@ pub enum Progress {
(
LianaDescriptor,
Network,
HashMap<Fingerprint, String>,
HashMap<Fingerprint, settings::KeySetting>,
Backup,
),
),
@ -315,7 +315,6 @@ 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),
@ -324,6 +323,7 @@ impl Export {
ImportExportType::ImportDescriptor => import_descriptor(sender, path),
ImportExportType::ExportBackup(str) => export_string(sender, path, str),
ImportExportType::ImportBackup(..) => import_backup(sender, path, daemon).await,
ImportExportType::WalletFromBackup => wallet_from_backup(sender, path).await,
};
}
@ -1092,10 +1092,16 @@ pub async fn wallet_from_backup(sender: Sender<Progress>, path: PathBuf) {
}
};
let mut aliases: HashMap<Fingerprint, String> = HashMap::new();
let mut aliases: HashMap<Fingerprint, settings::KeySetting> = HashMap::new();
for (k, v) in &account.keys {
if let Some(alias) = &v.alias {
aliases.insert(*k, alias.clone());
if let Some(ks) = KeySetting::from_backup(
v.alias.clone().unwrap_or("".into()),
*k,
v.role,
v.key_type,
v.metadata.clone(),
) {
aliases.insert(*k, ks);
}
}

View File

@ -2,13 +2,15 @@ use liana::miniscript::{
bitcoin::{bip32::Fingerprint, Network},
DescriptorPublicKey,
};
use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};
use super::{context, Error};
use crate::{
app::settings::ProviderKey,
app::view::Close,
backup,
app::{
settings::{self, ProviderKey},
view::Close,
},
backup::{self, Backup},
download::{DownloadError, Progress},
export::ImportExportMessage,
hw::HardwareWalletMessage,
@ -55,6 +57,8 @@ pub enum Message {
BackupWallet,
ExportWallet(Result<String, backup::Error>),
ImportExport(ImportExportMessage),
ImportBackup,
WalletFromBackup((HashMap<Fingerprint, settings::KeySetting>, Backup)),
}
impl Close for Message {

View File

@ -292,6 +292,11 @@ impl Installer {
.expect("There is always a step")
.update(&mut self.hws, Message::Installed(Err(e)))
}
Message::WalletFromBackup((ks, backup)) => {
self.context.keys = ks;
self.context.backup = Some(backup);
Task::none()
}
_ => self
.steps
.get_mut(self.current)

View File

@ -18,7 +18,7 @@ use async_hwi::DeviceKind;
use crate::{
app::{settings::KeySetting, state::export::ExportModal, wallet::wallet_name},
backup::{self, Backup},
export::{ImportExportMessage, ImportExportType},
export::{ImportExportMessage, ImportExportType, Progress},
hw::{HardwareWallet, HardwareWallets},
installer::{
message::{self, Message},
@ -32,6 +32,8 @@ pub struct ImportDescriptor {
imported_descriptor: form::Value<String>,
wrong_network: bool,
error: Option<String>,
modal: Option<ExportModal>,
imported_backup: bool,
}
impl ImportDescriptor {
@ -41,6 +43,8 @@ impl ImportDescriptor {
imported_descriptor: form::Value::default(),
wrong_network: false,
error: None,
modal: None,
imported_backup: false,
}
}
@ -77,14 +81,55 @@ impl Step for ImportDescriptor {
fn skip(&self, ctx: &Context) -> bool {
ctx.remote_backend.is_some()
}
// 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 subscription(&self, _hws: &HardwareWallets) -> Subscription<Message> {
if let Some(modal) = &self.modal {
if let Some(sub) = modal.subscription() {
sub.map(|m| Message::ImportExport(ImportExportMessage::Progress(m)))
} else {
Subscription::none()
}
} else {
Subscription::none()
}
}
fn update(&mut self, _hws: &mut HardwareWallets, message: Message) -> Task<Message> {
if let Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) =
message
{
self.imported_descriptor.value = desc;
self.check_descriptor(self.network);
match message {
Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(desc)) => {
self.imported_descriptor.value = desc;
self.check_descriptor(self.network);
}
Message::ImportExport(ImportExportMessage::Close) => {
self.modal = None;
}
Message::ImportBackup => {
if !self.imported_backup {
let modal = ExportModal::new(None, ImportExportType::WalletFromBackup);
let launch = modal.launch(false);
self.modal = Some(modal);
return launch;
}
}
Message::ImportExport(ImportExportMessage::Progress(Progress::WalletFromBackup(r))) => {
let (descriptor, network, aliases, backup) = r;
if self.network == network {
self.imported_backup = true;
self.imported_descriptor.value = descriptor.to_string();
return Task::perform(async move { (aliases, backup) }, |(a, b)| {
Message::WalletFromBackup((a, b))
});
} else {
self.error = Some("Backup network do not match the selected network!".into());
}
}
Message::ImportExport(m) => {
if let Some(modal) = self.modal.as_mut() {
let task: Task<Message> = modal.update(m);
return task;
};
}
_ => {}
}
Task::none()
}
@ -108,13 +153,19 @@ impl Step for ImportDescriptor {
progress: (usize, usize),
email: Option<&'a str>,
) -> Element<Message> {
view::import_descriptor(
let content = view::import_descriptor(
progress,
email,
&self.imported_descriptor,
self.imported_backup,
self.wrong_network,
self.error.as_ref(),
)
);
if let Some(modal) = &self.modal {
modal.view(content)
} else {
content
}
}
}

View File

@ -264,11 +264,15 @@ pub fn import_descriptor<'a>(
progress: (usize, usize),
email: Option<&'a str>,
imported_descriptor: &form::Value<String>,
imported_backup: bool,
wrong_network: bool,
error: Option<&String>,
) -> Element<'a, Message> {
let valid = !imported_descriptor.value.is_empty() && imported_descriptor.valid;
let col_descriptor = Column::new()
.push(text("Descriptor:").bold())
.push(Space::with_height(10))
.push(
form::Form::new_trimmed("Descriptor", imported_descriptor, |msg| {
Message::DefineDescriptor(message::DefineDescriptor::ImportDescriptor(msg))
@ -280,21 +284,65 @@ pub fn import_descriptor<'a>(
})
.size(text::P1_SIZE)
.padding(10),
);
let descriptor = if imported_backup {
None
} else {
Some(col_descriptor)
};
let or = if !valid && !imported_backup {
Some(
Row::new()
.push(text("or").bold())
.push(Space::with_width(Length::Fill)),
)
.spacing(10);
} else {
None
};
let import_backup = if !valid && !imported_backup {
Some(
Row::new()
.push(button::secondary(None, "Import backup").on_press(Message::ImportBackup))
.push(Space::with_width(Length::Fill)),
)
} else {
None
};
let backup_imported = if imported_backup {
Some(
Row::new()
.push(text("Backup successfuly imported!").bold())
.push(Space::with_width(Length::Fill)),
)
} else {
None
};
layout(
progress,
email,
"Import the wallet",
Column::new()
.push(Column::new().spacing(20).push(col_descriptor).push(text(
"If you are using a Bitcoin Core node, \
.push(
Column::new()
.spacing(20)
.push_maybe(descriptor)
.push_maybe(or)
.push_maybe(import_backup)
.push_maybe(backup_imported)
.push(text(
"If you are using a Bitcoin Core node, \
you will need to perform a rescan of \
the blockchain after creating the wallet \
in order to see your coins and past \
transactions. This can be done in \
Settings > Node.",
)))
)),
)
.push(
if imported_descriptor.value.is_empty() || !imported_descriptor.valid {
button::secondary(None, "Next").width(Length::Fixed(200.0))