From 86979f6e6a41cab2e1f597f5cc7c632a8735beea Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 22 Mar 2024 15:12:58 -0500 Subject: [PATCH] Properly set gateway --- src/lnurlp.rs | 16 +++++------ src/mint.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/lnurlp.rs b/src/lnurlp.rs index 44aff26..36300f5 100644 --- a/src/lnurlp.rs +++ b/src/lnurlp.rs @@ -95,6 +95,12 @@ pub async fn lnurl_callback( let invoice_index = user.invoice_index; + let gateway = state + .mm + .get_gateway(&federation_id) + .await + .ok_or(anyhow!("Not gateway configured for federation"))?; + let (op_id, pr, preimage) = ln .create_bolt11_invoice_for_user_tweaked( Amount::from_msats(params.amount), @@ -103,7 +109,7 @@ pub async fn lnurl_callback( user.pubkey().public_key(Parity::Odd), // todo is this parity correct / easy to work with? invoice_index as u64, (), - None, // todo set gateway properly + Some(gateway), ) .await?; @@ -136,13 +142,7 @@ pub async fn lnurl_callback( .await .expect("subscribing to a just created operation can't fail"); - spawn_invoice_subscription( - state.clone(), - created_invoice, - user.clone(), - subscription, - ) - .await; + spawn_invoice_subscription(state.clone(), created_invoice, user.clone(), subscription).await; let verify_url = format!("{}/lnurlp/{}/verify/{}", state.domain, user.name, op_id); diff --git a/src/mint.rs b/src/mint.rs index 644bfd2..dc1239f 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -1,6 +1,10 @@ use async_trait::async_trait; use fedimint_client::ClientHandleArc; use fedimint_core::{api::InviteCode, config::FederationId}; +use fedimint_ln_client::LightningClientModule; +use fedimint_ln_common::LightningGateway; +use log::error; +use std::collections::HashMap; use std::{path::PathBuf, sync::Arc}; use tokio::sync::RwLock; @@ -14,11 +18,14 @@ 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] @@ -32,20 +39,84 @@ impl MultiMintWrapperTrait for MultiMintWrapper { } async fn register_new_federation(&self, invite_code: InviteCode) -> anyhow::Result<()> { - self.fm + let id = self + .fm .write() .await .register_new(invite_code, None) .await?; + + let client = self + .get_federation_client(id) + .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}"); + } + 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( db_path: PathBuf, ) -> anyhow::Result> { - let fm = Arc::new(RwLock::new(MultiMint::new(db_path).await?)); - let mmw = MultiMintWrapper { fm }; + let mm = MultiMint::new(db_path).await?; + + let clients = mm.clients.lock().await; + let mut gateways = HashMap::with_capacity(clients.len()); + + // select gateway for each federation + for (id, client) in clients.iter() { + match select_gateway(client).await { + Some(gateway) => { + gateways.insert(*id, gateway); + } + None => { + error!("No suitable gateway found for federation {id}"); + } + } + } + drop(clients); + + let mmw = MultiMintWrapper { + fm: Arc::new(RwLock::new(mm)), + gateways: Arc::new(RwLock::new(HashMap::new())), + }; Ok(Arc::new(mmw)) } + +pub(crate) async fn select_gateway(client: &ClientHandleArc) -> Option { + let ln = client.get_first_module::(); + let mut gateway_id = 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 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); + } + } + + None +}