Просмотр исходного кода

Remove dupllicate files (#1523)

* chore: remove unused file post custum merge

* fix: do not delete mint quotes
tsk 2 недель назад
Родитель
Сommit
1919be3e68

+ 6 - 2
crates/cdk/src/wallet/issue/bolt11.rs

@@ -329,8 +329,12 @@ impl Wallet {
             &keys,
             &keys,
         )?;
         )?;
 
 
-        // Remove filled quote from store
-        self.localstore.remove_mint_quote(&quote_info.id).await?;
+        // Update quote with issued amount
+        let mut quote_info = quote_info;
+        quote_info.state = MintQuoteState::Issued;
+        quote_info.amount_issued = proofs.total_amount()?;
+
+        self.localstore.add_mint_quote(quote_info.clone()).await?;
 
 
         let proof_infos = proofs
         let proof_infos = proofs
             .iter()
             .iter()

+ 9 - 5
crates/cdk/src/wallet/issue/custom.rs

@@ -2,7 +2,7 @@ use std::collections::HashMap;
 
 
 use cdk_common::nut04::MintMethodOptions;
 use cdk_common::nut04::MintMethodOptions;
 use cdk_common::wallet::{MintQuote, Transaction, TransactionDirection};
 use cdk_common::wallet::{MintQuote, Transaction, TransactionDirection};
-use cdk_common::{Proofs, SecretKey};
+use cdk_common::{MintQuoteState, Proofs, SecretKey};
 use tracing::instrument;
 use tracing::instrument;
 
 
 use crate::amount::SplitTarget;
 use crate::amount::SplitTarget;
@@ -167,8 +167,8 @@ impl Wallet {
             signature: None,
             signature: None,
         };
         };
 
 
-        if let Some(secret_key) = quote_info.secret_key {
-            request.sign(secret_key)?;
+        if let Some(secret_key) = &quote_info.secret_key {
+            request.sign(secret_key.clone())?;
         }
         }
 
 
         let mint_res = self.client.post_mint(request).await?;
         let mint_res = self.client.post_mint(request).await?;
@@ -194,8 +194,12 @@ impl Wallet {
             &keys,
             &keys,
         )?;
         )?;
 
 
-        // Remove filled quote from store
-        self.localstore.remove_mint_quote(&quote_info.id).await?;
+        // Update quote with issued amount
+        let mut quote_info = quote_info;
+        quote_info.state = MintQuoteState::Issued;
+        quote_info.amount_issued = proofs.total_amount()?;
+
+        self.localstore.add_mint_quote(quote_info.clone()).await?;
 
 
         let proof_infos = proofs
         let proof_infos = proofs
             .iter()
             .iter()

+ 0 - 367
crates/cdk/src/wallet/issue/issue_bolt11.rs

@@ -1,367 +0,0 @@
-use std::collections::HashMap;
-
-use cdk_common::nut04::MintMethodOptions;
-use cdk_common::wallet::{MintQuote, Transaction, TransactionDirection};
-use cdk_common::PaymentMethod;
-use tracing::instrument;
-
-use crate::amount::SplitTarget;
-use crate::dhke::construct_proofs;
-use crate::nuts::nut00::ProofsMethods;
-use crate::nuts::{
-    nut12, MintQuoteBolt11Request, MintQuoteBolt11Response, MintRequest, PreMintSecrets, Proofs,
-    SecretKey, SpendingConditions, State,
-};
-use crate::types::ProofInfo;
-use crate::util::unix_time;
-use crate::wallet::MintQuoteState;
-use crate::{Amount, Error, Wallet};
-
-impl Wallet {
-    /// Mint Quote
-    /// # Synopsis
-    /// ```rust,no_run
-    /// use std::sync::Arc;
-    ///
-    /// use cdk::amount::Amount;
-    /// use cdk::nuts::CurrencyUnit;
-    /// use cdk::wallet::Wallet;
-    /// use cdk_sqlite::wallet::memory;
-    /// use rand::random;
-    ///
-    /// #[tokio::main]
-    /// async fn main() -> anyhow::Result<()> {
-    ///     let seed = random::<[u8; 64]>();
-    ///     let mint_url = "https://fake.thesimplekid.dev";
-    ///     let unit = CurrencyUnit::Sat;
-    ///
-    ///     let localstore = memory::empty().await?;
-    ///     let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None)?;
-    ///     let amount = Amount::from(100);
-    ///
-    ///     let quote = wallet.mint_quote(amount, None).await?;
-    ///     Ok(())
-    /// }
-    /// ```
-    #[instrument(skip(self))]
-    pub async fn mint_quote(
-        &self,
-        amount: Amount,
-        description: Option<String>,
-    ) -> Result<MintQuote, Error> {
-        let mint_info = self.load_mint_info().await?;
-
-        let mint_url = self.mint_url.clone();
-        let unit = self.unit.clone();
-
-        // If we have a description, we check that the mint supports it.
-        if description.is_some() {
-            let settings = mint_info
-                .nuts
-                .nut04
-                .get_settings(&unit, &crate::nuts::PaymentMethod::Bolt11)
-                .ok_or(Error::UnsupportedUnit)?;
-
-            match settings.options {
-                Some(MintMethodOptions::Bolt11 { description }) if description => (),
-                _ => return Err(Error::InvoiceDescriptionUnsupported),
-            }
-        }
-
-        let secret_key = SecretKey::generate();
-
-        let request = MintQuoteBolt11Request {
-            amount,
-            unit: unit.clone(),
-            description,
-            pubkey: Some(secret_key.public_key()),
-        };
-
-        let quote_res = self.client.post_mint_quote(request).await?;
-
-        let quote = MintQuote::new(
-            quote_res.quote,
-            mint_url,
-            PaymentMethod::Bolt11,
-            Some(amount),
-            unit,
-            quote_res.request,
-            quote_res.expiry.unwrap_or(0),
-            Some(secret_key),
-        );
-
-        self.localstore.add_mint_quote(quote.clone()).await?;
-
-        Ok(quote)
-    }
-
-    /// Check mint quote status
-    #[instrument(skip(self, quote_id))]
-    pub async fn mint_quote_state(
-        &self,
-        quote_id: &str,
-    ) -> Result<MintQuoteBolt11Response<String>, Error> {
-        let response = self.client.get_mint_quote_status(quote_id).await?;
-
-        match self.localstore.get_mint_quote(quote_id).await? {
-            Some(quote) => {
-                let mut quote = quote;
-
-                quote.state = response.state;
-                self.localstore.add_mint_quote(quote).await?;
-            }
-            None => {
-                tracing::info!("Quote mint {} unknown", quote_id);
-            }
-        }
-
-        Ok(response)
-    }
-
-    /// Check status of pending mint quotes
-    #[instrument(skip(self))]
-    pub async fn check_all_mint_quotes(&self) -> Result<Amount, Error> {
-        let mint_quotes = self.localstore.get_unissued_mint_quotes().await?;
-        let mut total_amount = Amount::ZERO;
-
-        for mint_quote in mint_quotes {
-            match mint_quote.payment_method {
-                PaymentMethod::Bolt11 => {
-                    let mint_quote_response = self.mint_quote_state(&mint_quote.id).await?;
-
-                    if mint_quote_response.state == MintQuoteState::Paid {
-                        let proofs = self
-                            .mint(&mint_quote.id, SplitTarget::default(), None)
-                            .await?;
-                        total_amount += proofs.total_amount()?;
-                    }
-                }
-                PaymentMethod::Bolt12 => {
-                    let mint_quote_response = self.mint_bolt12_quote_state(&mint_quote.id).await?;
-                    if mint_quote_response.amount_paid > mint_quote_response.amount_issued {
-                        let proofs = self
-                            .mint_bolt12(&mint_quote.id, None, SplitTarget::default(), None)
-                            .await?;
-                        total_amount += proofs.total_amount()?;
-                    }
-                }
-                PaymentMethod::Custom(_) => {
-                    tracing::warn!("We cannot check unknown types");
-                }
-            }
-        }
-        Ok(total_amount)
-    }
-
-    /// Get active mint quotes
-    /// Returns mint quotes that are not expired and not yet issued.
-    #[instrument(skip(self))]
-    pub async fn get_active_mint_quotes(&self) -> Result<Vec<MintQuote>, Error> {
-        let mut mint_quotes = self.localstore.get_mint_quotes().await?;
-        let unix_time = unix_time();
-        mint_quotes.retain(|quote| {
-            quote.mint_url == self.mint_url
-                && quote.state != MintQuoteState::Issued
-                && quote.expiry > unix_time
-        });
-        Ok(mint_quotes)
-    }
-
-    /// Get unissued mint quotes
-    /// Returns bolt11 quotes where nothing has been issued yet (amount_issued = 0) and all bolt12 quotes.
-    /// Includes unpaid bolt11 quotes to allow checking with the mint if they've been paid (wallet state may be outdated).
-    /// Filters out quotes from other mints. Does not filter by expiry time to allow
-    /// checking with the mint if expired quotes can still be minted.
-    #[instrument(skip(self))]
-    pub async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, Error> {
-        let mut pending_quotes = self.localstore.get_unissued_mint_quotes().await?;
-        pending_quotes.retain(|quote| quote.mint_url == self.mint_url);
-        Ok(pending_quotes)
-    }
-
-    /// Mint
-    /// # Synopsis
-    /// ```rust,no_run
-    /// use std::sync::Arc;
-    ///
-    /// use anyhow::Result;
-    /// use cdk::amount::{Amount, SplitTarget};
-    /// use cdk::nuts::nut00::ProofsMethods;
-    /// use cdk::nuts::CurrencyUnit;
-    /// use cdk::wallet::Wallet;
-    /// use cdk_sqlite::wallet::memory;
-    /// use rand::random;
-    ///
-    /// #[tokio::main]
-    /// async fn main() -> Result<()> {
-    ///     let seed = random::<[u8; 64]>();
-    ///     let mint_url = "https://fake.thesimplekid.dev";
-    ///     let unit = CurrencyUnit::Sat;
-    ///
-    ///     let localstore = memory::empty().await?;
-    ///     let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None).unwrap();
-    ///     let amount = Amount::from(100);
-    ///
-    ///     let quote = wallet.mint_quote(amount, None).await?;
-    ///     let quote_id = quote.id;
-    ///     // To be called after quote request is paid
-    ///     let minted_proofs = wallet.mint(&quote_id, SplitTarget::default(), None).await?;
-    ///     let minted_amount = minted_proofs.total_amount()?;
-    ///
-    ///     Ok(())
-    /// }
-    /// ```
-    #[instrument(skip(self))]
-    pub async fn mint(
-        &self,
-        quote_id: &str,
-        amount_split_target: SplitTarget,
-        spending_conditions: Option<SpendingConditions>,
-    ) -> Result<Proofs, Error> {
-        let active_keyset_id = self.fetch_active_keyset().await?.id;
-        let fee_and_amounts = self
-            .get_keyset_fees_and_amounts_by_id(active_keyset_id)
-            .await?;
-
-        let quote_info = self
-            .localstore
-            .get_mint_quote(quote_id)
-            .await?
-            .ok_or(Error::UnknownQuote)?;
-
-        if quote_info.payment_method != PaymentMethod::Bolt11 {
-            return Err(Error::UnsupportedPaymentMethod);
-        }
-
-        let amount_mintable = quote_info.amount_mintable();
-
-        if amount_mintable == Amount::ZERO {
-            tracing::debug!("Amount mintable 0.");
-            return Err(Error::AmountUndefined);
-        }
-
-        let unix_time = unix_time();
-
-        if quote_info.expiry < unix_time && quote_info.expiry != 0 {
-            tracing::warn!("Attempting to mint with expired quote.");
-        }
-
-        let split_target = match amount_split_target {
-            SplitTarget::None => {
-                self.determine_split_target_values(amount_mintable, &fee_and_amounts)
-                    .await?
-            }
-            s => s,
-        };
-
-        let premint_secrets = match &spending_conditions {
-            Some(spending_conditions) => PreMintSecrets::with_conditions(
-                active_keyset_id,
-                amount_mintable,
-                &split_target,
-                spending_conditions,
-                &fee_and_amounts,
-            )?,
-            None => {
-                let amount_split =
-                    amount_mintable.split_targeted(&split_target, &fee_and_amounts)?;
-                let num_secrets = amount_split.len() as u32;
-
-                tracing::debug!(
-                    "Incrementing keyset {} counter by {}",
-                    active_keyset_id,
-                    num_secrets
-                );
-
-                // Atomically get the counter range we need
-                let new_counter = self
-                    .localstore
-                    .increment_keyset_counter(&active_keyset_id, num_secrets)
-                    .await?;
-
-                let count = new_counter - num_secrets;
-
-                PreMintSecrets::from_seed(
-                    active_keyset_id,
-                    count,
-                    &self.seed,
-                    amount_mintable,
-                    &split_target,
-                    &fee_and_amounts,
-                )?
-            }
-        };
-
-        let mut request = MintRequest {
-            quote: quote_id.to_string(),
-            outputs: premint_secrets.blinded_messages(),
-            signature: None,
-        };
-
-        if let Some(secret_key) = &quote_info.secret_key {
-            request.sign(secret_key.clone())?;
-        }
-
-        let mint_res = self.client.post_mint(request).await?;
-
-        let keys = self.load_keyset_keys(active_keyset_id).await?;
-
-        // Verify the signature DLEQ is valid
-        {
-            for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
-                let keys = self.load_keyset_keys(sig.keyset_id).await?;
-                let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
-                match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
-                    Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
-                    Err(_) => return Err(Error::CouldNotVerifyDleq),
-                }
-            }
-        }
-
-        let proofs = construct_proofs(
-            mint_res.signatures,
-            premint_secrets.rs(),
-            premint_secrets.secrets(),
-            &keys,
-        )?;
-
-        // Remove filled quote from store
-        self.localstore.remove_mint_quote(&quote_info.id).await?;
-
-        let proof_infos = proofs
-            .iter()
-            .map(|proof| {
-                ProofInfo::new(
-                    proof.clone(),
-                    self.mint_url.clone(),
-                    State::Unspent,
-                    quote_info.unit.clone(),
-                )
-            })
-            .collect::<Result<Vec<ProofInfo>, _>>()?;
-
-        // Add new proofs to store
-        self.localstore.update_proofs(proof_infos, vec![]).await?;
-
-        // Add transaction to store
-        self.localstore
-            .add_transaction(Transaction {
-                mint_url: self.mint_url.clone(),
-                direction: TransactionDirection::Incoming,
-                amount: proofs.total_amount()?,
-                fee: Amount::ZERO,
-                unit: self.unit.clone(),
-                ys: proofs.ys()?,
-                timestamp: unix_time,
-                memo: None,
-                metadata: HashMap::new(),
-                quote_id: Some(quote_id.to_string()),
-                payment_request: Some(quote_info.request),
-                payment_proof: None,
-                payment_method: Some(quote_info.payment_method),
-            })
-            .await?;
-
-        Ok(proofs)
-    }
-}

+ 0 - 274
crates/cdk/src/wallet/issue/issue_bolt12.rs

@@ -1,274 +0,0 @@
-use std::collections::HashMap;
-
-use cdk_common::nut04::MintMethodOptions;
-use cdk_common::nut25::MintQuoteBolt12Request;
-use cdk_common::wallet::{Transaction, TransactionDirection};
-use cdk_common::{Proofs, SecretKey};
-use tracing::instrument;
-
-use crate::amount::SplitTarget;
-use crate::dhke::construct_proofs;
-use crate::nuts::nut00::ProofsMethods;
-use crate::nuts::{
-    nut12, MintQuoteBolt12Response, MintRequest, PaymentMethod, PreMintSecrets, SpendingConditions,
-    State,
-};
-use crate::types::ProofInfo;
-use crate::util::unix_time;
-use crate::wallet::MintQuote;
-use crate::{Amount, Error, Wallet};
-
-impl Wallet {
-    /// Mint Bolt12
-    #[instrument(skip(self))]
-    pub async fn mint_bolt12_quote(
-        &self,
-        amount: Option<Amount>,
-        description: Option<String>,
-    ) -> Result<MintQuote, Error> {
-        let mint_info = self.load_mint_info().await?;
-
-        let mint_url = self.mint_url.clone();
-        let unit = &self.unit;
-
-        // If we have a description, we check that the mint supports it.
-        if description.is_some() {
-            let mint_method_settings = mint_info
-                .nuts
-                .nut04
-                .get_settings(unit, &crate::nuts::PaymentMethod::Bolt12)
-                .ok_or(Error::UnsupportedUnit)?;
-
-            match mint_method_settings.options {
-                Some(MintMethodOptions::Bolt11 { description }) if description => (),
-                _ => return Err(Error::InvoiceDescriptionUnsupported),
-            }
-        }
-
-        let secret_key = SecretKey::generate();
-
-        let mint_request = MintQuoteBolt12Request {
-            amount,
-            unit: self.unit.clone(),
-            description,
-            pubkey: secret_key.public_key(),
-        };
-
-        let quote_res = self.client.post_mint_bolt12_quote(mint_request).await?;
-
-        let quote = MintQuote::new(
-            quote_res.quote,
-            mint_url,
-            PaymentMethod::Bolt12,
-            amount,
-            unit.clone(),
-            quote_res.request,
-            quote_res.expiry.unwrap_or(0),
-            Some(secret_key),
-        );
-
-        self.localstore.add_mint_quote(quote.clone()).await?;
-
-        Ok(quote)
-    }
-
-    /// Mint bolt12
-    #[instrument(skip(self))]
-    pub async fn mint_bolt12(
-        &self,
-        quote_id: &str,
-        amount: Option<Amount>,
-        amount_split_target: SplitTarget,
-        spending_conditions: Option<SpendingConditions>,
-    ) -> Result<Proofs, Error> {
-        let active_keyset_id = self.fetch_active_keyset().await?.id;
-        let fee_and_amounts = self
-            .get_keyset_fees_and_amounts_by_id(active_keyset_id)
-            .await?;
-
-        let quote_info = self.localstore.get_mint_quote(quote_id).await?;
-
-        let quote_info = if let Some(quote) = quote_info {
-            if quote.expiry < unix_time() && quote.expiry != 0 {
-                tracing::info!("Attempting to mint expired quote.");
-            }
-
-            quote.clone()
-        } else {
-            return Err(Error::UnknownQuote);
-        };
-
-        let (quote_info, amount) = match amount {
-            Some(amount) => (quote_info, amount),
-            None => {
-                // If an amount it not supplied with check the status of the quote
-                // The mint will tell us how much can be minted
-                let state = self.mint_bolt12_quote_state(quote_id).await?;
-
-                let quote_info = self
-                    .localstore
-                    .get_mint_quote(quote_id)
-                    .await?
-                    .ok_or(Error::UnknownQuote)?;
-
-                (quote_info, state.amount_paid - state.amount_issued)
-            }
-        };
-
-        if amount == Amount::ZERO {
-            tracing::error!("Cannot mint zero amount.");
-            return Err(Error::UnpaidQuote);
-        }
-
-        let split_target = match amount_split_target {
-            SplitTarget::None => {
-                self.determine_split_target_values(amount, &fee_and_amounts)
-                    .await?
-            }
-            s => s,
-        };
-
-        let premint_secrets = match &spending_conditions {
-            Some(spending_conditions) => PreMintSecrets::with_conditions(
-                active_keyset_id,
-                amount,
-                &split_target,
-                spending_conditions,
-                &fee_and_amounts,
-            )?,
-            None => {
-                let amount_split = amount.split_targeted(&split_target, &fee_and_amounts)?;
-                let num_secrets = amount_split.len() as u32;
-
-                tracing::debug!(
-                    "Incrementing keyset {} counter by {}",
-                    active_keyset_id,
-                    num_secrets
-                );
-
-                // Atomically get the counter range we need
-                let new_counter = self
-                    .localstore
-                    .increment_keyset_counter(&active_keyset_id, num_secrets)
-                    .await?;
-
-                let count = new_counter - num_secrets;
-
-                PreMintSecrets::from_seed(
-                    active_keyset_id,
-                    count,
-                    &self.seed,
-                    amount,
-                    &split_target,
-                    &fee_and_amounts,
-                )?
-            }
-        };
-
-        let mut request = MintRequest {
-            quote: quote_id.to_string(),
-            outputs: premint_secrets.blinded_messages(),
-            signature: None,
-        };
-
-        if let Some(secret_key) = quote_info.secret_key.clone() {
-            request.sign(secret_key)?;
-        } else {
-            tracing::error!("Signature is required for bolt12.");
-            return Err(Error::SignatureMissingOrInvalid);
-        }
-
-        let mint_res = self.client.post_mint(request).await?;
-
-        let keys = self.load_keyset_keys(active_keyset_id).await?;
-
-        // Verify the signature DLEQ is valid
-        {
-            for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
-                let keys = self.load_keyset_keys(sig.keyset_id).await?;
-                let key = keys.amount_key(sig.amount).ok_or(Error::AmountKey)?;
-                match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
-                    Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
-                    Err(_) => return Err(Error::CouldNotVerifyDleq),
-                }
-            }
-        }
-
-        let proofs = construct_proofs(
-            mint_res.signatures,
-            premint_secrets.rs(),
-            premint_secrets.secrets(),
-            &keys,
-        )?;
-
-        // Update quote with issued amount
-        let mut quote_info = self
-            .localstore
-            .get_mint_quote(quote_id)
-            .await?
-            .ok_or(Error::UnpaidQuote)?;
-        quote_info.amount_issued += proofs.total_amount()?;
-
-        self.localstore.add_mint_quote(quote_info.clone()).await?;
-
-        let proof_infos = proofs
-            .iter()
-            .map(|proof| {
-                ProofInfo::new(
-                    proof.clone(),
-                    self.mint_url.clone(),
-                    State::Unspent,
-                    quote_info.unit.clone(),
-                )
-            })
-            .collect::<Result<Vec<ProofInfo>, _>>()?;
-
-        // Add new proofs to store
-        self.localstore.update_proofs(proof_infos, vec![]).await?;
-
-        // Add transaction to store
-        self.localstore
-            .add_transaction(Transaction {
-                mint_url: self.mint_url.clone(),
-                direction: TransactionDirection::Incoming,
-                amount: proofs.total_amount()?,
-                fee: Amount::ZERO,
-                unit: self.unit.clone(),
-                ys: proofs.ys()?,
-                timestamp: unix_time(),
-                memo: None,
-                metadata: HashMap::new(),
-                quote_id: Some(quote_id.to_string()),
-                payment_request: Some(quote_info.request),
-                payment_proof: None,
-                payment_method: Some(quote_info.payment_method),
-            })
-            .await?;
-
-        Ok(proofs)
-    }
-
-    /// Check mint quote status
-    #[instrument(skip(self, quote_id))]
-    pub async fn mint_bolt12_quote_state(
-        &self,
-        quote_id: &str,
-    ) -> Result<MintQuoteBolt12Response<String>, Error> {
-        let response = self.client.get_mint_quote_bolt12_status(quote_id).await?;
-
-        match self.localstore.get_mint_quote(quote_id).await? {
-            Some(quote) => {
-                let mut quote = quote;
-                quote.amount_issued = response.amount_issued;
-                quote.amount_paid = response.amount_paid;
-
-                self.localstore.add_mint_quote(quote).await?;
-            }
-            None => {
-                tracing::info!("Quote mint {} unknown", quote_id);
-            }
-        }
-
-        Ok(response)
-    }
-}

