Skip to content

Commit

Permalink
chore: users can leave federations with zero balance
Browse files Browse the repository at this point in the history
  • Loading branch information
tvolk131 committed Oct 1, 2024
1 parent 62e4ad3 commit f074aa5
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 11 deletions.
50 changes: 45 additions & 5 deletions src/fedimint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl Wallet {

pub async fn connect_to_joined_federations(&self) -> anyhow::Result<()> {
// Note: We're intentionally locking the clients mutex earlier than
// necessary so that the lock is held while we're reading the data directory.
// necessary so that the lock is held while we're accessing the data directory.
let mut clients = self.clients.lock().await;

// List all files in the data directory.
Expand Down Expand Up @@ -150,9 +150,12 @@ impl Wallet {
Ok(())
}

pub async fn join_federation(&self, invite_code: InviteCode) -> anyhow::Result<()> {
pub async fn join_federation(
&self,
invite_code: InviteCode,
) -> anyhow::Result<BTreeMap<FederationId, FederationView>> {
// Note: We're intentionally locking the clients mutex earlier than
// necessary so that the lock is held while we're reading the data directory.
// necessary so that the lock is held while we're accessing the data directory.
let mut clients = self.clients.lock().await;

let federation_id = invite_code.federation_id();
Expand All @@ -163,7 +166,7 @@ impl Wallet {

// Short-circuit if we're already connected to this federation.
if federation_data_dir.is_dir() {
return Ok(());
return Ok(Self::get_current_state(clients).await);
}

let db: Database = RocksDb::open(federation_data_dir)?.into();
Expand All @@ -172,7 +175,44 @@ impl Wallet {

clients.insert(federation_id, client);

Ok(())
Ok(Self::get_current_state(clients).await)
}

// TODO: Call `ClientModule::leave()` for every module.
// https://docs.rs/fedimint-client/0.4.2/fedimint_client/module/trait.ClientModule.html#method.leave
// Currently it isn't implemented for the `LightningClientModule`, so for now we're just checking
// that the client has a zero balance.
pub async fn leave_federation(
&self,
federation_id: FederationId,
) -> anyhow::Result<BTreeMap<FederationId, FederationView>> {
// Note: We're intentionally locking the clients mutex earlier than
// necessary so that the lock is held while we're accessing the data directory.
let mut clients = self.clients.lock().await;

if let Some(client) = clients.remove(&federation_id) {
if client.get_balance().await.msats != 0 {
// Re-insert the client back into the clients map.
clients.insert(federation_id, client);

return Err(anyhow::anyhow!(
"Cannot leave federation with non-zero balance: {}",
federation_id
));
}

client.shutdown().await;

let federation_data_dir = self
.fedimint_clients_data_dir
.join(federation_id.to_string());

if federation_data_dir.is_dir() {
std::fs::remove_dir_all(federation_data_dir)?;
}
}

Ok(Self::get_current_state(clients).await)
}

async fn get_current_state(
Expand Down
75 changes: 69 additions & 6 deletions src/routes/bitcoin_wallet.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::BTreeMap, str::FromStr};
use std::{collections::BTreeMap, str::FromStr, sync::Arc};

use fedimint_core::{
config::{ClientConfig, FederationId, META_FEDERATION_NAME_KEY},
Expand All @@ -15,7 +15,7 @@ use iced::{
use crate::{
app,
fedimint::FederationView,
ui_components::{icon_button, PaletteColor, SvgIcon},
ui_components::{icon_button, PaletteColor, SvgIcon, Toast, ToastStatus},
util::{format_amount, lighten, truncate_text},
};

Expand All @@ -42,6 +42,10 @@ pub enum Message {
JoinFederation(InviteCode),
ConnectedToFederation,

LeaveFederation(FederationId),
LeftFederation((FederationId, BTreeMap<FederationId, FederationView>)),
FailedToLeaveFederation((FederationId, Arc<anyhow::Error>)),

Send(send::Message),
Receive(receive::Message),

Expand Down Expand Up @@ -160,17 +164,57 @@ impl Page {
let wallet = self.connected_state.wallet.clone();

Task::future(async move {
wallet.join_federation(invite_code).await.unwrap();
app::Message::Routes(super::Message::BitcoinWalletPage(
Message::ConnectedToFederation,
))
let federation_views = wallet.join_federation(invite_code).await.unwrap();
app::Message::UpdateFederationViews(federation_views)
})
.chain(Task::done(app::Message::Routes(
super::Message::BitcoinWalletPage(Message::ConnectedToFederation),
)))
.chain(Task::done(app::Message::AddToast(Toast {
title: "Joined federation".to_string(),
body: "You have successfully joined the federation.".to_string(),
status: ToastStatus::Good,
})))
}
Message::ConnectedToFederation => {
// TODO: Do something here, or remove `ConnectedToFederation` message variant.

Task::none()
}
Message::LeaveFederation(federation_id) => {
let wallet = self.connected_state.wallet.clone();

Task::future(async move {
match wallet.leave_federation(federation_id).await {
Ok(federation_views) => {
app::Message::Routes(super::Message::BitcoinWalletPage(
Message::LeftFederation((federation_id, federation_views)),
))
}
Err(err) => app::Message::Routes(super::Message::BitcoinWalletPage(
Message::FailedToLeaveFederation((federation_id, Arc::from(err))),
)),
}
})
}
Message::LeftFederation((_federation_id, federation_views)) => {
Task::done(app::Message::UpdateFederationViews(federation_views))
.chain(Task::done(app::Message::Routes(super::Message::Navigate(
RouteName::BitcoinWallet(SubrouteName::List),
))))
.chain(Task::done(app::Message::AddToast(Toast {
title: "Left federation".to_owned(),
body: "You have successfully left the federation.".to_owned(),
status: ToastStatus::Good,
})))
}
Message::FailedToLeaveFederation((_federation_id, _err)) => {
Task::done(app::Message::AddToast(Toast {
title: "Failed to leave federation".to_owned(),
body: "Could not leave federation.".to_owned(),
status: ToastStatus::Bad,
}))
}
Message::Send(send_message) => {
if let Subroute::Send(send_page) = &mut self.subroute {
send_page.update(send_message)
Expand Down Expand Up @@ -418,6 +462,25 @@ impl FederationDetails {
);
}

let has_zero_balance = self.view.balance.msats == 0;

if !has_zero_balance {
container = container.push(
Text::new("Must have a zero balance in this federation in order to leave.")
.size(20),
);
}

container = container.push(
icon_button("Leave Federation", SvgIcon::Delete, PaletteColor::Danger).on_press_maybe(
has_zero_balance.then(|| {
app::Message::Routes(super::Message::BitcoinWalletPage(
Message::LeaveFederation(self.view.federation_id),
))
}),
),
);

container = container.push(
icon_button("Back", SvgIcon::ArrowBack, PaletteColor::Background).on_press(
app::Message::Routes(super::Message::Navigate(RouteName::BitcoinWallet(
Expand Down

0 comments on commit f074aa5

Please sign in to comment.