1use std::fmt;
4
5use cashu::{CurrencyUnit, PaymentMethod};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use serde_json::Value;
8use thiserror::Error;
9
10use crate::nuts::Id;
11use crate::util::hex;
12#[cfg(feature = "wallet")]
13use crate::wallet::WalletKey;
14use crate::Amount;
15
16#[derive(Debug, Error)]
18pub enum Error {
19 #[error("No Key for Amount")]
21 AmountKey,
22 #[error("Keyset id not known: `{0}`")]
24 KeysetUnknown(Id),
25 #[error("Unit unsupported")]
27 UnsupportedUnit,
28 #[error("Payment failed")]
30 PaymentFailed,
31 #[error("Payment pending")]
33 PaymentPending,
34 #[error("Request already paid")]
36 RequestAlreadyPaid,
37 #[error("Invalid payment request")]
39 InvalidPaymentRequest,
40 #[error("Invoice Amount undefined")]
42 InvoiceAmountUndefined,
43 #[error("Split Values must be less then or equal to amount")]
45 SplitValuesGreater,
46 #[error("Amount Overflow")]
48 AmountOverflow,
49 #[error("Signature missing or invalid")]
51 SignatureMissingOrInvalid,
52 #[error("Amount Less Invoice is not allowed")]
54 AmountLessNotAllowed,
55 #[error("Multi-Part Internal Melt Quotes are not supported")]
57 InternalMultiPartMeltQuote,
58 #[error("Multi-Part payment is not supported for unit `{0}` and method `{1}`")]
60 MppUnitMethodNotSupported(CurrencyUnit, PaymentMethod),
61
62 #[error("Minting is disabled")]
65 MintingDisabled,
66 #[error("Unknown quote")]
68 UnknownQuote,
69 #[error("Expired quote: Expired: `{0}`, Time: `{1}`")]
71 ExpiredQuote(u64, u64),
72 #[error("Amount must be between `{0}` and `{1}` is `{2}`")]
74 AmountOutofLimitRange(Amount, Amount, Amount),
75 #[error("Quote not paid")]
77 UnpaidQuote,
78 #[error("Quote pending")]
80 PendingQuote,
81 #[error("Quote already issued")]
83 IssuedQuote,
84 #[error("Quote is already paid")]
86 PaidQuote,
87 #[error("Payment state is unknown")]
89 UnknownPaymentState,
90 #[error("Minting is disabled")]
92 MeltingDisabled,
93 #[error("Unknown Keyset")]
95 UnknownKeySet,
96 #[error("Blinded Message is already signed")]
98 BlindedMessageAlreadySigned,
99 #[error("Inactive Keyset")]
101 InactiveKeyset,
102 #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
104 TransactionUnbalanced(u64, u64, u64),
105 #[error("Duplicate Inputs")]
107 DuplicateInputs,
108 #[error("Duplicate outputs")]
110 DuplicateOutputs,
111 #[error("Cannot have multiple units")]
113 MultipleUnits,
114 #[error("Input unit must match output")]
116 UnitMismatch,
117 #[error("Sig all cannot be used in melt")]
119 SigAllUsedInMelt,
120 #[error("Token Already Spent")]
122 TokenAlreadySpent,
123 #[error("Token Pending")]
125 TokenPending,
126 #[error("Internal Error")]
128 Internal,
129
130 #[error("P2PK condition not met `{0}`")]
133 P2PKConditionsNotMet(String),
134 #[error("Spending condition locktime not provided")]
136 LocktimeNotProvided,
137 #[error("Invalid spending conditions: `{0}`")]
139 InvalidSpendConditions(String),
140 #[error("Incorrect wallet: `{0}`")]
142 IncorrectWallet(String),
143 #[error("Unknown wallet: `{0}`")]
145 #[cfg(feature = "wallet")]
146 UnknownWallet(WalletKey),
147 #[error("Max fee exceeded")]
149 MaxFeeExceeded,
150 #[error("Url path segments could not be joined")]
152 UrlPathSegments,
153 #[error("Unknown error response: `{0}`")]
155 UnknownErrorResponse(String),
156 #[error("Could not verify DLEQ proof")]
158 CouldNotVerifyDleq,
159 #[error("Token does not match wallet mint")]
162 IncorrectMint,
163 #[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
165 MultiMintTokenNotSupported,
166 #[error("Preimage not provided")]
168 PreimageNotProvided,
169 #[error("Insufficient funds")]
171 InsufficientFunds,
172 #[error("No active keyset")]
174 NoActiveKeyset,
175 #[error("Incorrect quote amount")]
177 IncorrectQuoteAmount,
178 #[error("Invoice Description not supported")]
180 InvoiceDescriptionUnsupported,
181 #[error("`{0}`")]
183 Custom(String),
184
185 #[error(transparent)]
188 Invoice(#[from] lightning_invoice::ParseOrSemanticError),
189 #[error(transparent)]
191 Bip32(#[from] bitcoin::bip32::Error),
192 #[error(transparent)]
194 ParseInt(#[from] std::num::ParseIntError),
195 #[error(transparent)]
197 UrlParseError(#[from] url::ParseError),
198 #[error(transparent)]
200 Utf8ParseError(#[from] std::string::FromUtf8Error),
201 #[error(transparent)]
203 SerdeJsonError(#[from] serde_json::Error),
204 #[error(transparent)]
206 Base64Error(#[from] bitcoin::base64::DecodeError),
207 #[error(transparent)]
209 HexError(#[from] hex::Error),
210 #[error("Http transport error: {0}")]
212 HttpError(String),
213
214 #[error(transparent)]
217 CashuUrl(#[from] crate::mint_url::Error),
218 #[error(transparent)]
220 Secret(#[from] crate::secret::Error),
221 #[error(transparent)]
223 AmountError(#[from] crate::amount::Error),
224 #[error(transparent)]
226 DHKE(#[from] crate::dhke::Error),
227 #[error(transparent)]
229 NUT00(#[from] crate::nuts::nut00::Error),
230 #[error(transparent)]
232 NUT01(#[from] crate::nuts::nut01::Error),
233 #[error(transparent)]
235 NUT02(#[from] crate::nuts::nut02::Error),
236 #[error(transparent)]
238 NUT03(#[from] crate::nuts::nut03::Error),
239 #[error(transparent)]
241 NUT04(#[from] crate::nuts::nut04::Error),
242 #[error(transparent)]
244 NUT05(#[from] crate::nuts::nut05::Error),
245 #[error(transparent)]
247 NUT11(#[from] crate::nuts::nut11::Error),
248 #[error(transparent)]
250 NUT12(#[from] crate::nuts::nut12::Error),
251 #[error(transparent)]
253 #[cfg(feature = "wallet")]
254 NUT13(#[from] crate::nuts::nut13::Error),
255 #[error(transparent)]
257 NUT14(#[from] crate::nuts::nut14::Error),
258 #[error(transparent)]
260 NUT18(#[from] crate::nuts::nut18::Error),
261 #[error(transparent)]
263 NUT20(#[from] crate::nuts::nut20::Error),
264 #[error(transparent)]
266 Database(#[from] crate::database::Error),
267 #[error(transparent)]
269 #[cfg(feature = "mint")]
270 Lightning(#[from] crate::lightning::Error),
271}
272
273#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
277#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
278pub struct ErrorResponse {
279 pub code: ErrorCode,
281 pub error: Option<String>,
283 pub detail: Option<String>,
285}
286
287impl fmt::Display for ErrorResponse {
288 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289 write!(
290 f,
291 "code: {}, error: {}, detail: {}",
292 self.code,
293 self.error.clone().unwrap_or_default(),
294 self.detail.clone().unwrap_or_default()
295 )
296 }
297}
298
299impl ErrorResponse {
300 pub fn new(code: ErrorCode, error: Option<String>, detail: Option<String>) -> Self {
302 Self {
303 code,
304 error,
305 detail,
306 }
307 }
308
309 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
311 let value: Value = serde_json::from_str(json)?;
312
313 Self::from_value(value)
314 }
315
316 pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
318 match serde_json::from_value::<ErrorResponse>(value.clone()) {
319 Ok(res) => Ok(res),
320 Err(_) => Ok(Self {
321 code: ErrorCode::Unknown(999),
322 error: Some(value.to_string()),
323 detail: None,
324 }),
325 }
326 }
327}
328
329impl From<Error> for ErrorResponse {
330 fn from(err: Error) -> ErrorResponse {
331 match err {
332 Error::TokenAlreadySpent => ErrorResponse {
333 code: ErrorCode::TokenAlreadySpent,
334 error: Some(err.to_string()),
335 detail: None,
336 },
337 Error::UnsupportedUnit => ErrorResponse {
338 code: ErrorCode::UnsupportedUnit,
339 error: Some(err.to_string()),
340 detail: None,
341 },
342 Error::PaymentFailed => ErrorResponse {
343 code: ErrorCode::LightningError,
344 error: Some(err.to_string()),
345 detail: None,
346 },
347 Error::RequestAlreadyPaid => ErrorResponse {
348 code: ErrorCode::InvoiceAlreadyPaid,
349 error: Some("Invoice already paid.".to_string()),
350 detail: None,
351 },
352 Error::TransactionUnbalanced(inputs_total, outputs_total, fee_expected) => {
353 ErrorResponse {
354 code: ErrorCode::TransactionUnbalanced,
355 error: Some(format!(
356 "Inputs: {}, Outputs: {}, expected_fee: {}",
357 inputs_total, outputs_total, fee_expected,
358 )),
359 detail: Some("Transaction inputs should equal outputs less fee".to_string()),
360 }
361 }
362 Error::MintingDisabled => ErrorResponse {
363 code: ErrorCode::MintingDisabled,
364 error: Some(err.to_string()),
365 detail: None,
366 },
367 Error::BlindedMessageAlreadySigned => ErrorResponse {
368 code: ErrorCode::BlindedMessageAlreadySigned,
369 error: Some(err.to_string()),
370 detail: None,
371 },
372 Error::InsufficientFunds => ErrorResponse {
373 code: ErrorCode::TransactionUnbalanced,
374 error: Some(err.to_string()),
375 detail: None,
376 },
377 Error::AmountOutofLimitRange(_min, _max, _amount) => ErrorResponse {
378 code: ErrorCode::AmountOutofLimitRange,
379 error: Some(err.to_string()),
380 detail: None,
381 },
382 Error::ExpiredQuote(_, _) => ErrorResponse {
383 code: ErrorCode::QuoteExpired,
384 error: Some(err.to_string()),
385 detail: None,
386 },
387 Error::PendingQuote => ErrorResponse {
388 code: ErrorCode::QuotePending,
389 error: Some(err.to_string()),
390 detail: None,
391 },
392 Error::TokenPending => ErrorResponse {
393 code: ErrorCode::TokenPending,
394 error: Some(err.to_string()),
395 detail: None,
396 },
397 Error::NUT20(err) => ErrorResponse {
398 code: ErrorCode::WitnessMissingOrInvalid,
399 error: Some(err.to_string()),
400 detail: None,
401 },
402 Error::DuplicateInputs => ErrorResponse {
403 code: ErrorCode::DuplicateInputs,
404 error: Some(err.to_string()),
405 detail: None,
406 },
407 Error::DuplicateOutputs => ErrorResponse {
408 code: ErrorCode::DuplicateOutputs,
409 error: Some(err.to_string()),
410 detail: None,
411 },
412 Error::MultipleUnits => ErrorResponse {
413 code: ErrorCode::MultipleUnits,
414 error: Some(err.to_string()),
415 detail: None,
416 },
417 Error::UnitMismatch => ErrorResponse {
418 code: ErrorCode::UnitMismatch,
419 error: Some(err.to_string()),
420 detail: None,
421 },
422 _ => ErrorResponse {
423 code: ErrorCode::Unknown(9999),
424 error: Some(err.to_string()),
425 detail: None,
426 },
427 }
428 }
429}
430
431impl From<ErrorResponse> for Error {
432 fn from(err: ErrorResponse) -> Error {
433 match err.code {
434 ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
435 ErrorCode::QuoteNotPaid => Self::UnpaidQuote,
436 ErrorCode::QuotePending => Self::PendingQuote,
437 ErrorCode::QuoteExpired => Self::ExpiredQuote(0, 0),
438 ErrorCode::KeysetNotFound => Self::UnknownKeySet,
439 ErrorCode::KeysetInactive => Self::InactiveKeyset,
440 ErrorCode::BlindedMessageAlreadySigned => Self::BlindedMessageAlreadySigned,
441 ErrorCode::UnsupportedUnit => Self::UnsupportedUnit,
442 ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced(0, 0, 0),
443 ErrorCode::MintingDisabled => Self::MintingDisabled,
444 ErrorCode::InvoiceAlreadyPaid => Self::RequestAlreadyPaid,
445 ErrorCode::TokenNotVerified => Self::DHKE(crate::dhke::Error::TokenNotVerified),
446 ErrorCode::LightningError => Self::PaymentFailed,
447 ErrorCode::AmountOutofLimitRange => {
448 Self::AmountOutofLimitRange(Amount::default(), Amount::default(), Amount::default())
449 }
450 ErrorCode::TokenPending => Self::TokenPending,
451 ErrorCode::WitnessMissingOrInvalid => Self::SignatureMissingOrInvalid,
452 ErrorCode::DuplicateInputs => Self::DuplicateInputs,
453 ErrorCode::DuplicateOutputs => Self::DuplicateOutputs,
454 ErrorCode::MultipleUnits => Self::MultipleUnits,
455 ErrorCode::UnitMismatch => Self::UnitMismatch,
456 _ => Self::UnknownErrorResponse(err.to_string()),
457 }
458 }
459}
460
461#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
463#[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
464pub enum ErrorCode {
465 TokenAlreadySpent,
467 TokenPending,
469 QuoteNotPaid,
471 QuoteExpired,
473 QuotePending,
475 KeysetNotFound,
477 KeysetInactive,
479 BlindedMessageAlreadySigned,
481 UnsupportedUnit,
483 TokensAlreadyIssued,
485 MintingDisabled,
487 InvoiceAlreadyPaid,
489 TokenNotVerified,
491 LightningError,
493 TransactionUnbalanced,
495 AmountOutofLimitRange,
497 WitnessMissingOrInvalid,
499 DuplicateInputs,
501 DuplicateOutputs,
503 MultipleUnits,
505 UnitMismatch,
507 Unknown(u16),
509}
510
511impl ErrorCode {
512 pub fn from_code(code: u16) -> Self {
514 match code {
515 10002 => Self::BlindedMessageAlreadySigned,
516 10003 => Self::TokenNotVerified,
517 11001 => Self::TokenAlreadySpent,
518 11002 => Self::TransactionUnbalanced,
519 11005 => Self::UnsupportedUnit,
520 11006 => Self::AmountOutofLimitRange,
521 11007 => Self::DuplicateInputs,
522 11008 => Self::DuplicateOutputs,
523 11009 => Self::MultipleUnits,
524 11010 => Self::UnitMismatch,
525 11012 => Self::TokenPending,
526 12001 => Self::KeysetNotFound,
527 12002 => Self::KeysetInactive,
528 20000 => Self::LightningError,
529 20001 => Self::QuoteNotPaid,
530 20002 => Self::TokensAlreadyIssued,
531 20003 => Self::MintingDisabled,
532 20005 => Self::QuotePending,
533 20006 => Self::InvoiceAlreadyPaid,
534 20007 => Self::QuoteExpired,
535 20008 => Self::WitnessMissingOrInvalid,
536 _ => Self::Unknown(code),
537 }
538 }
539
540 pub fn to_code(&self) -> u16 {
542 match self {
543 Self::BlindedMessageAlreadySigned => 10002,
544 Self::TokenNotVerified => 10003,
545 Self::TokenAlreadySpent => 11001,
546 Self::TransactionUnbalanced => 11002,
547 Self::UnsupportedUnit => 11005,
548 Self::AmountOutofLimitRange => 11006,
549 Self::DuplicateInputs => 11007,
550 Self::DuplicateOutputs => 11008,
551 Self::MultipleUnits => 11009,
552 Self::UnitMismatch => 11010,
553 Self::TokenPending => 11012,
554 Self::KeysetNotFound => 12001,
555 Self::KeysetInactive => 12002,
556 Self::LightningError => 20000,
557 Self::QuoteNotPaid => 20001,
558 Self::TokensAlreadyIssued => 20002,
559 Self::MintingDisabled => 20003,
560 Self::QuotePending => 20005,
561 Self::InvoiceAlreadyPaid => 20006,
562 Self::QuoteExpired => 20007,
563 Self::WitnessMissingOrInvalid => 20008,
564 Self::Unknown(code) => *code,
565 }
566 }
567}
568
569impl Serialize for ErrorCode {
570 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
571 where
572 S: Serializer,
573 {
574 serializer.serialize_u16(self.to_code())
575 }
576}
577
578impl<'de> Deserialize<'de> for ErrorCode {
579 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
580 where
581 D: Deserializer<'de>,
582 {
583 let code = u16::deserialize(deserializer)?;
584
585 Ok(ErrorCode::from_code(code))
586 }
587}
588
589impl fmt::Display for ErrorCode {
590 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591 write!(f, "{}", self.to_code())
592 }
593}