From 4cefe875f184511361ad74a11cb0798230c5b44a Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Mon, 18 Nov 2024 09:08:55 +0000 Subject: [PATCH] feat: melt startup check into cdk --- crates/cdk-mintd/src/main.rs | 95 +-------------------------- crates/cdk/src/error.rs | 4 ++ crates/cdk/src/mint/start_up_check.rs | 95 +++++++++++++++++++++++++++ flake.nix | 2 +- 4 files changed, 103 insertions(+), 93 deletions(-) diff --git a/crates/cdk-mintd/src/main.rs b/crates/cdk-mintd/src/main.rs index 57da5bda..325ddd93 100644 --- a/crates/cdk-mintd/src/main.rs +++ b/crates/cdk-mintd/src/main.rs @@ -14,8 +14,8 @@ use bip39::Mnemonic; use cdk::cdk_database::{self, MintDatabase}; use cdk::cdk_lightning; use cdk::cdk_lightning::MintLightning; -use cdk::mint::{MeltQuote, Mint, MintBuilder, MintMeltLimits}; -use cdk::nuts::{ContactInfo, CurrencyUnit, MeltQuoteState, MintVersion, PaymentMethod}; +use cdk::mint::{MintBuilder, MintMeltLimits}; +use cdk::nuts::{ContactInfo, CurrencyUnit, MintVersion, PaymentMethod}; use cdk::types::LnKey; use cdk_mintd::cli::CLIArgs; use cdk_mintd::config::{self, DatabaseEngine, LnBackend}; @@ -275,7 +275,7 @@ async fn main() -> anyhow::Result<()> { // Checks the status of all pending melt quotes // Pending melt quotes where the payment has gone through inputs are burnt // Pending melt quotes where the payment has **failed** inputs are reset to unspent - check_pending_melt_quotes(Arc::clone(&mint), &ln_backends).await?; + mint.check_pending_melt_quotes().await?; let listen_addr = settings.info.listen_host; let listen_port = settings.info.listen_port; @@ -344,95 +344,6 @@ async fn main() -> anyhow::Result<()> { Ok(()) } -async fn check_pending_melt_quotes( - mint: Arc, - ln_backends: &HashMap + Send + Sync>>, -) -> Result<()> { - let melt_quotes = mint.localstore.get_melt_quotes().await?; - let pending_quotes: Vec = melt_quotes - .into_iter() - .filter(|q| q.state == MeltQuoteState::Pending || q.state == MeltQuoteState::Unknown) - .collect(); - tracing::info!("There are {} pending melt quotes.", pending_quotes.len()); - - for pending_quote in pending_quotes { - tracing::debug!("Checking status for melt quote {}.", pending_quote.id); - let melt_request_ln_key = mint.localstore.get_melt_request(&pending_quote.id).await?; - - let (melt_request, ln_key) = match melt_request_ln_key { - None => ( - None, - LnKey { - unit: pending_quote.unit, - method: PaymentMethod::Bolt11, - }, - ), - Some((melt_request, ln_key)) => (Some(melt_request), ln_key), - }; - - let ln_backend = match ln_backends.get(&ln_key) { - Some(ln_backend) => ln_backend, - None => { - tracing::warn!("No backend for ln key: {:?}", ln_key); - continue; - } - }; - - let pay_invoice_response = ln_backend - .check_outgoing_payment(&pending_quote.request_lookup_id) - .await?; - - match melt_request { - Some(melt_request) => { - match pay_invoice_response.status { - MeltQuoteState::Paid => { - if let Err(err) = mint - .process_melt_request( - &melt_request, - pay_invoice_response.payment_preimage, - pay_invoice_response.total_spent, - ) - .await - { - tracing::error!( - "Could not process melt request for pending quote: {}", - melt_request.quote - ); - tracing::error!("{}", err); - } - } - MeltQuoteState::Unpaid | MeltQuoteState::Unknown | MeltQuoteState::Failed => { - // Payment has not been made we want to unset - tracing::info!("Lightning payment for quote {} failed.", pending_quote.id); - if let Err(err) = mint.process_unpaid_melt(&melt_request).await { - tracing::error!("Could not reset melt quote state: {}", err); - } - } - MeltQuoteState::Pending => { - tracing::warn!( - "LN payment pending, proofs are stuck as pending for quote: {}", - melt_request.quote - ); - // Quote is still pending we do not want to do anything - // continue to check next quote - } - } - } - None => { - tracing::warn!( - "There is no stored melt request for pending melt quote: {}", - pending_quote.id - ); - - mint.localstore - .update_melt_quote_state(&pending_quote.id, pay_invoice_response.status) - .await?; - } - }; - } - Ok(()) -} - fn work_dir() -> Result { let home_dir = home::home_dir().ok_or(anyhow!("Unknown home dir"))?; diff --git a/crates/cdk/src/error.rs b/crates/cdk/src/error.rs index 5ddfac64..dae63c64 100644 --- a/crates/cdk/src/error.rs +++ b/crates/cdk/src/error.rs @@ -243,6 +243,10 @@ pub enum Error { #[cfg(any(feature = "wallet", feature = "mint"))] #[error(transparent)] Database(#[from] crate::cdk_database::Error), + /// Lightning Error + #[cfg(feature = "mint")] + #[error(transparent)] + Lightning(#[from] crate::cdk_lightning::Error), } /// CDK Error Response diff --git a/crates/cdk/src/mint/start_up_check.rs b/crates/cdk/src/mint/start_up_check.rs index 7d3d920b..fa640da0 100644 --- a/crates/cdk/src/mint/start_up_check.rs +++ b/crates/cdk/src/mint/start_up_check.rs @@ -4,6 +4,8 @@ //! These ensure that the status of the mint or melt quote matches in the mint db and on the node. use super::{Error, Mint}; +use crate::mint::{MeltQuote, MeltQuoteState, PaymentMethod}; +use crate::types::LnKey; impl Mint { /// Check the status of all pending mint quotes in the mint db @@ -40,4 +42,97 @@ impl Mint { } Ok(()) } + + /// Checks the states of melt quotes that are **PENDING** or **UNKNOWN** to the mint with the ln node + pub async fn check_pending_melt_quotes(&self) -> Result<(), Error> { + let melt_quotes = self.localstore.get_melt_quotes().await?; + let pending_quotes: Vec = melt_quotes + .into_iter() + .filter(|q| q.state == MeltQuoteState::Pending || q.state == MeltQuoteState::Unknown) + .collect(); + tracing::info!("There are {} pending melt quotes.", pending_quotes.len()); + + for pending_quote in pending_quotes { + tracing::debug!("Checking status for melt quote {}.", pending_quote.id); + let melt_request_ln_key = self.localstore.get_melt_request(&pending_quote.id).await?; + + let (melt_request, ln_key) = match melt_request_ln_key { + None => { + let ln_key = LnKey { + unit: pending_quote.unit, + method: PaymentMethod::Bolt11, + }; + + (None, ln_key) + } + Some((melt_request, ln_key)) => (Some(melt_request), ln_key), + }; + + let ln_backend = match self.ln.get(&ln_key) { + Some(ln_backend) => ln_backend, + None => { + tracing::warn!("No backend for ln key: {:?}", ln_key); + continue; + } + }; + + let pay_invoice_response = ln_backend + .check_outgoing_payment(&pending_quote.request_lookup_id) + .await?; + + match melt_request { + Some(melt_request) => { + match pay_invoice_response.status { + MeltQuoteState::Paid => { + if let Err(err) = self + .process_melt_request( + &melt_request, + pay_invoice_response.payment_preimage, + pay_invoice_response.total_spent, + ) + .await + { + tracing::error!( + "Could not process melt request for pending quote: {}", + melt_request.quote + ); + tracing::error!("{}", err); + } + } + MeltQuoteState::Unpaid + | MeltQuoteState::Unknown + | MeltQuoteState::Failed => { + // Payment has not been made we want to unset + tracing::info!( + "Lightning payment for quote {} failed.", + pending_quote.id + ); + if let Err(err) = self.process_unpaid_melt(&melt_request).await { + tracing::error!("Could not reset melt quote state: {}", err); + } + } + MeltQuoteState::Pending => { + tracing::warn!( + "LN payment pending, proofs are stuck as pending for quote: {}", + melt_request.quote + ); + // Quote is still pending we do not want to do anything + // continue to check next quote + } + } + } + None => { + tracing::warn!( + "There is no stored melt request for pending melt quote: {}", + pending_quote.id + ); + + self.localstore + .update_melt_quote_state(&pending_quote.id, pay_invoice_response.status) + .await?; + } + }; + } + Ok(()) + } } diff --git a/flake.nix b/flake.nix index aebb30d0..f1931d2b 100644 --- a/flake.nix +++ b/flake.nix @@ -53,7 +53,7 @@ targets = [ "wasm32-unknown-unknown" ]; # wasm }; - # Nighly for creating lock files + # Nightly for creating lock files nightly_toolchain = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { extensions = [ "rustfmt" "clippy" "rust-analyzer" ]; });