diff --git a/crates/cdk-axum/src/lib.rs b/crates/cdk-axum/src/lib.rs index b11d40e2..8cc2300a 100644 --- a/crates/cdk-axum/src/lib.rs +++ b/crates/cdk-axum/src/lib.rs @@ -3,6 +3,7 @@ #![warn(missing_docs)] #![warn(rustdoc::bare_urls)] +use std::collections::HashMap; use std::sync::Arc; use anyhow::Result; @@ -10,6 +11,7 @@ use axum::routing::{get, post}; use axum::Router; use cdk::cdk_lightning::{self, MintLightning}; use cdk::mint::Mint; +use cdk::nuts::{CurrencyUnit, PaymentMethod}; use router_handlers::*; mod router_handlers; @@ -18,7 +20,7 @@ mod router_handlers; pub async fn create_mint_router( mint_url: &str, mint: Arc, - ln: Arc + Send + Sync>, + ln: HashMap + Send + Sync>>, quote_ttl: u64, ) -> Result { let state = MintState { @@ -56,8 +58,24 @@ pub async fn create_mint_router( #[derive(Clone)] struct MintState { - ln: Arc + Send + Sync>, + ln: HashMap + Send + Sync>>, mint: Arc, mint_url: String, quote_ttl: u64, } + +/// Key used in hashmap of ln backends to identify what unit and payment method it is for +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct LnKey { + /// Unit of Payment backend + pub unit: CurrencyUnit, + /// Method of payment backend + pub method: PaymentMethod, +} + +impl LnKey { + /// Create new [`LnKey`] + pub fn new(unit: CurrencyUnit, method: PaymentMethod) -> Self { + Self { unit, method } + } +} diff --git a/crates/cdk-axum/src/router_handlers.rs b/crates/cdk-axum/src/router_handlers.rs index e6a0f0b2..1b66e292 100644 --- a/crates/cdk-axum/src/router_handlers.rs +++ b/crates/cdk-axum/src/router_handlers.rs @@ -11,12 +11,12 @@ use cdk::nuts::{ CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeysResponse, KeysetResponse, MeltBolt11Request, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request, MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, MintQuoteState, - RestoreRequest, RestoreResponse, SwapRequest, SwapResponse, + PaymentMethod, RestoreRequest, RestoreResponse, SwapRequest, SwapResponse, }; use cdk::util::unix_time; use cdk::Bolt11Invoice; -use crate::MintState; +use crate::{LnKey, MintState}; pub async fn get_keys(State(state): State) -> Result, Response> { let pubkeys = state.mint.pubkeys().await.map_err(|err| { @@ -52,16 +52,23 @@ pub async fn get_mint_bolt11_quote( State(state): State, Json(payload): Json, ) -> Result, Response> { - let amount = - to_unit(payload.amount, &payload.unit, &state.ln.get_base_unit()).map_err(|err| { - tracing::error!("Backed does not support unit: {}", err); + let ln = state + .ln + .get(&LnKey::new(payload.unit, PaymentMethod::Bolt11)) + .ok_or({ + tracing::info!("Bolt11 mint request for unsupported unit"); + into_response(Error::UnsupportedUnit) })?; + let amount = to_unit(payload.amount, &payload.unit, &ln.get_base_unit()).map_err(|err| { + tracing::error!("Backed does not support unit: {}", err); + into_response(Error::UnsupportedUnit) + })?; + let quote_expiry = unix_time() + state.quote_ttl; - let create_invoice_response = state - .ln + let create_invoice_response = ln .create_invoice(amount, "".to_string(), quote_expiry) .await .map_err(|err| { @@ -124,6 +131,15 @@ pub async fn get_melt_bolt11_quote( State(state): State, Json(payload): Json, ) -> Result, Response> { + let ln = state + .ln + .get(&LnKey::new(payload.unit, PaymentMethod::Bolt11)) + .ok_or({ + tracing::info!("Could not get ln backend for {}, bolt11 ", payload.unit); + + into_response(Error::UnsupportedUnit) + })?; + let invoice_amount_msat = payload .request .amount_milli_satoshis() @@ -137,7 +153,15 @@ pub async fn get_melt_bolt11_quote( into_response(Error::UnsupportedUnit) })?; - let payment_quote = state.ln.get_payment_quote(&payload).await.unwrap(); + let payment_quote = ln.get_payment_quote(&payload).await.map_err(|err| { + tracing::error!( + "Could not get payment quote for mint quote, {} bolt11, {}", + payload.unit, + err + ); + + into_response(Error::UnsupportedUnit) + })?; let quote = state .mint @@ -314,8 +338,19 @@ pub async fn post_melt_bolt11( } } - let pre = match state + let ln = state .ln + .get(&LnKey::new(quote.unit, PaymentMethod::Bolt11)) + .ok_or({ + tracing::info!("Could not get ln backend for {}, bolt11 ", quote.unit); + if let Err(err) = state.mint.process_unpaid_melt(&payload).await { + tracing::error!("Could not reset melt quote state: {}", err); + } + + into_response(Error::UnsupportedUnit) + })?; + + let pre = match ln .pay_invoice(quote.clone(), partial_msats, max_fee_msats) .await { @@ -335,12 +370,8 @@ pub async fn post_melt_bolt11( } }; - let amount_spent = to_unit( - pre.total_spent_msats, - &state.ln.get_base_unit(), - "e.unit, - ) - .map_err(|_| into_response(Error::UnsupportedUnit))?; + let amount_spent = to_unit(pre.total_spent_msats, &ln.get_base_unit(), "e.unit) + .map_err(|_| into_response(Error::UnsupportedUnit))?; (pre.payment_preimage, amount_spent.into()) } diff --git a/crates/cdk-mintd/src/main.rs b/crates/cdk-mintd/src/main.rs index eda239c0..8f0e659f 100644 --- a/crates/cdk-mintd/src/main.rs +++ b/crates/cdk-mintd/src/main.rs @@ -3,6 +3,7 @@ #![warn(missing_docs)] #![warn(rustdoc::bare_urls)] +use std::collections::HashMap; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; @@ -14,7 +15,8 @@ use cdk::cdk_database::{self, MintDatabase}; use cdk::cdk_lightning; use cdk::cdk_lightning::MintLightning; use cdk::mint::{FeeReserve, Mint}; -use cdk::nuts::{ContactInfo, MintInfo, MintVersion, Nuts}; +use cdk::nuts::{ContactInfo, CurrencyUnit, MintInfo, MintVersion, Nuts, PaymentMethod}; +use cdk_axum::LnKey; use cdk_cln::Cln; use cdk_redb::MintRedbDatabase; use cdk_sqlite::MintSqliteDatabase; @@ -164,9 +166,15 @@ async fn main() -> anyhow::Result<()> { .seconds_quote_is_valid_for .unwrap_or(DEFAULT_QUOTE_TTL_SECS); + let mut ln_backends = HashMap::new(); + + ln_backends.insert( + LnKey::new(CurrencyUnit::Sat, PaymentMethod::Bolt11), + Arc::clone(&ln), + ); + let v1_service = - cdk_axum::create_mint_router(&mint_url, Arc::clone(&mint), Arc::clone(&ln), quote_ttl) - .await?; + cdk_axum::create_mint_router(&mint_url, Arc::clone(&mint), ln_backends, quote_ttl).await?; let mint_service = Router::new() .nest("/", v1_service)