Quellcode durchsuchen

feat: remove mint and wallet errors

thesimplekid vor 7 Monaten
Ursprung
Commit
2fdf48cc72

+ 17 - 13
crates/cdk-axum/src/router_handlers.rs

@@ -59,13 +59,13 @@ pub async fn get_mint_bolt11_quote(
         .ok_or_else(|| {
             tracing::info!("Bolt11 mint request for unsupported unit");
 
-            into_response(Error::UnsupportedUnit)
+            into_response(Error::UnitUnsupported)
         })?;
 
     let amount =
         to_unit(payload.amount, &payload.unit, &ln.get_settings().unit).map_err(|err| {
             tracing::error!("Backed does not support unit: {}", err);
-            into_response(Error::UnsupportedUnit)
+            into_response(Error::UnitUnsupported)
         })?;
 
     let quote_expiry = unix_time() + state.quote_ttl;
@@ -139,7 +139,7 @@ pub async fn get_melt_bolt11_quote(
         .ok_or_else(|| {
             tracing::info!("Could not get ln backend for {}, bolt11 ", payload.unit);
 
-            into_response(Error::UnsupportedUnit)
+            into_response(Error::UnitUnsupported)
         })?;
 
     let payment_quote = ln.get_payment_quote(&payload).await.map_err(|err| {
@@ -149,7 +149,7 @@ pub async fn get_melt_bolt11_quote(
             err
         );
 
-        into_response(Error::UnsupportedUnit)
+        into_response(Error::UnitUnsupported)
     })?;
 
     let quote = state
@@ -199,7 +199,7 @@ pub async fn post_melt_bolt11(
             if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
                 tracing::error!("Could not reset melt quote state: {}", err);
             }
-            return Err(into_response(Error::MeltRequestInvalid));
+            return Err(into_response(Error::UnitUnsupported));
         }
     };
 
@@ -219,7 +219,7 @@ pub async fn post_melt_bolt11(
             if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
                 tracing::error!("Could not reset melt quote state: {}", err);
             }
-            return Err(into_response(Error::DatabaseError));
+            return Err(into_response(Error::Internal));
         }
     };
 
@@ -247,7 +247,7 @@ pub async fn post_melt_bolt11(
                 if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
                     tracing::error!("Could not reset melt quote state: {}", err);
                 }
-                return Err(into_response(Error::InsufficientInputProofs));
+                return Err(into_response(Error::InsufficientFunds));
             }
 
             mint_quote.state = MintQuoteState::Paid;
@@ -258,7 +258,7 @@ pub async fn post_melt_bolt11(
                 if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
                     tracing::error!("Could not reset melt quote state: {}", err);
                 }
-                return Err(into_response(Error::DatabaseError));
+                return Err(into_response(Error::Internal));
             }
 
             (None, amount)
@@ -305,7 +305,7 @@ pub async fn post_melt_bolt11(
 
                         Some(
                             to_unit(partial_msats, &CurrencyUnit::Msat, &quote.unit)
-                                .map_err(|_| into_response(Error::UnsupportedUnit))?,
+                                .map_err(|_| into_response(Error::UnitUnsupported))?,
                         )
                     }
                     false => None,
@@ -314,7 +314,7 @@ pub async fn post_melt_bolt11(
                 let amount_to_pay = match partial_amount {
                     Some(amount_to_pay) => amount_to_pay,
                     None => to_unit(invoice_amount_msats, &CurrencyUnit::Msat, &quote.unit)
-                        .map_err(|_| into_response(Error::UnsupportedUnit))?,
+                        .map_err(|_| into_response(Error::UnitUnsupported))?,
                 };
 
                 if amount_to_pay + quote.fee_reserve > inputs_amount_quote_unit {
@@ -327,7 +327,11 @@ pub async fn post_melt_bolt11(
                     if let Err(err) = state.mint.process_unpaid_melt(&payload).await {
                         tracing::error!("Could not reset melt quote state: {}", err);
                     }
-                    return Err(into_response(Error::InsufficientInputProofs));
+                    return Err(into_response(Error::InsufficientInputs(
+                        inputs_amount_quote_unit.into(),
+                        amount_to_pay.into(),
+                        quote.fee_reserve.into(),
+                    )));
                 }
             }
 
@@ -339,7 +343,7 @@ pub async fn post_melt_bolt11(
                         tracing::error!("Could not reset melt quote state: {}", err);
                     }
 
-                    return Err(into_response(Error::UnsupportedUnit));
+                    return Err(into_response(Error::UnitUnsupported));
                 }
             };
 
@@ -364,7 +368,7 @@ pub async fn post_melt_bolt11(
             };
 
             let amount_spent = to_unit(pre.total_spent, &ln.get_settings().unit, &quote.unit)
-                .map_err(|_| into_response(Error::UnsupportedUnit))?;
+                .map_err(|_| into_response(Error::UnitUnsupported))?;
 
             (pre.payment_preimage, amount_spent)
         }

+ 1 - 1
crates/cdk-integration-tests/src/lib.rs

@@ -79,7 +79,7 @@ pub async fn start_mint(
     )
     .await?;
 
-    let quote_ttl = 2000;
+    let quote_ttl = 100000;
 
     let mint_arc = Arc::new(mint);
 

+ 4 - 2
crates/cdk-integration-tests/tests/mint.rs

@@ -8,8 +8,8 @@ use bip39::Mnemonic;
 use cdk::amount::SplitTarget;
 use cdk::cdk_database::WalletMemoryDatabase;
 use cdk::nuts::CurrencyUnit;
-use cdk::wallet::error::Error;
 use cdk::wallet::SendKind;
+use cdk::Error;
 use cdk::Wallet;
 use cdk_integration_tests::{create_backends_fake_wallet, start_mint, wallet_mint, MINT_URL};
 
