cdk_common/
error.rs

1//! Errors
2
3use std::array::TryFromSliceError;
4use std::fmt;
5
6use cashu::{CurrencyUnit, PaymentMethod};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use serde_json::Value;
9use thiserror::Error;
10
11use crate::nuts::Id;
12use crate::util::hex;
13#[cfg(feature = "wallet")]
14use crate::wallet::WalletKey;
15use crate::Amount;
16
17/// CDK Error
18#[derive(Debug, Error)]
19pub enum Error {
20    /// Mint does not have a key for amount
21    #[error("No Key for Amount")]
22    AmountKey,
23    /// Keyset is not known
24    #[error("Keyset id not known: `{0}`")]
25    KeysetUnknown(Id),
26    /// Unsupported unit
27    #[error("Unit unsupported")]
28    UnsupportedUnit,
29    /// Payment failed
30    #[error("Payment failed")]
31    PaymentFailed,
32    /// Payment pending
33    #[error("Payment pending")]
34    PaymentPending,
35    /// Invoice already paid
36    #[error("Request already paid")]
37    RequestAlreadyPaid,
38    /// Invalid payment request
39    #[error("Invalid payment request")]
40    InvalidPaymentRequest,
41    /// Bolt11 invoice does not have amount
42    #[error("Invoice Amount undefined")]
43    InvoiceAmountUndefined,
44    /// Split Values must be less then or equal to amount
45    #[error("Split Values must be less then or equal to amount")]
46    SplitValuesGreater,
47    /// Amount overflow
48    #[error("Amount Overflow")]
49    AmountOverflow,
50    /// Witness missing or invalid
51    #[error("Signature missing or invalid")]
52    SignatureMissingOrInvalid,
53    /// Amountless Invoice Not supported
54    #[error("Amount Less Invoice is not allowed")]
55    AmountLessNotAllowed,
56    /// Multi-Part Internal Melt Quotes are not supported
57    #[error("Multi-Part Internal Melt Quotes are not supported")]
58    InternalMultiPartMeltQuote,
59    /// Multi-Part Payment not supported for unit and method
60    #[error("Multi-Part payment is not supported for unit `{0}` and method `{1}`")]
61    MppUnitMethodNotSupported(CurrencyUnit, PaymentMethod),
62    /// Clear Auth Required
63    #[error("Clear Auth Required")]
64    ClearAuthRequired,
65    /// Blind Auth Required
66    #[error("Blind Auth Required")]
67    BlindAuthRequired,
68    /// Clear Auth Failed
69    #[error("Clear Auth Failed")]
70    ClearAuthFailed,
71    /// Blind Auth Failed
72    #[error("Blind Auth Failed")]
73    BlindAuthFailed,
74    /// Auth settings undefined
75    #[error("Auth settings undefined")]
76    AuthSettingsUndefined,
77    /// Mint time outside of tolerance
78    #[error("Mint time outside of tolerance")]
79    MintTimeExceedsTolerance,
80    /// Insufficient blind auth tokens
81    #[error("Insufficient blind auth tokens, must reauth")]
82    InsufficientBlindAuthTokens,
83    /// Auth localstore undefined
84    #[error("Auth localstore undefined")]
85    AuthLocalstoreUndefined,
86    /// Wallet cat not set
87    #[error("Wallet cat not set")]
88    CatNotSet,
89    /// Could not get mint info
90    #[error("Could not get mint info")]
91    CouldNotGetMintInfo,
92    /// Multi-Part Payment not supported for unit and method
93    #[error("Amountless invoices are not supported for unit `{0}` and method `{1}`")]
94    AmountlessInvoiceNotSupported(CurrencyUnit, PaymentMethod),
95    /// Duplicate Payment id
96    #[error("Payment id seen for mint")]
97    DuplicatePaymentId,
98    /// Pubkey required
99    #[error("Pubkey required")]
100    PubkeyRequired,
101    /// Invalid payment method
102    #[error("Invalid payment method")]
103    InvalidPaymentMethod,
104    /// Amount undefined
105    #[error("Amount undefined")]
106    AmountUndefined,
107    /// Unsupported payment method
108    #[error("Payment method unsupported")]
109    UnsupportedPaymentMethod,
110    /// Could not parse bolt12
111    #[error("Could not parse bolt12")]
112    Bolt12parse,
113
114    /// BIP353 address parsing error
115    #[error("Failed to parse BIP353 address: {0}")]
116    Bip353Parse(String),
117
118    /// Operation timeout
119    #[error("Operation timeout")]
120    Timeout,
121
122    /// BIP353 address resolution error
123    #[error("Failed to resolve BIP353 address: {0}")]
124    Bip353Resolve(String),
125    /// BIP353 no Lightning offer found
126    #[error("No Lightning offer found in BIP353 payment instructions")]
127    Bip353NoLightningOffer,
128
129    /// Internal Error - Send error
130    #[error("Internal send error: {0}")]
131    SendError(String),
132
133    /// Internal Error - Recv error
134    #[error("Internal receive error: {0}")]
135    RecvError(String),
136
137    // Mint Errors
138    /// Minting is disabled
139    #[error("Minting is disabled")]
140    MintingDisabled,
141    /// Quote is not known
142    #[error("Unknown quote")]
143    UnknownQuote,
144    /// Quote is expired
145    #[error("Expired quote: Expired: `{0}`, Time: `{1}`")]
146    ExpiredQuote(u64, u64),
147    /// Amount is outside of allowed range
148    #[error("Amount must be between `{0}` and `{1}` is `{2}`")]
149    AmountOutofLimitRange(Amount, Amount, Amount),
150    /// Quote is not paiud
151    #[error("Quote not paid")]
152    UnpaidQuote,
153    /// Quote is pending
154    #[error("Quote pending")]
155    PendingQuote,
156    /// ecash already issued for quote
157    #[error("Quote already issued")]
158    IssuedQuote,
159    /// Quote has already been paid
160    #[error("Quote is already paid")]
161    PaidQuote,
162    /// Payment state is unknown
163    #[error("Payment state is unknown")]
164    UnknownPaymentState,
165    /// Melting is disabled
166    #[error("Minting is disabled")]
167    MeltingDisabled,
168    /// Unknown Keyset
169    #[error("Unknown Keyset")]
170    UnknownKeySet,
171    /// BlindedMessage is already signed
172    #[error("Blinded Message is already signed")]
173    BlindedMessageAlreadySigned,
174    /// Inactive Keyset
175    #[error("Inactive Keyset")]
176    InactiveKeyset,
177    /// Transaction unbalanced
178    #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
179    TransactionUnbalanced(u64, u64, u64),
180    /// Duplicate proofs provided
181    #[error("Duplicate Inputs")]
182    DuplicateInputs,
183    /// Duplicate output
184    #[error("Duplicate outputs")]
185    DuplicateOutputs,
186    /// Multiple units provided
187    #[error("Cannot have multiple units")]
188    MultipleUnits,
189    /// Unit mismatch
190    #[error("Input unit must match output")]
191    UnitMismatch,
192    /// Sig all cannot be used in melt
193    #[error("Sig all cannot be used in melt")]
194    SigAllUsedInMelt,
195    /// Token is already spent
196    #[error("Token Already Spent")]
197    TokenAlreadySpent,
198    /// Token is already pending
199    #[error("Token Pending")]
200    TokenPending,
201    /// Internal Error
202    #[error("Internal Error")]
203    Internal,
204    /// Oidc config not set
205    #[error("Oidc client not set")]
206    OidcNotSet,
207
208    // Wallet Errors
209    /// P2PK spending conditions not met
210    #[error("P2PK condition not met `{0}`")]
211    P2PKConditionsNotMet(String),
212    /// Duplicate signature from same pubkey in P2PK
213    #[error("Duplicate signature from same pubkey in P2PK")]
214    DuplicateSignatureError,
215    /// Spending Locktime not provided
216    #[error("Spending condition locktime not provided")]
217    LocktimeNotProvided,
218    /// Invalid Spending Conditions
219    #[error("Invalid spending conditions: `{0}`")]
220    InvalidSpendConditions(String),
221    /// Incorrect Wallet
222    #[error("Incorrect wallet: `{0}`")]
223    IncorrectWallet(String),
224    /// Unknown Wallet
225    #[error("Unknown wallet: `{0}`")]
226    #[cfg(feature = "wallet")]
227    UnknownWallet(WalletKey),
228    /// Max Fee Ecxeded
229    #[error("Max fee exceeded")]
230    MaxFeeExceeded,
231    /// Url path segments could not be joined
232    #[error("Url path segments could not be joined")]
233    UrlPathSegments,
234    ///  Unknown error response
235    #[error("Unknown error response: `{0}`")]
236    UnknownErrorResponse(String),
237    /// Invalid DLEQ proof
238    #[error("Could not verify DLEQ proof")]
239    CouldNotVerifyDleq,
240    /// Dleq Proof not provided for signature
241    #[error("Dleq proof not provided for signature")]
242    DleqProofNotProvided,
243    /// Incorrect Mint
244    /// Token does not match wallet mint
245    #[error("Token does not match wallet mint")]
246    IncorrectMint,
247    /// Receive can only be used with tokens from single mint
248    #[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
249    MultiMintTokenNotSupported,
250    /// Preimage not provided
251    #[error("Preimage not provided")]
252    PreimageNotProvided,
253
254    // MultiMint Wallet Errors
255    /// Currency unit mismatch in MultiMintWallet
256    #[error("Currency unit mismatch: wallet uses {expected}, but {found} provided")]
257    MultiMintCurrencyUnitMismatch {
258        /// Expected currency unit
259        expected: CurrencyUnit,
260        /// Found currency unit
261        found: CurrencyUnit,
262    },
263    /// Unknown mint in MultiMintWallet
264    #[error("Unknown mint: {mint_url}")]
265    UnknownMint {
266        /// URL of the unknown mint
267        mint_url: String,
268    },
269    /// Transfer between mints timed out
270    #[error("Transfer timeout: failed to transfer {amount} from {source_mint} to {target_mint}")]
271    TransferTimeout {
272        /// Source mint URL
273        source_mint: String,
274        /// Target mint URL  
275        target_mint: String,
276        /// Amount that failed to transfer
277        amount: Amount,
278    },
279    /// Insufficient Funds
280    #[error("Insufficient funds")]
281    InsufficientFunds,
282    /// Unexpected proof state
283    #[error("Unexpected proof state")]
284    UnexpectedProofState,
285    /// No active keyset
286    #[error("No active keyset")]
287    NoActiveKeyset,
288    /// Incorrect quote amount
289    #[error("Incorrect quote amount")]
290    IncorrectQuoteAmount,
291    /// Invoice Description not supported
292    #[error("Invoice Description not supported")]
293    InvoiceDescriptionUnsupported,
294    /// Invalid transaction direction
295    #[error("Invalid transaction direction")]
296    InvalidTransactionDirection,
297    /// Invalid transaction id
298    #[error("Invalid transaction id")]
299    InvalidTransactionId,
300    /// Transaction not found
301    #[error("Transaction not found")]
302    TransactionNotFound,
303    /// KV Store invalid key or namespace
304    #[error("Invalid KV store key or namespace: {0}")]
305    KVStoreInvalidKey(String),
306    /// Custom Error
307    #[error("`{0}`")]
308    Custom(String),
309
310    // External Error conversions
311    /// Parse invoice error
312    #[error(transparent)]
313    Invoice(#[from] lightning_invoice::ParseOrSemanticError),
314    /// Bip32 error
315    #[error(transparent)]
316    Bip32(#[from] bitcoin::bip32::Error),
317    /// Parse int error
318    #[error(transparent)]
319    ParseInt(#[from] std::num::ParseIntError),
320    /// Parse 9rl Error
321    #[error(transparent)]
322    UrlParseError(#[from] url::ParseError),
323    /// Utf8 parse error
324    #[error(transparent)]
325    Utf8ParseError(#[from] std::string::FromUtf8Error),
326    /// Serde Json error
327    #[error(transparent)]
328    SerdeJsonError(#[from] serde_json::Error),
329    /// Base64 error
330    #[error(transparent)]
331    Base64Error(#[from] bitcoin::base64::DecodeError),
332    /// From hex error
333    #[error(transparent)]
334    HexError(#[from] hex::Error),
335    /// Http transport error
336    #[error("Http transport error {0:?}: {1}")]
337    HttpError(Option<u16>, String),
338    #[cfg(feature = "wallet")]
339    // Crate error conversions
340    /// Cashu Url Error
341    #[error(transparent)]
342    CashuUrl(#[from] crate::mint_url::Error),
343    /// Secret error
344    #[error(transparent)]
345    Secret(#[from] crate::secret::Error),
346    /// Amount Error
347    #[error(transparent)]
348    AmountError(#[from] crate::amount::Error),
349    /// DHKE Error
350    #[error(transparent)]
351    DHKE(#[from] crate::dhke::Error),
352    /// NUT00 Error
353    #[error(transparent)]
354    NUT00(#[from] crate::nuts::nut00::Error),
355    /// Nut01 error
356    #[error(transparent)]
357    NUT01(#[from] crate::nuts::nut01::Error),
358    /// NUT02 error
359    #[error(transparent)]
360    NUT02(#[from] crate::nuts::nut02::Error),
361    /// NUT03 error
362    #[error(transparent)]
363    NUT03(#[from] crate::nuts::nut03::Error),
364    /// NUT04 error
365    #[error(transparent)]
366    NUT04(#[from] crate::nuts::nut04::Error),
367    /// NUT05 error
368    #[error(transparent)]
369    NUT05(#[from] crate::nuts::nut05::Error),
370    /// NUT11 Error
371    #[error(transparent)]
372    NUT11(#[from] crate::nuts::nut11::Error),
373    /// NUT12 Error
374    #[error(transparent)]
375    NUT12(#[from] crate::nuts::nut12::Error),
376    /// NUT13 Error
377    #[error(transparent)]
378    #[cfg(feature = "wallet")]
379    NUT13(#[from] crate::nuts::nut13::Error),
380    /// NUT14 Error
381    #[error(transparent)]
382    NUT14(#[from] crate::nuts::nut14::Error),
383    /// NUT18 Error
384    #[error(transparent)]
385    NUT18(#[from] crate::nuts::nut18::Error),
386    /// NUT20 Error
387    #[error(transparent)]
388    NUT20(#[from] crate::nuts::nut20::Error),
389    /// NUT21 Error
390    #[error(transparent)]
391    #[cfg(feature = "auth")]
392    NUT21(#[from] crate::nuts::nut21::Error),
393    /// NUT22 Error
394    #[error(transparent)]
395    #[cfg(feature = "auth")]
396    NUT22(#[from] crate::nuts::nut22::Error),
397    /// NUT23 Error
398    #[error(transparent)]
399    NUT23(#[from] crate::nuts::nut23::Error),
400    /// Quote ID Error
401    #[error(transparent)]
402    #[cfg(feature = "mint")]
403    QuoteId(#[from] crate::quote_id::QuoteIdError),
404    /// From slice error
405    #[error(transparent)]
406    TryFromSliceError(#[from] TryFromSliceError),
407    /// Database Error
408    #[error(transparent)]
409    Database(crate::database::Error),
410    /// Payment Error
411    #[error(transparent)]
412    #[cfg(feature = "mint")]
413    Payment(#[from] crate::payment::Error),
414}
415
416/// CDK Error Response
417///
418/// See NUT definition in [00](https://github.com/cashubtc/nuts/blob/main/00.md)
419#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
420#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
421pub struct ErrorResponse {
422    /// Error Code
423    pub code: ErrorCode,
424    /// Human readable description
425    #[serde(default)]
426    pub detail: String,
427}
428
429impl fmt::Display for ErrorResponse {
430    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
431        write!(f, "code: {}, detail: {}", self.code, self.detail)
432    }
433}
434
435impl ErrorResponse {
436    /// Create new [`ErrorResponse`]
437    pub fn new(code: ErrorCode, detail: String) -> Self {
438        Self { code, detail }
439    }
440
441    /// Error response from json
442    pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
443        let value: Value = serde_json::from_str(json)?;
444
445        Self::from_value(value)
446    }
447
448    /// Error response from json Value
449    pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
450        match serde_json::from_value::<ErrorResponse>(value.clone()) {
451            Ok(res) => Ok(res),
452            Err(_) => Ok(Self {
453                code: ErrorCode::Unknown(999),
454                detail: value.to_string(),
455            }),
456        }
457    }
458}
459
460/// Maps NUT11 errors to appropriate error codes
461fn map_nut11_error(nut11_error: &crate::nuts::nut11::Error) -> ErrorCode {
462    match nut11_error {
463        crate::nuts::nut11::Error::SignaturesNotProvided => ErrorCode::WitnessMissingOrInvalid,
464        crate::nuts::nut11::Error::InvalidSignature => ErrorCode::WitnessMissingOrInvalid,
465        crate::nuts::nut11::Error::DuplicateSignature => ErrorCode::DuplicateSignature,
466        _ => ErrorCode::Unknown(9999), // Parsing/validation errors
467    }
468}
469
470impl From<Error> for ErrorResponse {
471    fn from(err: Error) -> ErrorResponse {
472        match err {
473            Error::TokenAlreadySpent => ErrorResponse {
474                code: ErrorCode::TokenAlreadySpent,
475                detail: err.to_string(),
476            },
477            Error::UnsupportedUnit => ErrorResponse {
478                code: ErrorCode::UnsupportedUnit,
479                detail: err.to_string(),
480            },
481            Error::PaymentFailed => ErrorResponse {
482                code: ErrorCode::LightningError,
483                detail: err.to_string(),
484            },
485            Error::RequestAlreadyPaid => ErrorResponse {
486                code: ErrorCode::InvoiceAlreadyPaid,
487                detail: "Invoice already paid.".to_string(),
488            },
489            Error::TransactionUnbalanced(inputs_total, outputs_total, fee_expected) => {
490                ErrorResponse {
491                    code: ErrorCode::TransactionUnbalanced,
492                    detail: format!(
493                        "Inputs: {inputs_total}, Outputs: {outputs_total}, expected_fee: {fee_expected}. Transaction inputs should equal outputs less fee"
494                    ),
495                }
496            }
497            Error::MintingDisabled => ErrorResponse {
498                code: ErrorCode::MintingDisabled,
499                detail: err.to_string(),
500            },
501            Error::BlindedMessageAlreadySigned => ErrorResponse {
502                code: ErrorCode::BlindedMessageAlreadySigned,
503                detail: err.to_string(),
504            },
505            Error::InsufficientFunds => ErrorResponse {
506                code: ErrorCode::TransactionUnbalanced,
507                detail: err.to_string(),
508            },
509            Error::AmountOutofLimitRange(_min, _max, _amount) => ErrorResponse {
510                code: ErrorCode::AmountOutofLimitRange,
511                detail: err.to_string(),
512            },
513            Error::ExpiredQuote(_, _) => ErrorResponse {
514                code: ErrorCode::QuoteExpired,
515                detail: err.to_string(),
516            },
517            Error::PendingQuote => ErrorResponse {
518                code: ErrorCode::QuotePending,
519                detail: err.to_string(),
520            },
521            Error::TokenPending => ErrorResponse {
522                code: ErrorCode::TokenPending,
523                detail: err.to_string(),
524            },
525            Error::ClearAuthRequired => ErrorResponse {
526                code: ErrorCode::ClearAuthRequired,
527                detail: Error::ClearAuthRequired.to_string(),
528            },
529            Error::ClearAuthFailed => ErrorResponse {
530                code: ErrorCode::ClearAuthFailed,
531                detail: Error::ClearAuthFailed.to_string(),
532            },
533            Error::BlindAuthRequired => ErrorResponse {
534                code: ErrorCode::BlindAuthRequired,
535                detail: Error::BlindAuthRequired.to_string(),
536            },
537            Error::BlindAuthFailed => ErrorResponse {
538                code: ErrorCode::BlindAuthFailed,
539                detail: Error::BlindAuthFailed.to_string(),
540            },
541            Error::NUT20(err) => ErrorResponse {
542                code: ErrorCode::WitnessMissingOrInvalid,
543                detail: err.to_string(),
544            },
545            Error::DuplicateInputs => ErrorResponse {
546                code: ErrorCode::DuplicateInputs,
547                detail: err.to_string(),
548            },
549            Error::DuplicateOutputs => ErrorResponse {
550                code: ErrorCode::DuplicateOutputs,
551                detail: err.to_string(),
552            },
553            Error::MultipleUnits => ErrorResponse {
554                code: ErrorCode::MultipleUnits,
555                detail: err.to_string(),
556            },
557            Error::UnitMismatch => ErrorResponse {
558                code: ErrorCode::UnitMismatch,
559                detail: err.to_string(),
560            },
561            Error::UnpaidQuote => ErrorResponse {
562                code: ErrorCode::QuoteNotPaid,
563                detail: Error::UnpaidQuote.to_string(),
564            },
565            Error::NUT11(err) => {
566                let code = map_nut11_error(&err);
567                let extra = if matches!(err, crate::nuts::nut11::Error::SignaturesNotProvided) {
568                    Some("P2PK signatures are required but not provided".to_string())
569                } else {
570                    None
571                };
572                ErrorResponse {
573                    code,
574                    detail: match extra {
575                        Some(extra) => format!("{err}. {extra}"),
576                        None => err.to_string(),
577                    },
578                }
579            },
580            Error::DuplicateSignatureError => ErrorResponse {
581                code: ErrorCode::DuplicateSignature,
582                detail: err.to_string(),
583            },
584            _ => ErrorResponse {
585                code: ErrorCode::Unknown(9999),
586                detail: err.to_string(),
587            },
588        }
589    }
590}
591
592#[cfg(feature = "mint")]
593impl From<crate::database::Error> for Error {
594    fn from(db_error: crate::database::Error) -> Self {
595        match db_error {
596            crate::database::Error::InvalidStateTransition(state) => match state {
597                crate::state::Error::Pending => Self::TokenPending,
598                crate::state::Error::AlreadySpent => Self::TokenAlreadySpent,
599                state => Self::Database(crate::database::Error::InvalidStateTransition(state)),
600            },
601            db_error => Self::Database(db_error),
602        }
603    }
604}
605
606#[cfg(not(feature = "mint"))]
607impl From<crate::database::Error> for Error {
608    fn from(db_error: crate::database::Error) -> Self {
609        Self::Database(db_error)
610    }
611}
612
613impl From<ErrorResponse> for Error {
614    fn from(err: ErrorResponse) -> Error {
615        match err.code {
616            ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
617            ErrorCode::QuoteNotPaid => Self::UnpaidQuote,
618            ErrorCode::QuotePending => Self::PendingQuote,
619            ErrorCode::QuoteExpired => Self::ExpiredQuote(0, 0),
620            ErrorCode::KeysetNotFound => Self::UnknownKeySet,
621            ErrorCode::KeysetInactive => Self::InactiveKeyset,
622            ErrorCode::BlindedMessageAlreadySigned => Self::BlindedMessageAlreadySigned,
623            ErrorCode::UnsupportedUnit => Self::UnsupportedUnit,
624            ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced(0, 0, 0),
625            ErrorCode::MintingDisabled => Self::MintingDisabled,
626            ErrorCode::InvoiceAlreadyPaid => Self::RequestAlreadyPaid,
627            ErrorCode::TokenNotVerified => Self::DHKE(crate::dhke::Error::TokenNotVerified),
628            ErrorCode::LightningError => Self::PaymentFailed,
629            ErrorCode::AmountOutofLimitRange => {
630                Self::AmountOutofLimitRange(Amount::default(), Amount::default(), Amount::default())
631            }
632            ErrorCode::TokenPending => Self::TokenPending,
633            ErrorCode::WitnessMissingOrInvalid => Self::SignatureMissingOrInvalid,
634            ErrorCode::DuplicateInputs => Self::DuplicateInputs,
635            ErrorCode::DuplicateOutputs => Self::DuplicateOutputs,
636            ErrorCode::MultipleUnits => Self::MultipleUnits,
637            ErrorCode::UnitMismatch => Self::UnitMismatch,
638            ErrorCode::ClearAuthRequired => Self::ClearAuthRequired,
639            ErrorCode::BlindAuthRequired => Self::BlindAuthRequired,
640            ErrorCode::DuplicateSignature => Self::DuplicateSignatureError,
641            _ => Self::UnknownErrorResponse(err.to_string()),
642        }
643    }
644}
645
646/// Possible Error Codes
647#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
648#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
649pub enum ErrorCode {
650    /// Token is already spent
651    TokenAlreadySpent,
652    /// Token Pending
653    TokenPending,
654    /// Quote is not paid
655    QuoteNotPaid,
656    /// Quote is not expired
657    QuoteExpired,
658    /// Quote Pending
659    QuotePending,
660    /// Keyset is not found
661    KeysetNotFound,
662    /// Keyset inactive
663    KeysetInactive,
664    /// Blinded Message Already signed
665    BlindedMessageAlreadySigned,
666    /// Unsupported unit
667    UnsupportedUnit,
668    /// Token already issed for quote
669    TokensAlreadyIssued,
670    /// Minting Disabled
671    MintingDisabled,
672    /// Invoice Already Paid
673    InvoiceAlreadyPaid,
674    /// Token Not Verified
675    TokenNotVerified,
676    /// Lightning Error
677    LightningError,
678    /// Unbalanced Error
679    TransactionUnbalanced,
680    /// Amount outside of allowed range
681    AmountOutofLimitRange,
682    /// Witness missing or invalid
683    WitnessMissingOrInvalid,
684    /// Duplicate Inputs
685    DuplicateInputs,
686    /// Duplicate Outputs
687    DuplicateOutputs,
688    /// Multiple Units
689    MultipleUnits,
690    /// Input unit does not match output
691    UnitMismatch,
692    /// Clear Auth Required
693    ClearAuthRequired,
694    /// Clear Auth Failed
695    ClearAuthFailed,
696    /// Blind Auth Required
697    BlindAuthRequired,
698    /// Blind Auth Failed
699    BlindAuthFailed,
700    /// Duplicate signature from same pubkey
701    DuplicateSignature,
702    /// Unknown error code
703    Unknown(u16),
704}
705
706impl ErrorCode {
707    /// Error code from u16
708    pub fn from_code(code: u16) -> Self {
709        match code {
710            10002 => Self::BlindedMessageAlreadySigned,
711            10003 => Self::TokenNotVerified,
712            11001 => Self::TokenAlreadySpent,
713            11002 => Self::TransactionUnbalanced,
714            11005 => Self::UnsupportedUnit,
715            11006 => Self::AmountOutofLimitRange,
716            11007 => Self::DuplicateInputs,
717            11008 => Self::DuplicateOutputs,
718            11009 => Self::MultipleUnits,
719            11010 => Self::UnitMismatch,
720            11012 => Self::TokenPending,
721            12001 => Self::KeysetNotFound,
722            12002 => Self::KeysetInactive,
723            20000 => Self::LightningError,
724            20001 => Self::QuoteNotPaid,
725            20002 => Self::TokensAlreadyIssued,
726            20003 => Self::MintingDisabled,
727            20005 => Self::QuotePending,
728            20006 => Self::InvoiceAlreadyPaid,
729            20007 => Self::QuoteExpired,
730            20008 => Self::WitnessMissingOrInvalid,
731            20009 => Self::DuplicateSignature,
732            30001 => Self::ClearAuthRequired,
733            30002 => Self::ClearAuthFailed,
734            31001 => Self::BlindAuthRequired,
735            31002 => Self::BlindAuthFailed,
736            _ => Self::Unknown(code),
737        }
738    }
739
740    /// Error code to u16
741    pub fn to_code(&self) -> u16 {
742        match self {
743            Self::BlindedMessageAlreadySigned => 10002,
744            Self::TokenNotVerified => 10003,
745            Self::TokenAlreadySpent => 11001,
746            Self::TransactionUnbalanced => 11002,
747            Self::UnsupportedUnit => 11005,
748            Self::AmountOutofLimitRange => 11006,
749            Self::DuplicateInputs => 11007,
750            Self::DuplicateOutputs => 11008,
751            Self::MultipleUnits => 11009,
752            Self::UnitMismatch => 11010,
753            Self::TokenPending => 11012,
754            Self::KeysetNotFound => 12001,
755            Self::KeysetInactive => 12002,
756            Self::LightningError => 20000,
757            Self::QuoteNotPaid => 20001,
758            Self::TokensAlreadyIssued => 20002,
759            Self::MintingDisabled => 20003,
760            Self::QuotePending => 20005,
761            Self::InvoiceAlreadyPaid => 20006,
762            Self::QuoteExpired => 20007,
763            Self::WitnessMissingOrInvalid => 20008,
764            Self::DuplicateSignature => 20009,
765            Self::ClearAuthRequired => 30001,
766            Self::ClearAuthFailed => 30002,
767            Self::BlindAuthRequired => 31001,
768            Self::BlindAuthFailed => 31002,
769            Self::Unknown(code) => *code,
770        }
771    }
772}
773
774impl Serialize for ErrorCode {
775    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
776    where
777        S: Serializer,
778    {
779        serializer.serialize_u16(self.to_code())
780    }
781}
782
783impl<'de> Deserialize<'de> for ErrorCode {
784    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
785    where
786        D: Deserializer<'de>,
787    {
788        let code = u16::deserialize(deserializer)?;
789
790        Ok(ErrorCode::from_code(code))
791    }
792}
793
794impl fmt::Display for ErrorCode {
795    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796        write!(f, "{}", self.to_code())
797    }
798}