Delete wallet on Liana-Connect
This commit is contained in:
parent
f41605dcef
commit
1ca02a4897
@ -1,16 +1,25 @@
|
||||
use liana::miniscript::bitcoin::Network;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{
|
||||
app::settings::{self, SettingsError, WalletId},
|
||||
app::settings::{self, SettingsError, WalletSettings},
|
||||
dir::NetworkDirectory,
|
||||
services::connect::client::cache::{self, ConnectCacheError},
|
||||
services::connect::{
|
||||
client::{
|
||||
auth::AuthClient,
|
||||
cache::{self, ConnectCacheError},
|
||||
get_service_config,
|
||||
},
|
||||
login::{connect_with_credentials, BackendState},
|
||||
},
|
||||
signer,
|
||||
};
|
||||
|
||||
pub enum DeleteError {
|
||||
Io(std::io::Error),
|
||||
Settings(SettingsError),
|
||||
Connect(ConnectCacheError),
|
||||
ConnectCache(ConnectCacheError),
|
||||
Connect(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DeleteError {
|
||||
@ -18,6 +27,7 @@ impl std::fmt::Display for DeleteError {
|
||||
match self {
|
||||
Self::Io(e) => write!(f, "{}", e),
|
||||
Self::Settings(e) => write!(f, "{}", e),
|
||||
Self::ConnectCache(e) => write!(f, "{}", e),
|
||||
Self::Connect(e) => write!(f, "{}", e),
|
||||
}
|
||||
}
|
||||
@ -37,11 +47,12 @@ fn ignore_not_found<T>(result: std::io::Result<T>) -> std::io::Result<Option<T>>
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_wallet(
|
||||
pub async fn delete_failed_install(
|
||||
network_dir: &NetworkDirectory,
|
||||
wallet_id: &WalletId,
|
||||
wallet_id: &settings::WalletId,
|
||||
) -> Result<(), DeleteError> {
|
||||
let lianad_directory = network_dir.lianad_data_directory(wallet_id);
|
||||
|
||||
if !wallet_id.is_legacy() {
|
||||
ignore_not_found(tokio::fs::remove_dir_all(lianad_directory.path()).await)?;
|
||||
} else {
|
||||
@ -77,7 +88,95 @@ pub async fn delete_wallet(
|
||||
|
||||
cache::filter_connect_cache(network_dir, &remaining_accounts)
|
||||
.await
|
||||
.map_err(DeleteError::Connect)?;
|
||||
.map_err(DeleteError::ConnectCache)?;
|
||||
|
||||
signer::delete_wallet_mnemonics(
|
||||
network_dir,
|
||||
&wallet_id.descriptor_checksum,
|
||||
wallet_id.timestamp,
|
||||
)
|
||||
.map_err(DeleteError::Io)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_wallet(
|
||||
network: Network,
|
||||
network_dir: &NetworkDirectory,
|
||||
wallet: &WalletSettings,
|
||||
delete_liana_connect: bool,
|
||||
) -> Result<(), DeleteError> {
|
||||
let wallet_id = wallet.wallet_id();
|
||||
let lianad_directory = network_dir.lianad_data_directory(&wallet_id);
|
||||
|
||||
if !wallet_id.is_legacy() {
|
||||
ignore_not_found(tokio::fs::remove_dir_all(lianad_directory.path()).await)?;
|
||||
} else {
|
||||
// if this is a legacy wallet, then it is the only wallet in the network directory.
|
||||
ignore_not_found(tokio::fs::remove_file(lianad_directory.sqlite_db_file_path()).await)?;
|
||||
ignore_not_found(
|
||||
tokio::fs::remove_dir_all(lianad_directory.lianad_watchonly_wallet_path()).await,
|
||||
)?;
|
||||
ignore_not_found(
|
||||
tokio::fs::remove_file(lianad_directory.path().join("daemon.toml")).await,
|
||||
)?;
|
||||
}
|
||||
|
||||
if delete_liana_connect {
|
||||
if let Some(auth) = &wallet.remote_backend_auth {
|
||||
let service_config = get_service_config(network)
|
||||
.await
|
||||
.map_err(|e| DeleteError::Connect(e.to_string()))?;
|
||||
|
||||
let client = AuthClient::new(
|
||||
service_config.auth_api_url,
|
||||
service_config.auth_api_public_key,
|
||||
auth.email.to_string(),
|
||||
);
|
||||
if let BackendState::WalletExists(client, _, _) = connect_with_credentials(
|
||||
client,
|
||||
auth.wallet_id.clone(),
|
||||
service_config.backend_api_url,
|
||||
network,
|
||||
network_dir,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| DeleteError::Connect(e.to_string()))?
|
||||
{
|
||||
tracing::info!("Deleting wallet on Liana-Connect {} plateform", network);
|
||||
client
|
||||
.delete_wallet()
|
||||
.await
|
||||
.map_err(|e| DeleteError::Connect(e.to_string()))?;
|
||||
} else {
|
||||
tracing::warn!("Wallet not found on the platform");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut remaining_accounts = HashSet::<String>::new();
|
||||
settings::update_settings_file(network_dir, |mut settings| {
|
||||
settings
|
||||
.wallets
|
||||
.retain(|settings| settings.wallet_id() != wallet_id);
|
||||
remaining_accounts = settings
|
||||
.wallets
|
||||
.iter()
|
||||
.filter_map(|settings| {
|
||||
settings
|
||||
.remote_backend_auth
|
||||
.as_ref()
|
||||
.map(|auth| auth.email.clone())
|
||||
})
|
||||
.collect();
|
||||
settings
|
||||
})
|
||||
.await
|
||||
.map_err(DeleteError::Settings)?;
|
||||
|
||||
cache::filter_connect_cache(network_dir, &remaining_accounts)
|
||||
.await
|
||||
.map_err(DeleteError::ConnectCache)?;
|
||||
|
||||
signer::delete_wallet_mnemonics(
|
||||
network_dir,
|
||||
|
||||
@ -299,9 +299,10 @@ impl Installer {
|
||||
// In case of failure during install, block the thread to
|
||||
// deleted the data_dir/network directory in order to start clean again.
|
||||
warn!("Installation failed. Cleaning up the network directory.");
|
||||
if let Err(e) = Handle::current()
|
||||
.block_on(delete::delete_wallet(&network_directory, &wallet_id))
|
||||
{
|
||||
if let Err(e) = Handle::current().block_on(delete::delete_failed_install(
|
||||
&network_directory,
|
||||
&wallet_id,
|
||||
)) {
|
||||
error!(
|
||||
"Failed to completely clean the network directory (path: '{}'): {}",
|
||||
network_directory.path().to_string_lossy(),
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use iced::{
|
||||
alignment::Horizontal,
|
||||
widget::{pick_list, scrollable, Button, Space},
|
||||
widget::{checkbox, pick_list, scrollable, Button, Space},
|
||||
Alignment, Length, Subscription, Task,
|
||||
};
|
||||
|
||||
@ -124,6 +124,7 @@ impl Launcher {
|
||||
None
|
||||
};
|
||||
self.delete_wallet_modal = Some(DeleteWalletModal::new(
|
||||
self.network,
|
||||
wallet_datadir,
|
||||
wallets[i].clone(),
|
||||
internal_bitcoind,
|
||||
@ -425,52 +426,66 @@ pub enum DeleteWalletMessage {
|
||||
ShowModal(usize),
|
||||
CloseModal,
|
||||
Confirm(WalletId),
|
||||
DeleteLianaConnect(bool),
|
||||
Deleted,
|
||||
}
|
||||
|
||||
struct DeleteWalletModal {
|
||||
network: Network,
|
||||
network_directory: NetworkDirectory,
|
||||
wallet_settings: WalletSettings,
|
||||
warning: Option<DeleteError>,
|
||||
deleted: bool,
|
||||
delete_liana_connect: bool,
|
||||
// `None` means we were not able to determine whether wallet uses internal bitcoind.
|
||||
internal_bitcoind: Option<bool>,
|
||||
}
|
||||
|
||||
impl DeleteWalletModal {
|
||||
fn new(
|
||||
network: Network,
|
||||
network_directory: NetworkDirectory,
|
||||
wallet_settings: WalletSettings,
|
||||
internal_bitcoind: Option<bool>,
|
||||
) -> Self {
|
||||
Self {
|
||||
network,
|
||||
wallet_settings,
|
||||
network_directory,
|
||||
warning: None,
|
||||
deleted: false,
|
||||
delete_liana_connect: false,
|
||||
internal_bitcoind,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Task<Message> {
|
||||
if let Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Confirm(wallet_id))) =
|
||||
message
|
||||
{
|
||||
if wallet_id != self.wallet_settings.wallet_id() {
|
||||
return Task::none();
|
||||
match message {
|
||||
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Confirm(wallet_id))) => {
|
||||
if wallet_id != self.wallet_settings.wallet_id() {
|
||||
return Task::none();
|
||||
}
|
||||
self.warning = None;
|
||||
if let Err(e) = Handle::current().block_on(delete_wallet(
|
||||
self.network,
|
||||
&self.network_directory,
|
||||
&self.wallet_settings,
|
||||
self.delete_liana_connect,
|
||||
)) {
|
||||
self.warning = Some(e);
|
||||
} else {
|
||||
self.deleted = true;
|
||||
return Task::perform(async {}, |_| {
|
||||
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Deleted))
|
||||
});
|
||||
};
|
||||
}
|
||||
self.warning = None;
|
||||
if let Err(e) = Handle::current().block_on(delete_wallet(
|
||||
&self.network_directory,
|
||||
&self.wallet_settings.wallet_id(),
|
||||
)) {
|
||||
self.warning = Some(e);
|
||||
} else {
|
||||
self.deleted = true;
|
||||
return Task::perform(async {}, |_| {
|
||||
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::Deleted))
|
||||
});
|
||||
};
|
||||
Message::View(ViewMessage::DeleteWallet(DeleteWalletMessage::DeleteLianaConnect(
|
||||
delete,
|
||||
))) => {
|
||||
self.delete_liana_connect = delete;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
@ -523,6 +538,14 @@ impl DeleteWalletModal {
|
||||
.style(theme::text::destructive)
|
||||
.width(Length::Fill),
|
||||
))
|
||||
.push_maybe(self.wallet_settings.remote_backend_auth.as_ref().map(|_| {
|
||||
checkbox("Delete also on Liana-Connect", self.delete_liana_connect)
|
||||
.on_toggle(|v| {
|
||||
ViewMessage::DeleteWallet(DeleteWalletMessage::DeleteLianaConnect(
|
||||
v,
|
||||
))
|
||||
})
|
||||
}))
|
||||
.push(Row::new().push(text(help_text_1)))
|
||||
.push_maybe(
|
||||
help_text_2
|
||||
|
||||
@ -471,6 +471,21 @@ impl BackendWalletClient {
|
||||
pub async fn auth(&self) -> AccessTokenResponse {
|
||||
self.inner.auth.read().await.clone()
|
||||
}
|
||||
|
||||
pub async fn delete_wallet(&self) -> Result<(), DaemonError> {
|
||||
self.inner
|
||||
.request(
|
||||
Method::DELETE,
|
||||
&format!("{}/v1/wallets/{}", self.inner.url, self.wallet_uuid),
|
||||
)
|
||||
.await
|
||||
.send()
|
||||
.await?
|
||||
.check_success()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@ -16,7 +16,7 @@ use crate::{
|
||||
settings::{SettingsError, WalletSettings},
|
||||
},
|
||||
daemon::DaemonError,
|
||||
dir::LianaDirectory,
|
||||
dir::{LianaDirectory, NetworkDirectory},
|
||||
};
|
||||
|
||||
use super::client::{
|
||||
@ -170,7 +170,7 @@ impl LianaLiteLogin {
|
||||
auth.wallet_id,
|
||||
service_config.backend_api_url,
|
||||
network,
|
||||
datadir,
|
||||
&datadir.network_directory(network),
|
||||
)
|
||||
.await
|
||||
},
|
||||
@ -483,10 +483,9 @@ pub async fn connect_with_credentials(
|
||||
wallet_id: String,
|
||||
backend_api_url: String,
|
||||
network: Network,
|
||||
liana_directory: LianaDirectory,
|
||||
network_dir: &NetworkDirectory,
|
||||
) -> Result<BackendState, Error> {
|
||||
let network_dir = liana_directory.network_directory(network);
|
||||
let mut tokens = cache::Account::from_cache(&network_dir, &auth.email)
|
||||
let mut tokens = cache::Account::from_cache(network_dir, &auth.email)
|
||||
.map_err(|e| match e {
|
||||
ConnectCacheError::NotFound => Error::CredentialsMissing,
|
||||
_ => e.into(),
|
||||
@ -495,7 +494,7 @@ pub async fn connect_with_credentials(
|
||||
.tokens;
|
||||
|
||||
if tokens.expires_at < chrono::Utc::now().timestamp() {
|
||||
tokens = cache::update_connect_cache(&network_dir, &tokens, &auth, true).await?;
|
||||
tokens = cache::update_connect_cache(network_dir, &tokens, &auth, true).await?;
|
||||
}
|
||||
|
||||
let client = BackendClient::connect(auth, backend_api_url, tokens, network).await?;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user