@@ -35,7 +35,8 @@ pub async fn test_mint_double_receive() -> Result<()> {
 
     let wallet = Arc::new(wallet);
 
-    wallet_mint(Arc::clone(&wallet), 100.into()).await?;
+    wallet_mint(Arc::clone(&wallet), 100.into()).await.unwrap();
+    println!("Minted");
 
     let token = wallet
         .send(
@@ -72,6 +73,7 @@ pub async fn test_mint_double_receive() -> Result<()> {
         match err {
             Error::TokenAlreadySpent => (),
             _ => {
+                println!("{}", err);
                 bail!("Expected an already spent error");
             }
         }

+ 2 - 1
crates/cdk-integration-tests/tests/unbalanced.rs

@@ -5,6 +5,7 @@ use std::time::Duration;
 use anyhow::{bail, Result};
 use cdk::amount::SplitTarget;
 use cdk::nuts::{PreMintSecrets, SwapRequest};
+use cdk::Error;
 use cdk::HttpClient;
 use cdk_integration_tests::{create_backends_fake_wallet, mint_proofs, start_mint, MINT_URL};
 
@@ -40,7 +41,7 @@ pub async fn test_unbalanced_swap() -> Result<()> {
         // In the context of this test an error response here is good.
         // It means the mint does not allow us to swap for more then we should by overflowing
         Err(err) => match err {
-            cdk::wallet::error::Error::TransactionUnbalanced => {
+            Error::TransactionUnbalanced(_, _, _) => {
                 return Ok(());
             }
             _ => {

+ 5 - 2
crates/cdk-redb/src/error.rs

@@ -40,12 +40,15 @@ pub enum Error {
     /// CDK Error
     #[error(transparent)]
     CDK(#[from] cdk::error::Error),
+    /// NUT00 Error
+    #[error(transparent)]
+    CDKNUT00(#[from] cdk::nuts::nut00::Error),
     /// NUT02 Error
     #[error(transparent)]
     CDKNUT02(#[from] cdk::nuts::nut02::Error),
-    /// NUT00 Error
+    /// DHKE Error
     #[error(transparent)]
-    CDKNUT00(#[from] cdk::nuts::nut00::Error),
+    DHKE(#[from] cdk::dhke::Error),
     /// Unknown Mint Info
     #[error("Unknown mint info")]
     UnknownMintInfo,

+ 0 - 3
crates/cdk-sqlite/src/wallet/error.rs

@@ -11,9 +11,6 @@ pub enum Error {
     /// Serde Error
     #[error(transparent)]
     Serde(#[from] serde_json::Error),
-    /// Wallet Error
-    #[error(transparent)]
-    CDKWallet(#[from] cdk::wallet::error::Error),
     /// NUT00 Error
     #[error(transparent)]
     CDKNUT00(#[from] cdk::nuts::nut00::Error),

+ 12 - 2
crates/cdk/src/amount.rs

@@ -6,8 +6,18 @@ use std::cmp::Ordering;
 use std::fmt;
 
 use serde::{Deserialize, Serialize};
-
-use crate::error::Error;
+use thiserror::Error;
+
+/// Amount Error
+#[derive(Debug, Error)]
+pub enum Error {
+    /// Split Values must be less then or equal to amount
+    #[error("Split Values must be less then or equal to amount")]
+    SplitValuesGreater,
+    /// Amount overflow
+    #[error("Amount Overflow")]
+    AmountOverflow,
+}
 
 /// Amount can be any unit
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]

+ 2 - 2
crates/cdk/src/cdk_database/mod.rs

@@ -43,9 +43,9 @@ pub enum Error {
     /// Database Error
     #[error(transparent)]
     Database(Box<dyn std::error::Error + Send + Sync>),
-    /// CDK Error
+    /// DHKE error
     #[error(transparent)]
-    Cdk(#[from] crate::error::Error),
+    DHKE(#[from] crate::dhke::Error),
     /// NUT00 Error
     #[error(transparent)]
     NUT00(#[from] crate::nuts::nut00::Error),

+ 21 - 3
crates/cdk/src/dhke.rs

@@ -7,8 +7,8 @@ use bitcoin::hashes::Hash;
 use bitcoin::secp256k1::{
     Parity, PublicKey as NormalizedPublicKey, Scalar, Secp256k1, XOnlyPublicKey,
 };
+use thiserror::Error;
 
-use crate::error::Error;
 use crate::nuts::nut01::{PublicKey, SecretKey};
 use crate::nuts::nut12::ProofDleq;
 use crate::nuts::{BlindSignature, Keys, Proof, Proofs};
@@ -18,6 +18,24 @@ use crate::SECP256K1;
 
 const DOMAIN_SEPARATOR: &[u8; 28] = b"Secp256k1_HashToCurve_Cashu_";
 
+/// NUT00 Error
+#[derive(Debug, Error)]
+pub enum Error {
+    /// Token could not be validated
+    #[error("Token not verified")]
+    TokenNotVerified,
+    /// No valid point on curve
+    #[error("No valid point found")]
+    NoValidPoint,
+    /// Secp256k1 error
+    #[error(transparent)]
+    Secp256k1(#[from] bitcoin::secp256k1::Error),
+    // TODO: Remove use anyhow
+    /// Custom Error
+    #[error("`{0}`")]
+    Custom(String),
+}
+
 /// Deterministically maps a message to a public key point on the secp256k1
 /// curve, utilizing a domain separator to ensure uniqueness.
 ///
@@ -103,7 +121,7 @@ pub fn construct_proofs(
     keys: &Keys,
 ) -> Result<Proofs, Error> {
     if (promises.len() != rs.len()) || (promises.len() != secrets.len()) {
-        return Err(Error::CustomError(
+        return Err(Error::Custom(
             "Lengths of promises, rs, and secrets must be equal".to_string(),
         ));
     }
@@ -112,7 +130,7 @@ pub fn construct_proofs(
         let blinded_c: PublicKey = blinded_signature.c;
         let a: PublicKey = keys
             .amount_key(blinded_signature.amount)
-            .ok_or(Error::CustomError("Could not get proofs".to_string()))?;
+            .ok_or(Error::Custom("Could not get proofs".to_string()))?;
 
         let unblinded_signature: PublicKey = unblind_message(&blinded_c, &r, &a)?;
 

+ 240 - 58
crates/cdk/src/error.rs

@@ -1,13 +1,14 @@
 //! Errors
 
 use std::fmt;
-use std::string::FromUtf8Error;
 
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use serde_json::Value;
 use thiserror::Error;
 
-use crate::util::hex;
+#[cfg(feature = "wallet")]
+use crate::wallet::multi_mint_wallet::WalletKey;
+use crate::{util::hex, Amount};
 
 /// CDK Error
 #[derive(Debug, Error)]
@@ -15,57 +16,146 @@ pub enum Error {
     /// Mint does not have a key for amount
     #[error("No Key for Amount")]
     AmountKey,
-    /// Not enough input proofs provided
-    #[error("Not enough input proofs spent")]
-    InsufficientInputProofs,
-    /// Database update failed
-    #[error("Database error")]
-    DatabaseError,
-    /// Unsupported unit
-    #[error("Unit unsupported")]
-    UnsupportedUnit,
     /// Payment failed
     #[error("Payment failed")]
     PaymentFailed,
-    /// Melt Request is not valid
-    #[error("Melt request is not valid")]
-    MeltRequestInvalid,
     /// Invoice already paid
     #[error("Request already paid")]
     RequestAlreadyPaid,
-    /// Amount is not what expected
-    #[error("Amount miss match")]
-    Amount,
-    /// Token is already spent
-    #[error("Token already spent")]
-    TokenSpent,
-    /// Token could not be validated
-    #[error("Token not verified")]
-    TokenNotVerified,
     /// Invalid payment request
     #[error("Invalid payment request")]
     InvalidPaymentRequest,
     /// Bolt11 invoice does not have amount
     #[error("Invoice Amount undefined")]
     InvoiceAmountUndefined,
-    /// Proof is missing a required field
-    #[error("Proof missing required field")]
-    MissingProofField,
-    /// No valid point on curve
-    #[error("No valid point found")]
-    NoValidPoint,
     /// Split Values must be less then or equal to amount
     #[error("Split Values must be less then or equal to amount")]
     SplitValuesGreater,
     /// Amount overflow
     #[error("Amount Overflow")]
     AmountOverflow,
-    /// Secp256k1 error
-    #[error(transparent)]
-    Secp256k1(#[from] bitcoin::secp256k1::Error),
-    /// Secret error
+
+    // Mint Errors
+    /// Minting is disabled
+    #[error("Minting is disabled")]
+    MintingDisabled,
+    /// Quote is not known
+    #[error("Unknown quote")]
+    UnknownQuote,
+    /// Quote is expired
+    #[error("Expired quote: Expired: `{0}`, Time: `{1}`")]
+    ExpiredQuote(u64, u64),
+    /// Amount is outside of allowed range
+    #[error("Amount but be between `{0}` and `{1}` is `{2}`")]
+    AmountOutofLimitRange(Amount, Amount, Amount),
+    /// Quote is not paiud
+    #[error("Quote not paid")]
+    UnpaidQuote,
+    /// Quote is pending
+    #[error("Quote pending")]
+    PendingQuote,
+    /// ecash already issued for quote
+    #[error("Quote already issued")]
+    IssuedQuote,
+    /// Quote has already been paid
+    #[error("Quote is already paid")]
+    PaidQuote,
+    /// Melting is disabled
+    #[error("Minting is disabled")]
+    MeltingDisabled,
+    /// Unknown Keyset
+    #[error("Unknown Keyset")]
+    UnknownKeySet,
+    /// BlindedMessage is already signed
+    #[error("Blinded Message is already signed")]
+    BlindedMessageAlreadySigned,
+    /// Inactive Keyset
+    #[error("Inactive Keyset")]
+    InactiveKeyset,
+    /// Not engough inputs provided
+    #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
+    InsufficientInputs(u64, u64, u64),
+    /// Transaction unbalanced
+    #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
+    TransactionUnbalanced(u64, u64, u64),
+    /// Duplicate proofs provided
+    #[error("Duplicate proofs")]
+    DuplicateProofs,
+    /// Multiple units provided
+    #[error("Cannot have multiple units")]
+    MultipleUnits,
+    /// Sig all cannot be used in melt
+    #[error("Sig all cannot be used in melt")]
+    SigAllUsedInMelt,
+    /// Token is already spent
+    #[error("Token Already Spent")]
+    TokenAlreadySpent,
+    /// Token is already pending
+    #[error("Token Pending")]
+    TokenPending,
+    /// Internal Error
+    #[error("Internal Error")]
+    Internal,
+
+    // Wallet Errors
+    /// P2PK spending conditions not met
+    #[error("P2PK condition not met `{0}`")]
+    P2PKConditionsNotMet(String),
+    /// Spending Locktime not provided
+    #[error("Spending condition locktime not provided")]
+    LocktimeNotProvided,
+    /// Invalid Spending Conditions
+    #[error("Invalid spending conditions: `{0}`")]
+    InvalidSpendConditions(String),
+    /// Incorrect Wallet
+    #[error("Incorrect wallet: `{0}`")]
+    IncorrectWallet(String),
+    /// Unknown Wallet
+    #[cfg(feature = "wallet")]
+    #[error("Unknown wallet: `{0}`")]
+    UnknownWallet(WalletKey),
+    /// Max Fee Ecxeded
+    #[error("Max fee exceeded")]
+    MaxFeeExceeded,
+    /// Url path segments could not be joined
+    #[error("Url path segments could not be joined")]
+    UrlPathSegments,
+    ///  Unknown error response
+    #[error("Unknown error response: `{0}`")]
+    UnknownErrorResponse(String),
+    /// Invalid DLEQ proof
+    #[error("Could not verify DLEQ proof")]
+    CouldNotVerifyDleq,
+    /// Incorrect Mint
+    /// Token does not match wallet mint
+    #[error("Token does not match wallet mint")]
+    IncorrectMint,
+    /// Receive can only be used with tokens from single mint
+    #[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
+    MultiMintTokenNotSupported,
+    /// Unit Not supported
+    #[error("Unit not supported for method")]
+    UnitUnsupported,
+    /// Preimage not provided
+    #[error("Preimage not provided")]
+    PreimageNotProvided,
+    /// Insufficient Funds
+    #[error("Insufficient funds")]
+    InsufficientFunds,
+    /// No active keyset
+    #[error("No active keyset")]
+    NoActiveKeyset,
+    /// Incorrect quote amount
+    #[error("Incorrect quote amount")]
+    IncorrectQuoteAmount,
+    /// Custom Error
+    #[error("`{0}`")]
+    Custom(String),
+
+    // External Error conversions
+    /// Parse invoice error
     #[error(transparent)]
-    Secret(#[from] super::secret::Error),
+    Invoice(#[from] lightning_invoice::ParseOrSemanticError),
     /// Bip32 error
     #[error(transparent)]
     Bip32(#[from] bitcoin::bip32::Error),
@@ -77,7 +167,7 @@ pub enum Error {
     UrlParseError(#[from] url::ParseError),
     /// Utf8 parse error
     #[error(transparent)]
-    Utf8ParseError(#[from] FromUtf8Error),
+    Utf8ParseError(#[from] std::string::FromUtf8Error),
     /// Serde Json error
     #[error(transparent)]
     SerdeJsonError(#[from] serde_json::Error),
@@ -91,18 +181,51 @@ pub enum Error {
     /// From hex error
     #[error(transparent)]
     ReqwestError(#[from] reqwest::Error),
+
+    // Crate error conversions
+    /// Cashu Url Error
+    #[error(transparent)]
+    CashuUrl(#[from] crate::mint_url::Error),
+    /// Secret error
+    #[error(transparent)]
+    Secret(#[from] crate::secret::Error),
+    /// Amount Error
+    #[error(transparent)]
+    AmountError(#[from] crate::amount::Error),
+    /// DHKE Error
+    #[error(transparent)]
+    DHKE(#[from] crate::dhke::Error),
+    /// NUT00 Error
+    #[error(transparent)]
+    NUT00(#[from] crate::nuts::nut00::Error),
     /// Nut01 error
     #[error(transparent)]
     NUT01(#[from] crate::nuts::nut01::Error),
     /// NUT02 error
     #[error(transparent)]
     NUT02(#[from] crate::nuts::nut02::Error),
+    /// NUT03 error
+    #[error(transparent)]
+    NUT03(#[from] crate::nuts::nut03::Error),
+    /// NUT05 error
+    #[error(transparent)]
+    NUT05(#[from] crate::nuts::nut05::Error),
     /// NUT11 Error
     #[error(transparent)]
     NUT11(#[from] crate::nuts::nut11::Error),
-    /// Custom error
-    #[error("`{0}`")]
-    CustomError(String),
+    /// NUT12 Error
+    #[error(transparent)]
+    NUT12(#[from] crate::nuts::nut12::Error),
+    /// NUT13 Error
+    #[error(transparent)]
+    NUT13(#[from] crate::nuts::nut13::Error),
+    /// NUT14 Error
+    #[error(transparent)]
+    NUT14(#[from] crate::nuts::nut14::Error),
+    /// Database Error
+    #[cfg(any(feature = "wallet", feature = "mint"))]
+    #[error(transparent)]
+    Database(#[from] crate::cdk_database::Error),
 }
 
 /// CDK Error Response
@@ -163,29 +286,64 @@ impl ErrorResponse {
 impl From<Error> for ErrorResponse {
     fn from(err: Error) -> ErrorResponse {
         match err {
-            Error::TokenSpent => ErrorResponse {
+            Error::UnitUnsupported => ErrorResponse {
+                code: ErrorCode::UnitUnsupported,
+                error: Some(err.to_string()),
+                detail: None,
+            },
+            Error::PaymentFailed => ErrorResponse {
+                code: ErrorCode::LightningError,
+                error: Some(err.to_string()),
+                detail: None,
+            },
+            Error::RequestAlreadyPaid => ErrorResponse {
+                code: ErrorCode::InvoiceAlreadyPaid,
+                error: Some("Invoice already paid.".to_string()),
+                detail: None,
+            },
+            Error::TokenAlreadySpent => ErrorResponse {
                 code: ErrorCode::TokenAlreadySpent,
+                error: Some("Token is already spent.".to_string()),
+                detail: None,
+            },
+            Error::TransactionUnbalanced(inputs_total, outputs_total, fee_expected) => {
+                ErrorResponse {
+                    code: ErrorCode::TransactionUnbalanced,
+                    error: Some(format!(
+                        "Inputs: {}, Outputs: {}, expected_fee: {}",
+                        inputs_total, outputs_total, fee_expected,
+                    )),
+                    detail: Some("Transaction inputs should equal outputs less fee".to_string()),
+                }
+            }
+            Error::MintingDisabled => ErrorResponse {
+                code: ErrorCode::MintingDisabled,
                 error: Some(err.to_string()),
                 detail: None,
             },
-            Error::InsufficientInputProofs => ErrorResponse {
-                code: ErrorCode::InsufficientFee,
+            Error::BlindedMessageAlreadySigned => ErrorResponse {
+                code: ErrorCode::BlindedMessageAlreadySigned,
                 error: Some(err.to_string()),
                 detail: None,
             },
-            Error::UnsupportedUnit => ErrorResponse {
-                code: ErrorCode::UnitUnsupported,
+            Error::InsufficientFunds => ErrorResponse {
+                code: ErrorCode::TransactionUnbalanced,
                 error: Some(err.to_string()),
                 detail: None,
             },
-            Error::PaymentFailed => ErrorResponse {
-                code: ErrorCode::LightningError,
+            Error::AmountOutofLimitRange(_min, _max, _amount) => ErrorResponse {
+                code: ErrorCode::AmountOutofLimitRange,
                 error: Some(err.to_string()),
                 detail: None,
             },
-            Error::RequestAlreadyPaid => ErrorResponse {
-                code: ErrorCode::InvoiceAlreadyPaid,
-                error: Some("Invoice already paid.".to_string()),
+            Error::ExpiredQuote(_, _) => ErrorResponse {
+                code: ErrorCode::QuoteExpired,
+                error: Some(err.to_string()),
+                detail: None,
+            },
+            Error::PendingQuote => ErrorResponse {
+                code: ErrorCode::QuotePending,
+                error: Some(err.to_string()),
                 detail: None,
             },
             _ => ErrorResponse {
@@ -197,6 +355,30 @@ impl From<Error> for ErrorResponse {
     }
 }
 
+impl From<ErrorResponse> for Error {
+    fn from(err: ErrorResponse) -> Error {
+        match err.code {
+            ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
+            ErrorCode::QuoteNotPaid => Self::UnpaidQuote,
+            ErrorCode::QuotePending => Self::PendingQuote,
+            ErrorCode::QuoteExpired => Self::ExpiredQuote(0, 0),
+            ErrorCode::KeysetNotFound => Self::UnknownKeySet,
+            ErrorCode::KeysetInactive => Self::InactiveKeyset,
+            ErrorCode::BlindedMessageAlreadySigned => Self::BlindedMessageAlreadySigned,
+            ErrorCode::UnitUnsupported => Self::UnitUnsupported,
+            ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced(0, 0, 0),
+            ErrorCode::MintingDisabled => Self::MintingDisabled,
+            ErrorCode::InvoiceAlreadyPaid => Self::RequestAlreadyPaid,
+            ErrorCode::TokenNotVerified => Self::DHKE(crate::dhke::Error::TokenNotVerified),
+            ErrorCode::LightningError => Self::PaymentFailed,
+            ErrorCode::AmountOutofLimitRange => {
+                Self::AmountOutofLimitRange(Amount::default(), Amount::default(), Amount::default())
+            }
+            _ => Self::UnknownErrorResponse(err.to_string()),
+        }
+    }
+}
+
 /// Possible Error Codes
 #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
 pub enum ErrorCode {
@@ -204,14 +386,14 @@ pub enum ErrorCode {
     TokenAlreadySpent,
     /// Quote is not paid
     QuoteNotPaid,
+    /// Quote is not expired
+    QuoteExpired,
+    /// Quote Pending
+    QuotePending,
     /// Keyset is not found
     KeysetNotFound,
     /// Keyset inactive
     KeysetInactive,
-    /// Fee Overpaid
-    FeeOverPaid,
-    /// Insufficient Fee
-    InsufficientFee,
     /// Blinded Message Already signed
     BlindedMessageAlreadySigned,
     /// Unsupported unit
@@ -220,8 +402,6 @@ pub enum ErrorCode {
     TokensAlreadyIssued,
     /// Minting Disabled
     MintingDisabled,
-    /// Quote Pending
-    QuotePending,
     /// Invoice Already Paid
     InvoiceAlreadyPaid,
     /// Token Not Verified
@@ -230,6 +410,8 @@ pub enum ErrorCode {
     LightningError,
     /// Unbalanced Error
     TransactionUnbalanced,
+    /// Amount outside of allowed range
+    AmountOutofLimitRange,
     /// Unknown error code
     Unknown(u16),
 }
@@ -243,8 +425,7 @@ impl ErrorCode {
             11001 => Self::TokenAlreadySpent,
             11002 => Self::TransactionUnbalanced,
             11005 => Self::UnitUnsupported,
-            11006 => Self::InsufficientFee,
-            11007 => Self::FeeOverPaid,
+            11006 => Self::AmountOutofLimitRange,
             12001 => Self::KeysetNotFound,
             12002 => Self::KeysetInactive,
             20000 => Self::LightningError,
@@ -253,6 +434,7 @@ impl ErrorCode {
             20003 => Self::MintingDisabled,
             20005 => Self::QuotePending,
             20006 => Self::InvoiceAlreadyPaid,
+            20007 => Self::QuoteExpired,
             _ => Self::Unknown(code),
         }
     }
@@ -265,8 +447,7 @@ impl ErrorCode {
             Self::TokenAlreadySpent => 11001,
             Self::TransactionUnbalanced => 11002,
             Self::UnitUnsupported => 11005,
-            Self::InsufficientFee => 11006,
-            Self::FeeOverPaid => 11007,
+            Self::AmountOutofLimitRange => 11006,
             Self::KeysetNotFound => 12001,
             Self::KeysetInactive => 12002,
             Self::LightningError => 20000,
@@ -275,6 +456,7 @@ impl ErrorCode {
             Self::MintingDisabled => 20003,
             Self::QuotePending => 20005,
             Self::InvoiceAlreadyPaid => 20006,
+            Self::QuoteExpired => 20007,
             Self::Unknown(code) => *code,
         }
     }

+ 2 - 0
crates/cdk/src/lib.rs

@@ -23,6 +23,8 @@ pub mod wallet;
 #[doc(hidden)]
 pub use bitcoin::secp256k1;
 #[doc(hidden)]
+pub use error::Error;
+#[doc(hidden)]
 pub use lightning_invoice::{self, Bolt11Invoice};
 #[cfg(feature = "mint")]
 #[doc(hidden)]

+ 0 - 208
crates/cdk/src/mint/error.rs

@@ -1,208 +0,0 @@
-//! Mint Errors
-
-use thiserror::Error;
-
-use crate::error::{ErrorCode, ErrorResponse};
-use crate::{cdk_database, mint_url};
-
-/// CDK Mint Error
-#[derive(Debug, Error)]
-pub enum Error {
-    /// Unknown Keyset
-    #[error("Unknown Keyset")]
-    UnknownKeySet,
-    /// Inactive Keyset
-    #[error("Inactive Keyset")]
-    InactiveKeyset,
-    /// There is not key for amount given
-    #[error("No key for amount")]
-    AmountKey,
-    /// Amount is not what is expected
-    #[error("Amount")]
-    Amount,
-    /// Amount overflow
-    #[error("Amount Overflow")]
-    AmountOverflow,
-    /// Not engough inputs provided
-    #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
-    InsufficientInputs(u64, u64, u64),
-    /// Transaction unbalanced
-    #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
-    TransactionUnbalanced(u64, u64, u64),
-    /// Duplicate proofs provided
-    #[error("Duplicate proofs")]
-    DuplicateProofs,
-    /// Token is already spent
-    #[error("Token Already Spent")]
-    TokenAlreadySpent,
-    /// Token is already pending
-    #[error("Token Pending")]
-    TokenPending,
-    /// Quote is not paiud
-    #[error("Quote not paid")]
-    UnpaidQuote,
-    /// Quote has already been paid
-    #[error("Quote is already paid")]
-    PaidQuote,
-    /// Quote is not known
-    #[error("Unknown quote")]
-    UnknownQuote,
-    /// Quote is pending
-    #[error("Quote pending")]
-    PendingQuote,
-    /// ecash already issued for quote
-    #[error("Quote already issued")]
-    IssuedQuote,
-    /// Unknown secret kind
-    #[error("Unknown secret kind")]
-    UnknownSecretKind,
-    /// Multiple units provided
-    #[error("Cannot have multiple units")]
-    MultipleUnits,
-    /// Unit not supported
-    #[error("Unit not supported")]
-    UnsupportedUnit,
-    /// BlindMessage is already signed
-    #[error("Blinded Message is already signed")]
-    BlindedMessageAlreadySigned,
-    /// Sig all cannot be used in melt
-    #[error("Sig all cannot be used in melt")]
-    SigAllUsedInMelt,
-    /// Minting is disabled
-    #[error("Minting is disabled")]
-    MintingDisabled,
-    /// Minting request is over mint limit
-    #[error("Mint request is over mint limit")]
-    MintOverLimit,
-    /// Mint request is uver mint limit
-    #[error("Mint request is under mint limit")]
-    MintUnderLimit,
-    /// Melting is disabled
-    #[error("Minting is disabled")]
-    MeltingDisabled,
-    /// Melting request is over mint limit
-    #[error("Mint request is over mint limit")]
-    MeltOverLimit,
-    /// Melt request is uver mint limit
-    #[error("Mint request is under mint limit")]
-    MeltUnderLimit,
-    /// Cashu Error
-    #[error(transparent)]
-    Cashu(#[from] crate::error::Error),
-    /// Secret Error
-    #[error(transparent)]
-    Secret(#[from] crate::secret::Error),
-    /// NUT00 Error
-    #[error(transparent)]
-    NUT00(#[from] crate::nuts::nut00::Error),
-    /// NUT04 Error
-    #[error(transparent)]
-    NUT04(#[from] crate::nuts::nut04::Error),
-    /// NUT05 Error
-    #[error(transparent)]
-    NUT05(#[from] crate::nuts::nut05::Error),
-    /// NUT11 Error
-    #[error(transparent)]
-    NUT11(#[from] crate::nuts::nut11::Error),
-    /// NUT12 Error
-    #[error(transparent)]
-    Nut12(#[from] crate::nuts::nut12::Error),
-    /// NUT14 Error
-    #[error(transparent)]
-    Nut14(#[from] crate::nuts::nut14::Error),
-    /// Database Error
-    #[error(transparent)]
-    Database(#[from] cdk_database::Error),
-    /// Mint Url Error
-    #[error(transparent)]
-    MintUrl(#[from] mint_url::Error),
-    /// Custom Error
-    #[error("`{0}`")]
-    Custom(String),
-}
-
-impl From<Error> for cdk_database::Error {
-    fn from(e: Error) -> Self {
-        Self::Database(Box::new(e))
-    }
-}
-
-impl From<Error> for ErrorResponse {
-    fn from(err: Error) -> ErrorResponse {
-        match err {
-            Error::TokenAlreadySpent => ErrorResponse {
-                code: ErrorCode::TokenAlreadySpent,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            Error::TransactionUnbalanced(_inputs_amount, _output_amouns, _expected_fee) => {
-                ErrorResponse {
-                    code: ErrorCode::TransactionUnbalanced,
-                    error: Some(err.to_string()),
-                    detail: None,
-                }
-            }
-            Error::InsufficientInputs(_inputs_amount, _output_amount, _expected_fee) => {
-                ErrorResponse {
-                    code: ErrorCode::InsufficientFee,
-                    error: Some(err.to_string()),
-                    detail: None,
-                }
-            }
-            Error::MintingDisabled => ErrorResponse {
-                code: ErrorCode::MintingDisabled,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            Error::InactiveKeyset => ErrorResponse {
-                code: ErrorCode::KeysetInactive,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            Error::UnknownKeySet => ErrorResponse {
-                code: ErrorCode::KeysetNotFound,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            Error::UnpaidQuote => ErrorResponse {
-                code: ErrorCode::QuoteNotPaid,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            Error::PendingQuote => ErrorResponse {
-                code: ErrorCode::QuotePending,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            Error::IssuedQuote => ErrorResponse {
-                code: ErrorCode::TokensAlreadyIssued,
-                error: Some(err.to_string()),
-                detail: None,
-            },
-            _ => ErrorResponse {
-                code: ErrorCode::Unknown(9999),
-                error: Some(err.to_string()),
-                detail: None,
-            },
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-
-    use super::*;
-
-    #[test]
-    fn test_error_response_enum() {
-        let error = Error::TokenAlreadySpent;
-
-        let response: ErrorResponse = error.into();
-
-        let json = serde_json::to_string(&response).unwrap();
-
-        let error_response: ErrorResponse = serde_json::from_str(&json).unwrap();
-
-        assert_eq!(response.code, error_response.code);
-    }
-}

+ 35 - 8
crates/cdk/src/mint/mod.rs

@@ -6,7 +6,6 @@ use std::sync::Arc;
 
 use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
 use bitcoin::secp256k1::{self, Secp256k1};
-use error::Error;
 use serde::{Deserialize, Serialize};
 use tokio::sync::RwLock;
 use tracing::instrument;
@@ -15,13 +14,13 @@ use self::nut05::QuoteState;
 use self::nut11::EnforceSigFlag;
 use crate::cdk_database::{self, MintDatabase};
 use crate::dhke::{hash_to_curve, sign_message, verify_message};
+use crate::error::Error;
 use crate::mint_url::MintUrl;
 use crate::nuts::nut11::enforce_sig_flag;
 use crate::nuts::*;
 use crate::util::unix_time;
 use crate::Amount;
 
-pub mod error;
 pub mod types;
 
 pub use types::{MeltQuote, MintQuote};
@@ -212,18 +211,26 @@ impl Mint {
                     .max_amount
                     .map_or(false, |max_amount| amount > max_amount)
                 {
-                    return Err(Error::MintOverLimit);
+                    return Err(Error::AmountOutofLimitRange(
+                        settings.min_amount.unwrap_or_default(),
+                        settings.max_amount.unwrap_or_default(),
+                        amount,
+                    ));
                 }
 
                 if settings
                     .min_amount
                     .map_or(false, |min_amount| amount < min_amount)
                 {
-                    return Err(Error::MintUnderLimit);
+                    return Err(Error::AmountOutofLimitRange(
+                        settings.min_amount.unwrap_or_default(),
+                        settings.max_amount.unwrap_or_default(),
+                        amount,
+                    ));
                 }
             }
             None => {
-                return Err(Error::UnsupportedUnit);
+                return Err(Error::UnitUnsupported);
             }
         }
 
@@ -359,18 +366,26 @@ impl Mint {
                     .max_amount
                     .map_or(false, |max_amount| amount > max_amount)
                 {
-                    return Err(Error::MeltOverLimit);
+                    return Err(Error::AmountOutofLimitRange(
+                        settings.min_amount.unwrap_or_default(),
+                        settings.max_amount.unwrap_or_default(),
+                        amount,
+                    ));
                 }
 
                 if settings
                     .min_amount
                     .map_or(false, |min_amount| amount < min_amount)
                 {
-                    return Err(Error::MeltUnderLimit);
+                    return Err(Error::AmountOutofLimitRange(
+                        settings.min_amount.unwrap_or_default(),
+                        settings.max_amount.unwrap_or_default(),
+                        amount,
+                    ));
                 }
             }
             None => {
-                return Err(Error::UnsupportedUnit);
+                return Err(Error::UnitUnsupported);
             }
         }
 
@@ -555,6 +570,18 @@ impl Mint {
         &self,
         mint_request: nut04::MintBolt11Request,
     ) -> Result<nut04::MintBolt11Response, Error> {
+        // Check quote is known and not expired
+        match self.localstore.get_mint_quote(&mint_request.quote).await? {
+            Some(quote) => {
+                if quote.expiry < unix_time() {
+                    return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
+                }
+            }
+            None => {
+                return Err(Error::UnknownQuote);
+            }
+        }
+
         let state = self
             .localstore
             .update_mint_quote_state(&mint_request.quote, MintQuoteState::Pending)

+ 11 - 2
crates/cdk/src/nuts/nut00/mod.rs

@@ -59,9 +59,18 @@ pub enum Error {
     /// Ciborium error
     #[error(transparent)]
     CiboriumError(#[from] ciborium::de::Error<std::io::Error>),
-    /// CDK error
+    /// Amount Error
     #[error(transparent)]
-    Cdk(#[from] crate::error::Error),
+    Amount(#[from] crate::amount::Error),
+    /// Secret error
+    #[error(transparent)]
+    Secret(#[from] crate::secret::Error),
+    /// DHKE error
+    #[error(transparent)]
+    DHKE(#[from] crate::dhke::Error),
+    /// NUT10 error
+    #[error(transparent)]
+    NUT10(#[from] crate::nuts::nut10::Error),
     /// NUT11 error
     #[error(transparent)]
     NUT11(#[from] crate::nuts::nut11::Error),

+ 21 - 5
crates/cdk/src/nuts/nut03.rs

@@ -3,9 +3,21 @@
 //! <https://github.com/cashubtc/nuts/blob/main/03.md>
 
 use serde::{Deserialize, Serialize};
+use thiserror::Error;
 
 use super::nut00::{BlindSignature, BlindedMessage, PreMintSecrets, Proofs};
-use crate::{error::Error, Amount};
+use crate::Amount;
+
+/// NUT03 Error
+#[derive(Debug, Error)]
+pub enum Error {
+    /// DHKE error
+    #[error(transparent)]
+    DHKE(#[from] crate::dhke::Error),
+    /// Amount Error
+    #[error(transparent)]
+    Amount(#[from] crate::amount::Error),
+}
 
 /// Preswap information
 #[derive(Debug, Clone, PartialEq, Eq, Serialize)]
@@ -37,12 +49,16 @@ impl SwapRequest {
 
     /// Total value of proofs in [`SwapRequest`]
     pub fn input_amount(&self) -> Result<Amount, Error> {
-        Amount::try_sum(self.inputs.iter().map(|proof| proof.amount))
+        Ok(Amount::try_sum(
+            self.inputs.iter().map(|proof| proof.amount),
+        )?)
     }
 
     /// Total value of outputs in [`SwapRequest`]
     pub fn output_amount(&self) -> Result<Amount, Error> {
-        Amount::try_sum(self.outputs.iter().map(|proof| proof.amount))
+        Ok(Amount::try_sum(
+            self.outputs.iter().map(|proof| proof.amount),
+        )?)
     }
 }
 
@@ -63,10 +79,10 @@ impl SwapResponse {
 
     /// Total [`Amount`] of promises
     pub fn promises_amount(&self) -> Result<Amount, Error> {
-        Amount::try_sum(
+        Ok(Amount::try_sum(
             self.signatures
                 .iter()
                 .map(|BlindSignature { amount, .. }| *amount),
-        )
+        )?)
     }
 }

+ 12 - 2
crates/cdk/src/nuts/nut10.rs

@@ -6,8 +6,18 @@ use std::str::FromStr;
 
 use serde::ser::SerializeTuple;
 use serde::{Deserialize, Serialize, Serializer};
-
-use crate::error::Error;
+use thiserror::Error;
+
+/// NUT13 Error
+#[derive(Debug, Error)]
+pub enum Error {
+    /// Secret error
+    #[error(transparent)]
+    Secret(#[from] crate::secret::Error),
+    /// Serde Json error
+    #[error(transparent)]
+    SerdeJsonError(#[from] serde_json::Error),
+}
 
 ///  NUT10 Secret Kind
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]

+ 2 - 2
crates/cdk/src/nuts/nut12.rs

@@ -26,9 +26,9 @@ pub enum Error {
     /// Invalid DLEQ Proof
     #[error("Invalid DLEQ proof")]
     InvalidDleqProof,
-    /// Cashu Error
+    /// DHKE error
     #[error(transparent)]
-    Cashu(#[from] crate::error::Error),
+    DHKE(#[from] crate::dhke::Error),
     /// NUT01 Error
     #[error(transparent)]
     NUT01(#[from] crate::nuts::nut01::Error),

+ 21 - 1
crates/cdk/src/nuts/nut13.rs

@@ -3,6 +3,7 @@
 //! <https://github.com/cashubtc/nuts/blob/main/13.md>
 
 use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
+use thiserror::Error;
 use tracing::instrument;
 
 use super::nut00::{BlindedMessage, PreMint, PreMintSecrets};
@@ -10,11 +11,30 @@ use super::nut01::SecretKey;
 use super::nut02::Id;
 use crate::amount::SplitTarget;
 use crate::dhke::blind_message;
-use crate::error::Error;
 use crate::secret::Secret;
 use crate::util::hex;
 use crate::{Amount, SECP256K1};
 
+/// NUT13 Error
+#[derive(Debug, Error)]
+pub enum Error {
+    /// DHKE error
+    #[error(transparent)]
+    DHKE(#[from] crate::dhke::Error),
+    /// Amount Error
+    #[error(transparent)]
+    Amount(#[from] crate::amount::Error),
+    /// NUT00 Error
+    #[error(transparent)]
+    NUT00(#[from] crate::nuts::nut00::Error),
+    /// NUT02 Error
+    #[error(transparent)]
+    NUT02(#[from] crate::nuts::nut02::Error),
+    /// Bip32 Error
+    #[error(transparent)]
+    Bip32(#[from] bitcoin::bip32::Error),
+}
+
 impl Secret {
     /// Create new [`Secret`] from xpriv
     pub fn from_xpriv(xpriv: ExtendedPrivKey, keyset_id: Id, counter: u32) -> Result<Self, Error> {

+ 5 - 2
crates/cdk/src/secret.rs

@@ -23,6 +23,9 @@ pub enum Error {
     /// Hex Error
     #[error(transparent)]
     Hex(#[from] hex::Error),
+    /// Serde Json error
+    #[error(transparent)]
+    SerdeJsonError(#[from] serde_json::Error),
 }
 
 impl Default for Secret {
@@ -119,10 +122,10 @@ impl TryFrom<Secret> for crate::nuts::nut10::Secret {
 }
 
 impl TryFrom<&Secret> for crate::nuts::nut10::Secret {
-    type Error = serde_json::Error;
+    type Error = Error;
 
     fn try_from(unchecked_secret: &Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> {
-        serde_json::from_str(&unchecked_secret.0)
+        Ok(serde_json::from_str(&unchecked_secret.0)?)
     }
 }
 

+ 1 - 3
crates/cdk/src/types.rs

@@ -44,9 +44,7 @@ impl ProofInfo {
         state: State,
         unit: CurrencyUnit,
     ) -> Result<Self, Error> {
-        let y = proof
-            .y()
-            .map_err(|_| Error::CustomError("Could not find y".to_string()))?;
+        let y = proof.y()?;
 
         let spending_condition: Option<SpendingConditions> = (&proof.secret).try_into().ok();
 

+ 0 - 148
crates/cdk/src/wallet/error.rs

@@ -1,148 +0,0 @@
-//! CDK Wallet Error
-
-use std::num::ParseIntError;
-
-use thiserror::Error;
-
-use super::multi_mint_wallet::WalletKey;
-use crate::cdk_database;
-use crate::error::{ErrorCode, ErrorResponse};
-use crate::util::hex;
-
-/// Wallet Error
-#[derive(Debug, Error)]
-pub enum Error {
-    /// Insufficient Funds
-    #[error("Insufficient funds")]
-    InsufficientFunds,
-    /// Quote Expired
-    #[error("Quote expired")]
-    QuoteExpired,
-    /// Unknown Quote
-    #[error("Quote unknown")]
-    QuoteUnknown,
-    /// Not active keyset
-    #[error("No active keyset")]
-    NoActiveKeyset,
-    /// Invalid DLEQ proof
-    #[error("Could not verify DLEQ proof")]
-    CouldNotVerifyDleq,
-    /// P2PK spending conditions not met
-    #[error("P2PK condition not met `{0}`")]
-    P2PKConditionsNotMet(String),
-    /// Invalid Spending Conditions
-    #[error("Invalid spending conditions: `{0}`")]
-    InvalidSpendConditions(String),
-    /// Preimage not provided
-    #[error("Preimage not provided")]
-    PreimageNotProvided,
-    /// Unknown Key
-    #[error("Unknown key")]
-    UnknownKey,
-    /// Amount overflow
-    #[error("Amount Overflow")]
-    AmountOverflow,
-    /// Spending Locktime not provided
-    #[error("Spending condition locktime not provided")]
-    LocktimeNotProvided,
-    /// Url path segments could not be joined
-    #[error("Url path segments could not be joined")]
-    UrlPathSegments,
-    /// Quote not paid
-    #[error("Quote not paid")]
-    QuoteNotePaid,
-    /// Token Already Spent
-    #[error("Token already spent")]
-    TokenAlreadySpent,
-    /// Unit Not supported
-    #[error("Unit not supported for method")]
-    UnitNotSupported,
-    /// Bolt11 invoice does not have amount
-    #[error("Invoice amount undefined")]
-    InvoiceAmountUndefined,
-    /// Incorrect quote amount
-    #[error("Incorrect quote amount")]
-    IncorrectQuoteAmount,
-    /// Keyset Not Found
-    #[error("Keyset not found")]
-    KeysetNotFound,
-    /// Receive can only be used with tokens from single mint
-    #[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
-    MultiMintTokenNotSupported,
-    /// Incorrect Mint
-    /// Token does not match wallet mint
-    #[error("Token does not match wallet mint")]
-    IncorrectMint,
-    /// From hex error
-    #[error(transparent)]
-    ReqwestError(#[from] reqwest::Error),
-    ///  Unknown error response
-    #[error("Unknown error response: `{0}`")]
-    UnknownErrorResponse(String),
-    /// Hex Error
-    #[error(transparent)]
-    HexError(#[from] hex::Error),
-    /// Unknown Wallet
-    #[error("Unknown wallet: `{0}`")]
-    UnknownWallet(WalletKey),
-    /// Incorrect Wallet
-    #[error("Incorrect wallet: `{0}`")]
-    IncorrectWallet(String),
-    /// Max Fee Ecxeded
-    #[error("Max fee exceeded")]
-    MaxFeeExceeded,
-    /// Transaction unbalanced
-    #[error("Transaction is unbalanced")]
-    TransactionUnbalanced,
-    /// CDK Error
-    #[error(transparent)]
-    Cashu(#[from] crate::error::Error),
-    /// Cashu Url Error
-    #[error(transparent)]
-    CashuUrl(#[from] crate::mint_url::Error),
-    /// Database Error
-    #[error(transparent)]
-    Database(#[from] crate::cdk_database::Error),
-    /// NUT00 Error
-    #[error(transparent)]
-    NUT00(#[from] crate::nuts::nut00::Error),
-    /// NUT01 Error
-    #[error(transparent)]
-    NUT01(#[from] crate::nuts::nut01::Error),
-    /// NUT11 Error
-    #[error(transparent)]
-    NUT11(#[from] crate::nuts::nut11::Error),
-    /// NUT12 Error
-    #[error(transparent)]
-    NUT12(#[from] crate::nuts::nut12::Error),
-    /// Parse int
-    #[error(transparent)]
-    ParseInt(#[from] ParseIntError),
-    /// Parse invoice error
-    #[error(transparent)]
-    Invoice(#[from] lightning_invoice::ParseOrSemanticError),
-    /// Serde Error
-    #[error(transparent)]
-    Serde(#[from] serde_json::Error),
-    /// Custom Error
-    #[error("`{0}`")]
-    Custom(String),
-}
-
-impl From<Error> for cdk_database::Error {
-    fn from(e: Error) -> Self {
-        Self::Database(Box::new(e))
-    }
-}
-
-impl From<ErrorResponse> for Error {
-    fn from(err: ErrorResponse) -> Error {
-        match err.code {
-            ErrorCode::QuoteNotPaid => Self::QuoteNotePaid,
-            ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
-            ErrorCode::KeysetNotFound => Self::KeysetNotFound,
-            ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced,
-            _ => Self::UnknownErrorResponse(err.to_string()),
-        }
-    }
-}

+ 14 - 15
crates/cdk/src/wallet/mod.rs

@@ -9,12 +9,12 @@ use bitcoin::hashes::sha256::Hash as Sha256Hash;
 use bitcoin::hashes::Hash;
 use bitcoin::key::XOnlyPublicKey;
 use bitcoin::Network;
-use error::Error;
 use tracing::instrument;
 
 use crate::amount::SplitTarget;
 use crate::cdk_database::{self, WalletDatabase};
 use crate::dhke::{construct_proofs, hash_to_curve};
+use crate::error::Error;
 use crate::mint_url::MintUrl;
 use crate::nuts::nut00::token::Token;
 use crate::nuts::{
@@ -28,7 +28,6 @@ use crate::util::{hex, unix_time};
 use crate::{Amount, Bolt11Invoice, HttpClient, SECP256K1};
 
 pub mod client;
-pub mod error;
 pub mod multi_mint_wallet;
 pub mod types;
 pub mod util;
@@ -108,7 +107,7 @@ impl Wallet {
                 .localstore
                 .get_keyset_by_id(&proof.keyset_id)
                 .await?
-                .ok_or(Error::UnknownKey)?;
+                .ok_or(Error::UnknownKeySet)?;
 
             sum_fee += input_fee_ppk.input_fee_ppk;
         }
@@ -125,7 +124,7 @@ impl Wallet {
             .localstore
             .get_keyset_by_id(keyset_id)
             .await?
-            .ok_or(Error::UnknownKey)?
+            .ok_or(Error::UnknownKeySet)?
             .input_fee_ppk;
 
         let fee = (input_fee_ppk * count + 999) / 1000;
@@ -615,12 +614,12 @@ impl Wallet {
 
         let quote_info = if let Some(quote) = quote_info {
             if quote.expiry.le(&unix_time()) && quote.expiry.ne(&0) {
-                return Err(Error::QuoteExpired);
+                return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
             }
 
             quote.clone()
         } else {
-            return Err(Error::QuoteUnknown);
+            return Err(Error::UnknownQuote);
         };
 
         let active_keyset_id = self.get_active_mint_keyset().await?.id;
@@ -663,7 +662,7 @@ impl Wallet {
         {
             for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
                 let keys = self.get_keyset_keys(sig.keyset_id).await?;
-                let key = keys.amount_key(sig.amount).ok_or(Error::UnknownKey)?;
+                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),
@@ -1292,7 +1291,7 @@ impl Wallet {
         let amount = match self.unit {
             CurrencyUnit::Sat => Amount::from(request_amount / 1000),
             CurrencyUnit::Msat => Amount::from(request_amount),
-            _ => return Err(Error::UnitNotSupported),
+            _ => return Err(Error::UnitUnsupported),
         };
 
         let quote_res = self
@@ -1352,12 +1351,12 @@ impl Wallet {
         let quote_info = self.localstore.get_melt_quote(quote_id).await?;
         let quote_info = if let Some(quote) = quote_info {
             if quote.expiry.le(&unix_time()) {
-                return Err(Error::QuoteExpired);
+                return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
             }
 
             quote.clone()
         } else {
-            return Err(Error::QuoteUnknown);
+            return Err(Error::UnknownQuote);
         };
 
         let ys = proofs
@@ -1503,12 +1502,12 @@ impl Wallet {
 
         let quote_info = if let Some(quote) = quote_info {
             if quote.expiry.le(&unix_time()) {
-                return Err(Error::QuoteExpired);
+                return Err(Error::ExpiredQuote(quote.expiry, unix_time()));
             }
 
             quote.clone()
         } else {
-            return Err(Error::QuoteUnknown);
+            return Err(Error::UnknownQuote);
         };
 
         let inputs_needed_amount = quote_info.amount + quote_info.fee_reserve;
@@ -1684,7 +1683,7 @@ impl Wallet {
             // Verify that proof DLEQ is valid
             if proof.dleq.is_some() {
                 let keys = self.get_keyset_keys(proof.keyset_id).await?;
-                let key = keys.amount_key(proof.amount).ok_or(Error::UnknownKey)?;
+                let key = keys.amount_key(proof.amount).ok_or(Error::AmountKey)?;
                 proof.verify_dleq(key)?;
             }
 
@@ -1815,7 +1814,7 @@ impl Wallet {
         let unit = token_data.unit().unwrap_or_default();
 
         if unit != self.unit {
-            return Err(Error::UnitNotSupported);
+            return Err(Error::UnitUnsupported);
         }
 
         let proofs = token_data.proofs();
@@ -2108,7 +2107,7 @@ impl Wallet {
                         key
                     }
                 }
-                .ok_or(Error::UnknownKey)?;
+                .ok_or(Error::AmountKey)?;
 
                 proof
                     .verify_dleq(mint_pubkey)

+ 4 - 1
justfile

@@ -7,7 +7,7 @@ alias t := test
 default:
   @just --list
 
-final-check: format clippy test
+final-check: typos format clippy test
 
 # run `cargo build` on everything
 build *ARGS="--workspace --all-targets":
@@ -54,6 +54,9 @@ clippy *ARGS="--locked --offline --workspace --all-targets":
 clippy-fix *ARGS="--locked --offline --workspace --all-targets":
   cargo clippy {{ARGS}} --fix
 
+typos: 
+  typos
+
 # fix all typos
 [no-exit-message]
 typos-fix: