From cf9cacaff4321089e48745393ecd17bdcdb5617f Mon Sep 17 00:00:00 2001 From: thesimplekid Date: Thu, 13 Mar 2025 09:54:40 +0000 Subject: [PATCH] fix: verification of melt quote with empty outputs --- crates/cdk-integration-tests/tests/regtest.rs | 34 +++++++++++++------ crates/cdk/src/mint/melt.rs | 14 +++++--- crates/cdk/src/mint/mint_nut04.rs | 1 + crates/cdk/src/mint/verification.rs | 21 +++++++++--- 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/crates/cdk-integration-tests/tests/regtest.rs b/crates/cdk-integration-tests/tests/regtest.rs index 55a74aa0..7c840fc2 100644 --- a/crates/cdk-integration-tests/tests/regtest.rs +++ b/crates/cdk-integration-tests/tests/regtest.rs @@ -256,42 +256,54 @@ async fn test_restore() -> Result<()> { } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] -async fn test_pay_invoice_twice() -> Result<()> { +async fn test_pay_invoice_twice() -> anyhow::Result<()> { let lnd_client = init_lnd_client().await; - let seed = Mnemonic::generate(12)?.to_seed_normalized(""); + let seed = Mnemonic::generate(12).unwrap().to_seed_normalized(""); let wallet = Wallet::new( &get_mint_url("0"), CurrencyUnit::Sat, - Arc::new(memory::empty().await?), + Arc::new(memory::empty().await.unwrap()), &seed, None, )?; - let mint_quote = wallet.mint_quote(100.into(), None).await?; + let mint_quote = wallet + .mint_quote(100.into(), None) + .await + .expect("Get mint quote"); lnd_client .pay_invoice(mint_quote.request) .await .expect("Could not pay invoice"); - wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60).await?; + wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60) + .await + .expect("Mint invoice timeout not paid"); let proofs = wallet .mint(&mint_quote.id, SplitTarget::default(), None) - .await?; + .await + .expect("Could not mint"); - let mint_amount = proofs.total_amount()?; + let mint_amount = proofs.total_amount().unwrap(); assert_eq!(mint_amount, 100.into()); - let invoice = lnd_client.create_invoice(Some(10)).await?; + let invoice = lnd_client + .create_invoice(Some(10)) + .await + .expect("Could not create invoice"); - let melt_quote = wallet.melt_quote(invoice.clone(), None).await?; + let melt_quote = wallet + .melt_quote(invoice.clone(), None) + .await + .expect("Could not get melt quote"); let melt = wallet.melt(&melt_quote.id).await.unwrap(); - let melt_two = wallet.melt_quote(invoice, None).await?; + let melt_two = wallet.melt_quote(invoice, None).await.unwrap(); let melt_two = wallet.melt(&melt_two.id).await; @@ -307,7 +319,7 @@ async fn test_pay_invoice_twice() -> Result<()> { } } - let balance = wallet.total_balance().await?; + let balance = wallet.total_balance().await.unwrap(); assert_eq!(balance, (Amount::from(100) - melt.fee_paid - melt.amount)); diff --git a/crates/cdk/src/mint/melt.rs b/crates/cdk/src/mint/melt.rs index 87ad883f..0d69000e 100644 --- a/crates/cdk/src/mint/melt.rs +++ b/crates/cdk/src/mint/melt.rs @@ -310,6 +310,8 @@ impl Mint { unit: input_unit, } = self.verify_inputs(&melt_request.inputs).await?; + ensure_cdk!(input_unit.is_some(), Error::UnsupportedUnit); + let input_ys = melt_request.inputs.ys()?; let fee = self.get_proofs_fee(&melt_request.inputs).await?; @@ -343,12 +345,14 @@ impl Mint { ensure_cdk!(sig_flag.ne(&SigFlag::SigAll), Error::SigAllUsedInMelt); if let Some(outputs) = &melt_request.outputs { - let Verification { - amount: _, - unit: output_unit, - } = self.verify_outputs(outputs).await?; + if !outputs.is_empty() { + let Verification { + amount: _, + unit: output_unit, + } = self.verify_outputs(outputs).await?; - ensure_cdk!(input_unit == output_unit, Error::UnsupportedUnit); + ensure_cdk!(input_unit == output_unit, Error::UnsupportedUnit); + } } tracing::debug!("Verified melt quote: {}", melt_request.quote); diff --git a/crates/cdk/src/mint/mint_nut04.rs b/crates/cdk/src/mint/mint_nut04.rs index 8887a775..ce91268d 100644 --- a/crates/cdk/src/mint/mint_nut04.rs +++ b/crates/cdk/src/mint/mint_nut04.rs @@ -327,6 +327,7 @@ impl Mint { )); } + let unit = unit.ok_or(Error::UnsupportedUnit)?; ensure_cdk!(unit == mint_quote.unit, Error::UnsupportedUnit); let mut blind_signatures = Vec::with_capacity(mint_request.outputs.len()); diff --git a/crates/cdk/src/mint/verification.rs b/crates/cdk/src/mint/verification.rs index 679ff9a5..bc43149c 100644 --- a/crates/cdk/src/mint/verification.rs +++ b/crates/cdk/src/mint/verification.rs @@ -8,7 +8,7 @@ use super::{Error, Mint}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Verification { pub amount: Amount, - pub unit: CurrencyUnit, + pub unit: Option, } impl Mint { @@ -171,6 +171,13 @@ impl Mint { /// Checks outputs are unique, of the same unit and not signed before #[instrument(skip_all)] pub async fn verify_outputs(&self, outputs: &[BlindedMessage]) -> Result { + if outputs.is_empty() { + return Ok(Verification { + amount: Amount::ZERO, + unit: None, + }); + } + Mint::check_outputs_unique(outputs)?; self.check_output_already_signed(outputs).await?; @@ -178,7 +185,10 @@ impl Mint { let amount = Amount::try_sum(outputs.iter().map(|o| o.amount).collect::>())?; - Ok(Verification { amount, unit }) + Ok(Verification { + amount, + unit: Some(unit), + }) } /// Verifies inputs @@ -194,7 +204,10 @@ impl Mint { self.verify_proof(proof).await?; } - Ok(Verification { amount, unit }) + Ok(Verification { + amount, + unit: Some(unit), + }) } /// Verify that inputs and outputs are valid and balanced @@ -215,7 +228,7 @@ impl Mint { if output_verification.unit != input_verification.unit { tracing::debug!( - "Output unit {} does not match input unit {}", + "Output unit {:?} does not match input unit {:?}", output_verification.unit, input_verification.unit );