use std::collections::{HashMap, HashSet}; use std::sync::Arc; use anyhow::{anyhow, bail}; use axum::{async_trait, Router}; use bip39::rand::{thread_rng, Rng}; use cdk::cdk_lightning::MintLightning; use cdk::mint::FeeReserve; use cdk::mint_url::MintUrl; use cdk::nuts::CurrencyUnit; use tokio::sync::Mutex; use url::Url; use crate::config::{self, Settings}; use crate::expand_path; #[async_trait] pub trait LnBackendSetup { async fn setup( &self, routers: &mut Vec, settings: &Settings, unit: CurrencyUnit, ) -> anyhow::Result; } #[async_trait] impl LnBackendSetup for config::Cln { async fn setup( &self, _routers: &mut Vec, _settings: &Settings, _unit: CurrencyUnit, ) -> anyhow::Result { let cln_socket = expand_path( self.rpc_path .to_str() .ok_or(anyhow!("cln socket not defined"))?, ) .ok_or(anyhow!("cln socket not defined"))?; let fee_reserve = FeeReserve { min_fee_reserve: self.reserve_fee_min, percent_fee_reserve: self.fee_percent, }; let cln = cdk_cln::Cln::new(cln_socket, fee_reserve).await?; Ok(cln) } } #[async_trait] impl LnBackendSetup for config::Strike { async fn setup( &self, routers: &mut Vec, settings: &Settings, unit: CurrencyUnit, ) -> anyhow::Result { let api_key = &self.api_key; // Channel used for strike web hook let (sender, receiver) = tokio::sync::mpsc::channel(8); let webhook_endpoint = format!("/webhook/{}/invoice", unit); let mint_url: MintUrl = settings.info.url.parse()?; let webhook_url = mint_url.join(&webhook_endpoint)?; let strike = cdk_strike::Strike::new( api_key.clone(), unit, Arc::new(Mutex::new(Some(receiver))), webhook_url.to_string(), ) .await?; let router = strike .create_invoice_webhook(&webhook_endpoint, sender) .await?; routers.push(router); Ok(strike) } } #[async_trait] impl LnBackendSetup for config::LNbits { async fn setup( &self, routers: &mut Vec, settings: &Settings, _unit: CurrencyUnit, ) -> anyhow::Result { let admin_api_key = &self.admin_api_key; let invoice_api_key = &self.invoice_api_key; // Channel used for lnbits web hook let (sender, receiver) = tokio::sync::mpsc::channel(8); let webhook_endpoint = "/webhook/lnbits/sat/invoice"; let mint_url: MintUrl = settings.info.url.parse()?; let webhook_url = mint_url.join(webhook_endpoint)?; let fee_reserve = FeeReserve { min_fee_reserve: self.reserve_fee_min, percent_fee_reserve: self.fee_percent, }; let lnbits = cdk_lnbits::LNbits::new( admin_api_key.clone(), invoice_api_key.clone(), self.lnbits_api.clone(), fee_reserve, Arc::new(Mutex::new(Some(receiver))), webhook_url.to_string(), ) .await?; let router = lnbits .create_invoice_webhook_router(webhook_endpoint, sender) .await?; routers.push(router); Ok(lnbits) } } #[async_trait] impl LnBackendSetup for config::Phoenixd { async fn setup( &self, routers: &mut Vec, settings: &Settings, _unit: CurrencyUnit, ) -> anyhow::Result { let api_password = &self.api_password; let api_url = &self.api_url; let fee_reserve = FeeReserve { min_fee_reserve: self.reserve_fee_min, percent_fee_reserve: self.fee_percent, }; if fee_reserve.percent_fee_reserve < 0.04 { bail!("Fee reserve is too low needs to be at least 0.02"); } let webhook_endpoint = "/webhook/phoenixd"; let mint_url = Url::parse(&settings.info.url)?; let webhook_url = mint_url.join(webhook_endpoint)?.to_string(); let (sender, receiver) = tokio::sync::mpsc::channel(8); let phoenixd = cdk_phoenixd::Phoenixd::new( api_password.to_string(), api_url.to_string(), fee_reserve, Arc::new(Mutex::new(Some(receiver))), webhook_url, )?; let router = phoenixd .create_invoice_webhook(webhook_endpoint, sender) .await?; routers.push(router); Ok(phoenixd) } } #[async_trait] impl LnBackendSetup for config::Lnd { async fn setup( &self, _routers: &mut Vec, _settings: &Settings, _unit: CurrencyUnit, ) -> anyhow::Result { let address = &self.address; let cert_file = &self.cert_file; let macaroon_file = &self.macaroon_file; let fee_reserve = FeeReserve { min_fee_reserve: self.reserve_fee_min, percent_fee_reserve: self.fee_percent, }; let lnd = cdk_lnd::Lnd::new( address.to_string(), cert_file.clone(), macaroon_file.clone(), fee_reserve, ) .await?; Ok(lnd) } } #[async_trait] impl LnBackendSetup for config::FakeWallet { async fn setup( &self, _router: &mut Vec, _settings: &Settings, _unit: CurrencyUnit, ) -> anyhow::Result { let fee_reserve = FeeReserve { min_fee_reserve: self.reserve_fee_min, percent_fee_reserve: self.fee_percent, }; // calculate random delay time let mut rng = thread_rng(); let delay_time = rng.gen_range(self.min_delay_time..=self.max_delay_time); let fake_wallet = cdk_fake_wallet::FakeWallet::new( fee_reserve, HashMap::default(), HashSet::default(), delay_time, ); Ok(fake_wallet) } }