+ 0 - 519
crates/cdk/src/wallet/melt/melt_bolt11.rs

@@ -1,519 +0,0 @@
-use std::collections::HashMap;
-use std::str::FromStr;
-
-use cdk_common::amount::SplitTarget;
-use cdk_common::wallet::{Transaction, TransactionDirection};
-use cdk_common::PaymentMethod;
-use lightning_invoice::Bolt11Invoice;
-use tracing::instrument;
-
-use crate::amount::to_unit;
-use crate::dhke::construct_proofs;
-use crate::nuts::nut00::ProofsMethods;
-use crate::nuts::{
-    CurrencyUnit, MeltOptions, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MeltRequest,
-    PreMintSecrets, Proofs, State,
-};
-use crate::types::{Melted, ProofInfo};
-use crate::util::unix_time;
-use crate::wallet::send::split_proofs_for_send;
-use crate::wallet::MeltQuote;
-use crate::{ensure_cdk, Amount, Error, Wallet};
-
-impl Wallet {
-    /// Melt Quote
-    /// # Synopsis
-    /// ```rust,no_run
-    ///  use std::sync::Arc;
-    ///
-    ///  use cdk_sqlite::wallet::memory;
-    ///  use cdk::nuts::CurrencyUnit;
-    ///  use cdk::wallet::Wallet;
-    ///  use rand::random;
-    ///
-    /// #[tokio::main]
-    /// async fn main() -> anyhow::Result<()> {
-    ///     let seed = random::<[u8; 64]>();
-    ///     let mint_url = "https://fake.thesimplekid.dev";
-    ///     let unit = CurrencyUnit::Sat;
-    ///
-    ///     let localstore = memory::empty().await?;
-    ///     let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None).unwrap();
-    ///     let bolt11 = "lnbc100n1pnvpufspp5djn8hrq49r8cghwye9kqw752qjncwyfnrprhprpqk43mwcy4yfsqdq5g9kxy7fqd9h8vmmfvdjscqzzsxqyz5vqsp5uhpjt36rj75pl7jq2sshaukzfkt7uulj456s4mh7uy7l6vx7lvxs9qxpqysgqedwz08acmqwtk8g4vkwm2w78suwt2qyzz6jkkwcgrjm3r3hs6fskyhvud4fan3keru7emjm8ygqpcrwtlmhfjfmer3afs5hhwamgr4cqtactdq".to_string();
-    ///     let quote = wallet.melt_quote(bolt11, None).await?;
-    ///
-    ///     Ok(())
-    /// }
-    /// ```
-    #[instrument(skip(self, request))]
-    pub async fn melt_quote(
-        &self,
-        request: String,
-        options: Option<MeltOptions>,
-    ) -> Result<MeltQuote, Error> {
-        let invoice = Bolt11Invoice::from_str(&request)?;
-
-        let quote_request = MeltQuoteBolt11Request {
-            request: invoice.clone(),
-            unit: self.unit.clone(),
-            options,
-        };
-
-        let quote_res = self.client.post_melt_quote(quote_request).await?;
-
-        if self.unit == CurrencyUnit::Msat || self.unit == CurrencyUnit::Sat {
-            let amount_msat = options
-                .map(|opt| opt.amount_msat().into())
-                .or_else(|| invoice.amount_milli_satoshis())
-                .ok_or(Error::InvoiceAmountUndefined)?;
-
-            let amount_quote_unit = to_unit(amount_msat, &CurrencyUnit::Msat, &self.unit)?;
-
-            if quote_res.amount != amount_quote_unit {
-                tracing::warn!(
-                    "Mint returned incorrect quote amount. Expected {}, got {}",
-                    amount_quote_unit,
-                    quote_res.amount
-                );
-                return Err(Error::IncorrectQuoteAmount);
-            }
-        }
-
-        let quote = MeltQuote {
-            id: quote_res.quote,
-            amount: quote_res.amount,
-            request,
-            unit: self.unit.clone(),
-            fee_reserve: quote_res.fee_reserve,
-            state: quote_res.state,
-            expiry: quote_res.expiry,
-            payment_preimage: quote_res.payment_preimage,
-            payment_method: PaymentMethod::Bolt11,
-        };
-
-        self.localstore.add_melt_quote(quote.clone()).await?;
-
-        Ok(quote)
-    }
-
-    /// Melt quote status
-    #[instrument(skip(self, quote_id))]
-    pub async fn melt_quote_status(
-        &self,
-        quote_id: &str,
-    ) -> Result<MeltQuoteBolt11Response<String>, Error> {
-        let response = self.client.get_melt_quote_status(quote_id).await?;
-
-        match self.localstore.get_melt_quote(quote_id).await? {
-            Some(quote) => {
-                let mut quote = quote;
-
-                if let Err(e) = self
-                    .add_transaction_for_pending_melt(&quote, &response)
-                    .await
-                {
-                    tracing::error!("Failed to add transaction for pending melt: {}", e);
-                }
-
-                quote.state = response.state;
-                self.localstore.add_melt_quote(quote).await?;
-            }
-            None => {
-                tracing::info!("Quote melt {} unknown", quote_id);
-            }
-        }
-
-        Ok(response)
-    }
-
-    /// Melt specific proofs
-    #[instrument(skip(self, proofs))]
-    pub async fn melt_proofs(&self, quote_id: &str, proofs: Proofs) -> Result<Melted, Error> {
-        self.melt_proofs_with_metadata(quote_id, proofs, HashMap::new())
-            .await
-    }
-
-    /// Melt specific proofs
-    #[instrument(skip(self, proofs))]
-    pub async fn melt_proofs_with_metadata(
-        &self,
-        quote_id: &str,
-        proofs: Proofs,
-        metadata: HashMap<String, String>,
-    ) -> Result<Melted, Error> {
-        let active_keyset_id = self.fetch_active_keyset().await?.id;
-        let mut quote_info = self
-            .localstore
-            .get_melt_quote(quote_id)
-            .await?
-            .ok_or(Error::UnknownQuote)?;
-
-        ensure_cdk!(
-            quote_info.expiry.gt(&unix_time()),
-            Error::ExpiredQuote(quote_info.expiry, unix_time())
-        );
-
-        let proofs_total = proofs.total_amount()?;
-        if proofs_total < quote_info.amount + quote_info.fee_reserve {
-            return Err(Error::InsufficientFunds);
-        }
-
-        // Since the proofs may be external (not in our database), add them first
-        let proofs_info = proofs
-            .clone()
-            .into_iter()
-            .map(|p| ProofInfo::new(p, self.mint_url.clone(), State::Pending, self.unit.clone()))
-            .collect::<Result<Vec<ProofInfo>, _>>()?;
-
-        self.localstore.update_proofs(proofs_info, vec![]).await?;
-
-        // Calculate change accounting for input fees
-        // The mint deducts input fees from available funds before calculating change
-        let input_fee = self.get_proofs_fee(&proofs).await?.total;
-        let change_amount = proofs_total - quote_info.amount - input_fee;
-
-        let premint_secrets = if change_amount <= Amount::ZERO {
-            PreMintSecrets::new(active_keyset_id)
-        } else {
-            // TODO: consolidate this calculation with from_seed_blank into a shared function
-            // Calculate how many secrets will be needed using the same logic as from_seed_blank
-            let num_secrets =
-                ((u64::from(change_amount) as f64).log2().ceil() as u64).max(1) as u32;
-
-            tracing::debug!(
-                "Incrementing keyset {} counter by {}",
-                active_keyset_id,
-                num_secrets
-            );
-
-            // Atomically get the counter range we need
-            let new_counter = self
-                .localstore
-                .increment_keyset_counter(&active_keyset_id, num_secrets)
-                .await?;
-
-            let count = new_counter - num_secrets;
-
-            PreMintSecrets::from_seed_blank(active_keyset_id, count, &self.seed, change_amount)?
-        };
-
-        let request = MeltRequest::new(
-            quote_id.to_string(),
-            proofs.clone(),
-            Some(premint_secrets.blinded_messages()),
-        );
-
-        let melt_response = match quote_info.payment_method {
-            cdk_common::PaymentMethod::Bolt11 => {
-                self.try_proof_operation_or_reclaim(
-                    request.inputs().clone(),
-                    self.client.post_melt(request),
-                )
-                .await?
-            }
-            cdk_common::PaymentMethod::Bolt12 => {
-                self.try_proof_operation_or_reclaim(
-                    request.inputs().clone(),
-                    self.client.post_melt_bolt12(request),
-                )
-                .await?
-            }
-            cdk_common::PaymentMethod::Custom(_) => {
-                return Err(Error::UnsupportedPaymentMethod);
-            }
-        };
-
-        let active_keys = self.load_keyset_keys(active_keyset_id).await?;
-
-        let change_proofs = match melt_response.change {
-            Some(change) => {
-                let num_change_proof = change.len();
-
-                let num_change_proof = match (
-                    premint_secrets.len() < num_change_proof,
-                    premint_secrets.secrets().len() < num_change_proof,
-                ) {
-                    (true, _) | (_, true) => {
-                        tracing::error!("Mismatch in change promises to change");
-                        premint_secrets.len()
-                    }
-                    _ => num_change_proof,
-                };
-
-                Some(construct_proofs(
-                    change,
-                    premint_secrets.rs()[..num_change_proof].to_vec(),
-                    premint_secrets.secrets()[..num_change_proof].to_vec(),
-                    &active_keys,
-                )?)
-            }
-            None => None,
-        };
-
-        let payment_preimage = melt_response.payment_preimage.clone();
-
-        let melted = Melted::from_proofs(
-            melt_response.state,
-            melt_response.payment_preimage,
-            quote_info.amount,
-            proofs.clone(),
-            change_proofs.clone(),
-        )?;
-
-        let change_proof_infos = match change_proofs {
-            Some(change_proofs) => {
-                tracing::debug!(
-                    "Change amount returned from melt: {}",
-                    change_proofs.total_amount()?
-                );
-
-                change_proofs
-                    .into_iter()
-                    .map(|proof| {
-                        ProofInfo::new(
-                            proof,
-                            self.mint_url.clone(),
-                            State::Unspent,
-                            quote_info.unit.clone(),
-                        )
-                    })
-                    .collect::<Result<Vec<ProofInfo>, _>>()?
-            }
-            None => Vec::new(),
-        };
-
-        quote_info.state = cdk_common::MeltQuoteState::Paid;
-
-        let payment_request = quote_info.request.clone();
-        let payment_method = quote_info.payment_method.clone();
-        self.localstore.add_melt_quote(quote_info).await?;
-
-        let deleted_ys = proofs.ys()?;
-
-        self.localstore
-            .update_proofs(change_proof_infos, deleted_ys)
-            .await?;
-
-        // Add transaction to store
-        self.localstore
-            .add_transaction(Transaction {
-                mint_url: self.mint_url.clone(),
-                direction: TransactionDirection::Outgoing,
-                amount: melted.amount,
-                fee: melted.fee_paid,
-                unit: self.unit.clone(),
-                ys: proofs.ys()?,
-                timestamp: unix_time(),
-                memo: None,
-                metadata,
-                quote_id: Some(quote_id.to_string()),
-                payment_request: Some(payment_request),
-                payment_proof: payment_preimage,
-                payment_method: Some(payment_method),
-            })
-            .await?;
-
-        Ok(melted)
-    }
-
-    /// Melt
-    /// # Synopsis
-    /// ```rust, no_run
-    ///  use std::sync::Arc;
-    ///
-    ///  use cdk_sqlite::wallet::memory;
-    ///  use cdk::nuts::CurrencyUnit;
-    ///  use cdk::wallet::Wallet;
-    ///  use rand::random;
-    ///
-    /// #[tokio::main]
-    /// async fn main() -> anyhow::Result<()> {
-    ///  let seed = random::<[u8; 64]>();
-    ///  let mint_url = "https://fake.thesimplekid.dev";
-    ///  let unit = CurrencyUnit::Sat;
-    ///
-    ///  let localstore = memory::empty().await?;
-    ///  let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None).unwrap();
-    ///  let bolt11 = "lnbc100n1pnvpufspp5djn8hrq49r8cghwye9kqw752qjncwyfnrprhprpqk43mwcy4yfsqdq5g9kxy7fqd9h8vmmfvdjscqzzsxqyz5vqsp5uhpjt36rj75pl7jq2sshaukzfkt7uulj456s4mh7uy7l6vx7lvxs9qxpqysgqedwz08acmqwtk8g4vkwm2w78suwt2qyzz6jkkwcgrjm3r3hs6fskyhvud4fan3keru7emjm8ygqpcrwtlmhfjfmer3afs5hhwamgr4cqtactdq".to_string();
-    ///  let quote = wallet.melt_quote(bolt11, None).await?;
-    ///  let quote_id = quote.id;
-    ///
-    ///  let _ = wallet.melt(&quote_id).await?;
-    ///
-    ///  Ok(())
-    /// }
-    #[instrument(skip(self))]
-    pub async fn melt(&self, quote_id: &str) -> Result<Melted, Error> {
-        self.melt_with_metadata(quote_id, HashMap::new()).await
-    }
-
-    /// Melt with additional metadata to be saved locally with the transaction
-    /// # Synopsis
-    /// ```rust, no_run
-    ///  use std::sync::Arc;
-    ///
-    ///  use cdk_sqlite::wallet::memory;
-    ///  use cdk::nuts::CurrencyUnit;
-    ///  use cdk::wallet::Wallet;
-    ///  use rand::random;
-    ///
-    /// #[tokio::main]
-    /// async fn main() -> anyhow::Result<()> {
-    ///  let seed = random::<[u8; 64]>();
-    ///  let mint_url = "https://fake.thesimplekid.dev";
-    ///  let unit = CurrencyUnit::Sat;
-    ///
-    ///  let localstore = memory::empty().await?;
-    ///  let wallet = Wallet::new(mint_url, unit, Arc::new(localstore), seed, None).unwrap();
-    ///  let bolt11 = "lnbc100n1pnvpufspp5djn8hrq49r8cghwye9kqw752qjncwyfnrprhprpqk43mwcy4yfsqdq5g9kxy7fqd9h8vmmfvdjscqzzsxqyz5vqsp5uhpjt36rj75pl7jq2sshaukzfkt7uulj456s4mh7uy7l6vx7lvxs9qxpqysgqedwz08acmqwtk8g4vkwm2w78suwt2qyzz6jkkwcgrjm3r3hs6fskyhvud4fan3keru7emjm8ygqpcrwtlmhfjfmer3afs5hhwamgr4cqtactdq".to_string();
-    ///  let quote = wallet.melt_quote(bolt11, None).await?;
-    ///  let quote_id = quote.id;
-    ///
-    ///  let mut metadata = std::collections::HashMap::new();
-    ///  metadata.insert("my key".to_string(), "my value".to_string());
-    ///
-    ///  let _ = wallet.melt_with_metadata(&quote_id, metadata).await?;
-    ///
-    ///  Ok(())
-    /// }
-    #[instrument(skip(self))]
-    pub async fn melt_with_metadata(
-        &self,
-        quote_id: &str,
-        metadata: HashMap<String, String>,
-    ) -> Result<Melted, Error> {
-        let quote_info = self
-            .localstore
-            .get_melt_quote(quote_id)
-            .await?
-            .ok_or(Error::UnknownQuote)?;
-
-        ensure_cdk!(
-            quote_info.expiry.gt(&unix_time()),
-            Error::ExpiredQuote(quote_info.expiry, unix_time())
-        );
-
-        let inputs_needed_amount = quote_info.amount + quote_info.fee_reserve;
-
-        let active_keyset_ids = self
-            .get_mint_keysets()
-            .await?
-            .into_iter()
-            .map(|k| k.id)
-            .collect();
-        let keyset_fees_and_amounts = self.get_keyset_fees_and_amounts().await?;
-
-        let available_proofs = self.get_unspent_proofs().await?;
-
-        // Two-step proof selection for melt:
-        // Step 1: Try to select proofs that exactly match inputs_needed_amount.
-        //         If successful, no swap is required and we avoid paying swap fees.
-        // Step 2: If exact match not possible, we need to swap to get optimal denominations.
-        //         In this case, we must select more proofs to cover the additional swap fees.
-        {
-            let input_proofs = Wallet::select_proofs(
-                inputs_needed_amount,
-                available_proofs.clone(),
-                &active_keyset_ids,
-                &keyset_fees_and_amounts,
-                true,
-            )?;
-            let proofs_total = input_proofs.total_amount()?;
-
-            // If exact match, use proofs directly without swap
-            if proofs_total == inputs_needed_amount {
-                return self
-                    .melt_proofs_with_metadata(quote_id, input_proofs, metadata)
-                    .await;
-            }
-        }
-
-        let active_keyset_id = self.get_active_keyset().await?.id;
-        let fee_and_amounts = self
-            .get_keyset_fees_and_amounts_by_id(active_keyset_id)
-            .await?;
-
-        // Calculate optimal denomination split and the fee for those proofs
-        // First estimate based on inputs_needed_amount to get target_fee
-        let initial_split = inputs_needed_amount.split(&fee_and_amounts);
-        let target_fee = self
-            .get_proofs_fee_by_count(
-                vec![(active_keyset_id, initial_split.len() as u64)]
-                    .into_iter()
-                    .collect(),
-            )
-            .await?
-            .total;
-
-        // Since we could not select the correct inputs amount needed for melting,
-        // we select again this time including the amount we will now have to pay as a fee for the swap.
-        let inputs_total_needed = inputs_needed_amount + target_fee;
-
-        // Recalculate target amounts based on the actual total we need (including fee)
-        let target_amounts = inputs_total_needed.split(&fee_and_amounts);
-        let input_proofs = Wallet::select_proofs(
-            inputs_total_needed,
-            available_proofs,
-            &active_keyset_ids,
-            &keyset_fees_and_amounts,
-            true,
-        )?;
-        let proofs_total = input_proofs.total_amount()?;
-
-        // Need to swap to get exact denominations
-        tracing::debug!(
-            "Proofs total {} != inputs needed {}, swapping to get exact amount",
-            proofs_total,
-            inputs_total_needed
-        );
-
-        let keyset_fees: HashMap<cdk_common::Id, u64> = keyset_fees_and_amounts
-            .iter()
-            .map(|(key, values)| (*key, values.fee()))
-            .collect();
-
-        let split_result = split_proofs_for_send(
-            input_proofs,
-            &target_amounts,
-            inputs_total_needed,
-            target_fee,
-            &keyset_fees,
-            false,
-            false,
-        )?;
-
-        let mut final_proofs = split_result.proofs_to_send;
-
-        if !split_result.proofs_to_swap.is_empty() {
-            let swap_amount = inputs_total_needed
-                .checked_sub(final_proofs.total_amount()?)
-                .ok_or(Error::AmountOverflow)?;
-
-            tracing::debug!(
-                "Swapping {} proofs to get {} sats (swap fee: {} sats)",
-                split_result.proofs_to_swap.len(),
-                swap_amount,
-                split_result.swap_fee
-            );
-
-            if let Some(swapped) = self
-                .try_proof_operation_or_reclaim(
-                    split_result.proofs_to_swap.clone(),
-                    self.swap(
-                        Some(swap_amount),
-                        SplitTarget::None,
-                        split_result.proofs_to_swap,
-                        None,
-                        false, // fees already accounted for in inputs_total_needed
-                    ),
-                )
-                .await?
-            {
-                final_proofs.extend(swapped);
-            }
-        }
-
-        self.melt_proofs_with_metadata(quote_id, final_proofs, metadata)
-            .await
-    }
-}

