diff --git a/src/lnurlp.rs b/src/lnurlp.rs index f15fd87..4277ce8 100644 --- a/src/lnurlp.rs +++ b/src/lnurlp.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use crate::{ invoice::{spawn_invoice_subscription, InvoiceState}, + mint::select_gateway, models::{invoice::NewInvoice, zaps::NewZap}, routes::{LnurlCallbackParams, LnurlCallbackResponse, LnurlVerifyResponse}, State, @@ -103,11 +104,9 @@ pub async fn lnurl_callback( let invoice_index = user.invoice_index; - let gateway = state - .mm - .get_gateway(&federation_id) + let gateway = select_gateway(&client) .await - .ok_or(anyhow!("Not gateway configured for federation"))?; + .ok_or(anyhow!("No gateway found for federation"))?; let (op_id, pr, preimage) = ln .create_bolt11_invoice_for_user_tweaked( diff --git a/src/mint.rs b/src/mint.rs index dc1239f..5fb1e45 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -5,6 +5,7 @@ use fedimint_ln_client::LightningClientModule; use fedimint_ln_common::LightningGateway; use log::error; use std::collections::HashMap; +use std::time::Duration; use std::{path::PathBuf, sync::Arc}; use tokio::sync::RwLock; @@ -18,14 +19,11 @@ pub(crate) trait MultiMintWrapperTrait { async fn check_has_federation(&self, id: FederationId) -> bool; async fn get_federation_client(&self, id: FederationId) -> Option; async fn register_new_federation(&self, invite_code: InviteCode) -> anyhow::Result<()>; - async fn get_gateway(&self, id: &FederationId) -> Option; } #[derive(Clone)] struct MultiMintWrapper { fm: Arc>, - /// Our preferred lightning gateway for each federation - gateways: Arc>>, } #[async_trait] @@ -51,19 +49,14 @@ impl MultiMintWrapperTrait for MultiMintWrapper { .await .expect("just registered"); - if let Some(gateway) = select_gateway(&client).await { - self.gateways.write().await.insert(id, gateway); - } else { - error!("No suitable gateway found for federation {id}"); + // update gateway cache, so we can find the best gateways + let ln = client.get_first_module::(); + if let Err(e) = ln.update_gateway_cache(true).await { + error!("Failed to update gateway cache: {e}"); } Ok(()) } - - async fn get_gateway(&self, id: &FederationId) -> Option { - let lock = self.gateways.read().await; - lock.get(id).cloned() - } } pub(crate) async fn setup_multimint( @@ -76,6 +69,12 @@ pub(crate) async fn setup_multimint( // select gateway for each federation for (id, client) in clients.iter() { + // update gateway cache, so we can find the best gateways + let ln = client.get_first_module::(); + if let Err(e) = ln.update_gateway_cache(true).await { + error!("Failed to update gateway cache: {e}"); + } + match select_gateway(client).await { Some(gateway) => { gateways.insert(*id, gateway); @@ -89,34 +88,49 @@ pub(crate) async fn setup_multimint( let mmw = MultiMintWrapper { fm: Arc::new(RwLock::new(mm)), - gateways: Arc::new(RwLock::new(HashMap::new())), }; - Ok(Arc::new(mmw)) + let mmw = Arc::new(mmw); + + // spawn thread to update gateways periodically, check every hour + let mmw_clone = mmw.clone(); + tokio::spawn(async move { + loop { + tokio::time::sleep(Duration::from_secs(60 * 60)).await; + let mm = mmw_clone.fm.read().await; + let clients = mm.clients.lock().await; + for (_, client) in clients.iter() { + let ln = client.get_first_module::(); + if let Err(e) = ln.update_gateway_cache(true).await { + error!("Failed to update gateway cache: {e}"); + } + } + } + }); + + Ok(mmw) } pub(crate) async fn select_gateway(client: &ClientHandleArc) -> Option { let ln = client.get_first_module::(); - let mut gateway_id = None; + let mut selected_gateway = None; for gateway in ln.list_gateways().await { // first try to find a vetted gateway if gateway.vetted { - gateway_id = Some(gateway.info.gateway_id); - break; // if vetted gateway found, use it + // if we can select the gateway, return it + if let Some(gateway) = ln.select_gateway(&gateway.info.gateway_id).await { + return Some(gateway); + } } // if no vetted gateway found, try to find a gateway with reasonable fees let fees = gateway.info.fees; if fees.base_msat >= 1_000 && fees.proportional_millionths >= 100 { - gateway_id = Some(gateway.info.gateway_id); - } - } - - if let Some(gateway_id) = gateway_id { - if let Some(gateway) = ln.select_gateway(&gateway_id).await { - return Some(gateway); + if let Some(g) = ln.select_gateway(&gateway.info.gateway_id).await { + selected_gateway = Some(g); + } } } - None + selected_gateway }