Jelajahi Sumber

refactor: move client to wallet mod

refactor: client error

refactor: mint error mod

refactor: error codes

refactor: remove enum numbers
thesimplekid 9 bulan lalu
induk
melakukan
1ab1f47885

+ 60 - 11
crates/cdk/src/error.rs

@@ -3,7 +3,7 @@
 use std::fmt;
 use std::string::FromUtf8Error;
 
-use serde::{Deserialize, Serialize};
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use serde_json::Value;
 use thiserror::Error;
 
@@ -79,7 +79,7 @@ pub enum Error {
 
 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
 pub struct ErrorResponse {
-    pub code: u32,
+    pub code: ErrorCode,
     pub error: Option<String>,
     pub detail: Option<String>,
 }
@@ -98,24 +98,73 @@ impl fmt::Display for ErrorResponse {
 
 impl ErrorResponse {
     pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
-        match serde_json::from_str::<ErrorResponse>(json) {
-            Ok(res) => Ok(res),
-            Err(_) => Ok(Self {
-                code: 999,
-                error: Some(json.to_string()),
-                detail: None,
-            }),
-        }
+        let value: Value = serde_json::from_str(json)?;
+
+        Self::from_value(value)
     }
 
     pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
         match serde_json::from_value::<ErrorResponse>(value.clone()) {
             Ok(res) => Ok(res),
             Err(_) => Ok(Self {
-                code: 999,
+                code: ErrorCode::Unknown(999),
                 error: Some(value.to_string()),
                 detail: None,
             }),
         }
     }
 }
+
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+pub enum ErrorCode {
+    TokenAlreadySpent,
+    QuoteNotPaid,
+    KeysetNotFound,
+    Unknown(u16),
+}
+
+impl Serialize for ErrorCode {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        let code = match self {
+            ErrorCode::TokenAlreadySpent => 11001,
+            ErrorCode::QuoteNotPaid => 20001,
+            ErrorCode::KeysetNotFound => 12001,
+            ErrorCode::Unknown(code) => *code,
+        };
+
+        serializer.serialize_u16(code)
+    }
+}
+
+impl<'de> Deserialize<'de> for ErrorCode {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let code = u16::deserialize(deserializer)?;
+
+        let error_code = match code {
+            11001 => ErrorCode::TokenAlreadySpent,
+            20001 => ErrorCode::QuoteNotPaid,
+            12001 => ErrorCode::KeysetNotFound,
+            c => ErrorCode::Unknown(c),
+        };
+
+        Ok(error_code)
+    }
+}
+
+impl fmt::Display for ErrorCode {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let code = match self {
+            Self::TokenAlreadySpent => 11001,
+            Self::QuoteNotPaid => 20001,
+            Self::KeysetNotFound => 12001,
+            Self::Unknown(code) => *code,
+        };
+        write!(f, "{}", code)
+    }
+}

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

@@ -8,8 +8,6 @@ pub use lightning_invoice::{self, Bolt11Invoice};
 
 pub mod amount;
 pub mod cdk_database;
-#[cfg(feature = "wallet")]
-pub mod client;
 pub mod dhke;
 pub mod error;
 #[cfg(feature = "mint")]
@@ -23,8 +21,8 @@ pub mod util;
 pub mod wallet;
 
 pub use self::amount::Amount;
-#[cfg(feature = "wallet")]
-pub use self::client::HttpClient;
 pub use self::util::SECP256K1;
+#[cfg(feature = "wallet")]
+pub use self::wallet::client::HttpClient;
 
 pub type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;

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

@@ -0,0 +1,100 @@
+use http::StatusCode;
+use thiserror::Error;
+
+use crate::cdk_database;
+use crate::error::{ErrorCode, ErrorResponse};
+
+#[derive(Debug, Error)]
+pub enum Error {
+    /// Unknown Keyset
+    #[error("Unknown Keyset")]
+    UnknownKeySet,
+    /// Inactive Keyset
+    #[error("Inactive Keyset")]
+    InactiveKeyset,
+    #[error("No key for amount")]
+    AmountKey,
+    #[error("Amount")]
+    Amount,
+    #[error("Duplicate proofs")]
+    DuplicateProofs,
+    #[error("Token Already Spent")]
+    TokenAlreadySpent,
+    #[error("Token Pending")]
+    TokenPending,
+    #[error("Quote not paid")]
+    UnpaidQuote,
+    #[error("Unknown quote")]
+    UnknownQuote,
+    #[error("Unknown secret kind")]
+    UnknownSecretKind,
+    #[error("Cannot have multiple units")]
+    MultipleUnits,
+    #[error("Blinded Message is already signed")]
+    BlindedMessageAlreadySigned,
+    #[error(transparent)]
+    Cashu(#[from] crate::error::Error),
+    #[error(transparent)]
+    Secret(#[from] crate::secret::Error),
+    #[error(transparent)]
+    NUT00(#[from] crate::nuts::nut00::Error),
+    #[error(transparent)]
+    NUT11(#[from] crate::nuts::nut11::Error),
+    #[error(transparent)]
+    Nut12(#[from] crate::nuts::nut12::Error),
+    #[error(transparent)]
+    Nut14(#[from] crate::nuts::nut14::Error),
+    /// Database Error
+    #[error(transparent)]
+    Database(#[from] cdk_database::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,
+            },
+            _ => ErrorResponse {
+                code: ErrorCode::Unknown(9999),
+                error: Some(err.to_string()),
+                detail: None,
+            },
+        }
+    }
+}
+
+impl From<Error> for (StatusCode, ErrorResponse) {
+    fn from(err: Error) -> (StatusCode, ErrorResponse) {
+        (StatusCode::NOT_FOUND, err.into())
+    }
+}
+
+#[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);
+    }
+}

+ 4 - 73
crates/cdk/src/mint.rs → crates/cdk/src/mint/mod.rs

@@ -3,90 +3,21 @@ use std::sync::Arc;
 
 use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey};
 use bitcoin::secp256k1::{self, Secp256k1};
-use http::StatusCode;
+use error::Error;
 use serde::{Deserialize, Serialize};
-use thiserror::Error;
 use tokio::sync::RwLock;
 use tracing::{debug, error, info};
 
-use self::nut11::enforce_sig_flag;
 use crate::cdk_database::{self, MintDatabase};
 use crate::dhke::{hash_to_curve, sign_message, verify_message};
-use crate::error::ErrorResponse;
+use crate::nuts::nut11::enforce_sig_flag;
 use crate::nuts::*;
 use crate::types::{MeltQuote, MintQuote};
 use crate::url::UncheckedUrl;
 use crate::util::unix_time;
 use crate::Amount;
 
-#[derive(Debug, Error)]
-pub enum Error {
-    /// Unknown Keyset
-    #[error("Unknown Keyset")]
-    UnknownKeySet,
-    /// Inactive Keyset
-    #[error("Inactive Keyset")]
-    InactiveKeyset,
-    #[error("No key for amount")]
-    AmountKey,
-    #[error("Amount")]
-    Amount,
-    #[error("Duplicate proofs")]
-    DuplicateProofs,
-    #[error("Token Spent")]
-    TokenSpent,
-    #[error("Token Pending")]
-    TokenPending,
-    #[error("Quote not paid")]
-    UnpaidQuote,
-    #[error("`{0}`")]
-    Custom(String),
-    #[error(transparent)]
-    Cashu(#[from] crate::error::Error),
-    #[error(transparent)]
-    Secret(#[from] crate::secret::Error),
-    #[error(transparent)]
-    NUT00(#[from] crate::nuts::nut00::Error),
-    #[error(transparent)]
-    NUT11(#[from] crate::nuts::nut11::Error),
-    #[error(transparent)]
-    Nut12(#[from] crate::nuts::nut12::Error),
-    #[error(transparent)]
-    Nut14(#[from] crate::nuts::nut14::Error),
-    /// Database Error
-    #[error(transparent)]
-    Database(#[from] crate::cdk_database::Error),
-    #[error("Unknown quote")]
-    UnknownQuote,
-    #[error("Unknown secret kind")]
-    UnknownSecretKind,
-    #[error("Cannot have multiple units")]
-    MultipleUnits,
-    #[error("Blinded Message is already signed")]
-    BlindedMessageAlreadySigned,
-}
-
-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 {
-        ErrorResponse {
-            code: 9999,
-            error: Some(err.to_string()),
-            detail: None,
-        }
-    }
-}
-
-impl From<Error> for (StatusCode, ErrorResponse) {
-    fn from(err: Error) -> (StatusCode, ErrorResponse) {
-        (StatusCode::NOT_FOUND, err.into())
-    }
-}
+pub mod error;
 
 #[derive(Clone)]
 pub struct Mint {
@@ -493,7 +424,7 @@ impl Mint {
         let y: PublicKey = hash_to_curve(&proof.secret.to_bytes())?;
 
         if self.localstore.get_spent_proof_by_y(&y).await?.is_some() {
-            return Err(Error::TokenSpent);
+            return Err(Error::TokenAlreadySpent);
         }
 
         if self.localstore.get_pending_proof_by_y(&y).await?.is_some() {

+ 1 - 23
crates/cdk/src/client.rs → crates/cdk/src/wallet/client.rs

@@ -2,10 +2,10 @@
 
 use reqwest::Client;
 use serde_json::Value;
-use thiserror::Error;
 use tracing::instrument;
 use url::Url;
 
+use super::Error;
 use crate::error::ErrorResponse;
 use crate::nuts::{
     BlindedMessage, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysResponse,
@@ -16,28 +16,6 @@ use crate::nuts::{
 };
 use crate::{Amount, Bolt11Invoice};
 
-#[derive(Debug, Error)]
-pub enum Error {
-    /// Unknown Keyset
-    #[error("Url Path segments could not be joined")]
-    UrlPathSegments,
-    /// Serde Json error
-    #[error(transparent)]
-    SerdeJsonError(#[from] serde_json::Error),
-    /// From hex error
-    #[error(transparent)]
-    ReqwestError(#[from] reqwest::Error),
-    ///  Unknown error response
-    #[error("Unknown Error response: `{0}`")]
-    UnknownErrorResponse(String),
-}
-
-impl From<ErrorResponse> for Error {
-    fn from(err: ErrorResponse) -> Error {
-        Self::UnknownErrorResponse(err.to_string())
-    }
-}
-
 fn join_url(url: Url, paths: &[&str]) -> Result<Url, Error> {
     let mut url = url;
     for path in paths {

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

@@ -0,0 +1,115 @@
+use std::num::ParseIntError;
+
+use thiserror::Error;
+
+use crate::cdk_database;
+use crate::error::{ErrorCode, ErrorResponse};
+
+#[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 prood
+    #[error("Could not verify Dleq")]
+    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,
+    #[error("Unknown Key")]
+    UnknownKey,
+    /// Spending Locktime not provided
+    #[error("Spending condition locktime not provided")]
+    LocktimeNotProvided,
+    /// Unknown Keyset
+    #[error("Url Path segments could not be joined")]
+    UrlPathSegments,
+    /// Quote not paid
+    #[error("Quote not paid")]
+    QuoteNotePaid,
+    /// Token Already spent error
+    #[error("Token Already Spent Error")]
+    TokenAlreadySpent,
+    /// Keyset Not Found
+    #[error("Keyset Not Found")]
+    KeysetNotFound,
+    /// From hex error
+    #[error(transparent)]
+    ReqwestError(#[from] reqwest::Error),
+    ///  Unknown error response
+    #[error("Unknown Error response: `{0}`")]
+    UnknownErrorResponse(String),
+    /// CDK Error
+    #[error(transparent)]
+    Cashu(#[from] crate::error::Error),
+    /// Cashu Url Error
+    #[error(transparent)]
+    CashuUrl(#[from] crate::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),
+    /// Nostr Client Error
+    #[cfg(feature = "nostr")]
+    #[error(transparent)]
+    NostrClient(#[from] nostr_sdk::client::Error),
+    /// Nostr Key Error
+    #[cfg(feature = "nostr")]
+    #[error(transparent)]
+    NostrKey(#[from] nostr_sdk::key::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,
+            _ => Self::UnknownErrorResponse(err.to_string()),
+        }
+    }
+}

+ 4 - 74
crates/cdk/src/wallet.rs → crates/cdk/src/wallet/mod.rs

@@ -1,7 +1,6 @@
 //! Cashu Wallet
 
 use std::collections::{HashMap, HashSet};
-use std::num::ParseIntError;
 use std::ops::Deref;
 use std::str::FromStr;
 use std::sync::Arc;
@@ -11,17 +10,16 @@ use bitcoin::hashes::sha256::Hash as Sha256Hash;
 use bitcoin::hashes::Hash;
 use bitcoin::secp256k1::XOnlyPublicKey;
 use bitcoin::Network;
+use error::Error;
 #[cfg(feature = "nostr")]
 use nostr_sdk::nips::nip04;
 #[cfg(feature = "nostr")]
 use nostr_sdk::{Filter, Timestamp};
-use thiserror::Error;
 use tokio::sync::RwLock;
 use tracing::instrument;
 
 use crate::amount::SplitTarget;
 use crate::cdk_database::{self, WalletDatabase};
-use crate::client::HttpClient;
 use crate::dhke::{construct_proofs, hash_to_curve};
 use crate::nuts::{
     nut10, nut12, Conditions, CurrencyUnit, Id, KeySet, KeySetInfo, Keys, Kind,
@@ -32,78 +30,10 @@ use crate::nuts::{
 use crate::types::{MeltQuote, Melted, MintQuote, ProofInfo};
 use crate::url::UncheckedUrl;
 use crate::util::{hex, unix_time};
-use crate::{Amount, Bolt11Invoice};
-
-#[derive(Debug, Error)]
-pub enum Error {
-    /// Insufficient Funds
-    #[error("Insufficient Funds")]
-    InsufficientFunds,
-    #[error("Quote Expired")]
-    QuoteExpired,
-    #[error("Quote Unknown")]
-    QuoteUnknown,
-    #[error("No active keyset")]
-    NoActiveKeyset,
-    #[error(transparent)]
-    Cashu(#[from] crate::error::Error),
-    #[error("Could not verify Dleq")]
-    CouldNotVerifyDleq,
-    #[error("P2PK Condition Not met `{0}`")]
-    P2PKConditionsNotMet(String),
-    #[error("Invalid Spending Conditions: `{0}`")]
-    InvalidSpendConditions(String),
-    #[error("Preimage not provided")]
-    PreimageNotProvided,
-    #[error("Unknown Key")]
-    UnknownKey,
-    /// Spending Locktime not provided
-    #[error("Spending condition locktime not provided")]
-    LocktimeNotProvided,
-    /// Cashu Url Error
-    #[error(transparent)]
-    CashuUrl(#[from] crate::url::Error),
-    /// NUT11 Error
-    #[error(transparent)]
-    Client(#[from] crate::client::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),
-    #[error(transparent)]
-    Serde(#[from] serde_json::Error),
-    #[cfg(feature = "nostr")]
-    #[error(transparent)]
-    NostrClient(#[from] nostr_sdk::client::Error),
-    #[cfg(feature = "nostr")]
-    #[error(transparent)]
-    NostrKey(#[from] nostr_sdk::key::Error),
-    #[error("`{0}`")]
-    Custom(String),
-}
+use crate::{Amount, Bolt11Invoice, HttpClient};
 
-impl From<Error> for cdk_database::Error {
-    fn from(e: Error) -> Self {
-        Self::Database(Box::new(e))
-    }
-}
+pub mod client;
+pub mod error;
 
 #[derive(Clone)]
 pub struct Wallet {