Skip to content

Commit

Permalink
feat: melt startup check into cdk
Browse files Browse the repository at this point in the history
  • Loading branch information
thesimplekid committed Nov 18, 2024
1 parent 682e005 commit 4cefe87
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 93 deletions.
95 changes: 3 additions & 92 deletions crates/cdk-mintd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -344,95 +344,6 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}

async fn check_pending_melt_quotes(
mint: Arc<Mint>,
ln_backends: &HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
) -> Result<()> {
let melt_quotes = mint.localstore.get_melt_quotes().await?;
let pending_quotes: Vec<MeltQuote> = 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<PathBuf> {
let home_dir = home::home_dir().ok_or(anyhow!("Unknown home dir"))?;

Expand Down
4 changes: 4 additions & 0 deletions crates/cdk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
95 changes: 95 additions & 0 deletions crates/cdk/src/mint/start_up_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<MeltQuote> = 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(())
}
}
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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" ];
});
Expand Down

0 comments on commit 4cefe87

Please sign in to comment.