+ 0 - 98
crates/cdk/src/wallet/melt/melt_bolt12.rs

@@ -1,98 +0,0 @@
-//! Melt BOLT12
-//!
-//! Implementation of melt functionality for BOLT12 offers
-
-use std::str::FromStr;
-
-use cdk_common::amount::amount_for_offer;
-use cdk_common::wallet::MeltQuote;
-use cdk_common::PaymentMethod;
-use lightning::offers::offer::Offer;
-use tracing::instrument;
-
-use crate::amount::to_unit;
-use crate::nuts::{CurrencyUnit, MeltOptions, MeltQuoteBolt11Response, MeltQuoteBolt12Request};
-use crate::{Error, Wallet};
-
-impl Wallet {
-    /// Melt Quote for BOLT12 offer
-    #[instrument(skip(self, request))]
-    pub async fn melt_bolt12_quote(
-        &self,
-        request: String,
-        options: Option<MeltOptions>,
-    ) -> Result<MeltQuote, Error> {
-        let quote_request = MeltQuoteBolt12Request {
-            request: request.clone(),
-            unit: self.unit.clone(),
-            options,
-        };
-
-        let quote_res = self.client.post_melt_bolt12_quote(quote_request).await?;
-
-        if self.unit == CurrencyUnit::Sat || self.unit == CurrencyUnit::Msat {
-            let offer = Offer::from_str(&request).map_err(|_| Error::Bolt12parse)?;
-            // Get amount from offer or options
-            let amount_msat = options
-                .map(|opt| opt.amount_msat())
-                .or_else(|| amount_for_offer(&offer, &CurrencyUnit::Msat).ok())
-                .ok_or(Error::AmountUndefined)?;
-            let amount_quote_unit = to_unit(amount_msat, &CurrencyUnit::Msat, &self.unit)?;
-
-            if quote_res.amount != amount_quote_unit {
-                tracing::warn!(
-                    "Mint returned incorrect quote amount. Expected {}, got {}",
-                    amount_quote_unit,
-                    quote_res.amount
-                );
-                return Err(Error::IncorrectQuoteAmount);
-            }
-        }
-
-        let quote = MeltQuote {
-            id: quote_res.quote,
-            amount: quote_res.amount,
-            request,
-            unit: self.unit.clone(),
-            fee_reserve: quote_res.fee_reserve,
-            state: quote_res.state,
-            expiry: quote_res.expiry,
-            payment_preimage: quote_res.payment_preimage,
-            payment_method: PaymentMethod::Bolt12,
-        };
-
-        self.localstore.add_melt_quote(quote.clone()).await?;
-
-        Ok(quote)
-    }
-
-    /// BOLT12 melt quote status
-    #[instrument(skip(self, quote_id))]
-    pub async fn melt_bolt12_quote_status(
-        &self,
-        quote_id: &str,
-    ) -> Result<MeltQuoteBolt11Response<String>, Error> {
-        let response = self.client.get_melt_bolt12_quote_status(quote_id).await?;
-
-        match self.localstore.get_melt_quote(quote_id).await? {
-            Some(quote) => {
-                let mut quote = quote;
-
-                if let Err(e) = self
-                    .add_transaction_for_pending_melt(&quote, &response)
-                    .await
-                {
-                    tracing::error!("Failed to add transaction for pending melt: {}", e);
-                }
-
-                quote.state = response.state;
-                self.localstore.add_melt_quote(quote).await?;
-            }
-            None => {
-                tracing::info!("Quote melt {} unknown", quote_id);
-            }
-        }
-
-        Ok(response)
-    }
-}