error.rs 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238
  1. //! Errors
  2. use std::array::TryFromSliceError;
  3. use std::fmt;
  4. use cashu::{CurrencyUnit, PaymentMethod};
  5. use serde::{Deserialize, Deserializer, Serialize, Serializer};
  6. use serde_json::Value;
  7. use thiserror::Error;
  8. use crate::nuts::Id;
  9. use crate::util::hex;
  10. #[cfg(feature = "wallet")]
  11. use crate::wallet::WalletKey;
  12. use crate::Amount;
  13. /// CDK Error
  14. #[derive(Debug, Error)]
  15. pub enum Error {
  16. /// Mint does not have a key for amount
  17. #[error("No Key for Amount")]
  18. AmountKey,
  19. /// Keyset is not known
  20. #[error("Keyset id not known: `{0}`")]
  21. KeysetUnknown(Id),
  22. /// Unsupported unit
  23. #[error("Unit unsupported")]
  24. UnsupportedUnit,
  25. /// Payment failed
  26. #[error("Payment failed")]
  27. PaymentFailed,
  28. /// Payment pending
  29. #[error("Payment pending")]
  30. PaymentPending,
  31. /// Invoice already paid
  32. #[error("Request already paid")]
  33. RequestAlreadyPaid,
  34. /// Invalid payment request
  35. #[error("Invalid payment request")]
  36. InvalidPaymentRequest,
  37. /// Bolt11 invoice does not have amount
  38. #[error("Invoice Amount undefined")]
  39. InvoiceAmountUndefined,
  40. /// Split Values must be less then or equal to amount
  41. #[error("Split Values must be less then or equal to amount")]
  42. SplitValuesGreater,
  43. /// Amount overflow
  44. #[error("Amount Overflow")]
  45. AmountOverflow,
  46. /// Over issue - tried to issue more than paid
  47. #[error("Cannot issue more than amount paid")]
  48. OverIssue,
  49. /// Witness missing or invalid
  50. #[error("Signature missing or invalid")]
  51. SignatureMissingOrInvalid,
  52. /// Amountless Invoice Not supported
  53. #[error("Amount Less Invoice is not allowed")]
  54. AmountLessNotAllowed,
  55. /// Multi-Part Internal Melt Quotes are not supported
  56. #[error("Multi-Part Internal Melt Quotes are not supported")]
  57. InternalMultiPartMeltQuote,
  58. /// Multi-Part Payment not supported for unit and method
  59. #[error("Multi-Part payment is not supported for unit `{0}` and method `{1}`")]
  60. MppUnitMethodNotSupported(CurrencyUnit, PaymentMethod),
  61. /// Clear Auth Required
  62. #[error("Clear Auth Required")]
  63. ClearAuthRequired,
  64. /// Blind Auth Required
  65. #[error("Blind Auth Required")]
  66. BlindAuthRequired,
  67. /// Clear Auth Failed
  68. #[error("Clear Auth Failed")]
  69. ClearAuthFailed,
  70. /// Blind Auth Failed
  71. #[error("Blind Auth Failed")]
  72. BlindAuthFailed,
  73. /// Auth settings undefined
  74. #[error("Auth settings undefined")]
  75. AuthSettingsUndefined,
  76. /// Mint time outside of tolerance
  77. #[error("Mint time outside of tolerance")]
  78. MintTimeExceedsTolerance,
  79. /// Insufficient blind auth tokens
  80. #[error("Insufficient blind auth tokens, must reauth")]
  81. InsufficientBlindAuthTokens,
  82. /// Auth localstore undefined
  83. #[error("Auth localstore undefined")]
  84. AuthLocalstoreUndefined,
  85. /// Wallet cat not set
  86. #[error("Wallet cat not set")]
  87. CatNotSet,
  88. /// Could not get mint info
  89. #[error("Could not get mint info")]
  90. CouldNotGetMintInfo,
  91. /// Multi-Part Payment not supported for unit and method
  92. #[error("Amountless invoices are not supported for unit `{0}` and method `{1}`")]
  93. AmountlessInvoiceNotSupported(CurrencyUnit, PaymentMethod),
  94. /// Duplicate Payment id
  95. #[error("Payment id seen for mint")]
  96. DuplicatePaymentId,
  97. /// Pubkey required
  98. #[error("Pubkey required")]
  99. PubkeyRequired,
  100. /// Invalid payment method
  101. #[error("Invalid payment method")]
  102. InvalidPaymentMethod,
  103. /// Amount undefined
  104. #[error("Amount undefined")]
  105. AmountUndefined,
  106. /// Unsupported payment method
  107. #[error("Payment method unsupported")]
  108. UnsupportedPaymentMethod,
  109. /// Payment method required
  110. #[error("Payment method required")]
  111. PaymentMethodRequired,
  112. /// Could not parse bolt12
  113. #[error("Could not parse bolt12")]
  114. Bolt12parse,
  115. /// Could not parse invoice (bolt11 or bolt12)
  116. #[error("Could not parse invoice")]
  117. InvalidInvoice,
  118. /// BIP353 address parsing error
  119. #[error("Failed to parse BIP353 address: {0}")]
  120. Bip353Parse(String),
  121. /// Operation timeout
  122. #[error("Operation timeout")]
  123. Timeout,
  124. /// BIP353 address resolution error
  125. #[error("Failed to resolve BIP353 address: {0}")]
  126. Bip353Resolve(String),
  127. /// BIP353 no Lightning offer found
  128. #[error("No Lightning offer found in BIP353 payment instructions")]
  129. Bip353NoLightningOffer,
  130. /// Lightning Address parsing error
  131. #[error("Failed to parse Lightning address: {0}")]
  132. LightningAddressParse(String),
  133. /// Lightning Address request error
  134. #[error("Failed to request invoice from Lightning address service: {0}")]
  135. LightningAddressRequest(String),
  136. /// Internal Error - Send error
  137. #[error("Internal send error: {0}")]
  138. SendError(String),
  139. /// Internal Error - Recv error
  140. #[error("Internal receive error: {0}")]
  141. RecvError(String),
  142. // Mint Errors
  143. /// Minting is disabled
  144. #[error("Minting is disabled")]
  145. MintingDisabled,
  146. /// Quote is not known
  147. #[error("Unknown quote")]
  148. UnknownQuote,
  149. /// Quote is expired
  150. #[error("Expired quote: Expired: `{0}`, Time: `{1}`")]
  151. ExpiredQuote(u64, u64),
  152. /// Amount is outside of allowed range
  153. #[error("Amount must be between `{0}` and `{1}` is `{2}`")]
  154. AmountOutofLimitRange(Amount, Amount, Amount),
  155. /// Quote is not paid
  156. #[error("Quote not paid")]
  157. UnpaidQuote,
  158. /// Quote is pending
  159. #[error("Quote pending")]
  160. PendingQuote,
  161. /// ecash already issued for quote
  162. #[error("Quote already issued")]
  163. IssuedQuote,
  164. /// Quote has already been paid
  165. #[error("Quote is already paid")]
  166. PaidQuote,
  167. /// Payment state is unknown
  168. #[error("Payment state is unknown")]
  169. UnknownPaymentState,
  170. /// Melting is disabled
  171. #[error("Melting is disabled")]
  172. MeltingDisabled,
  173. /// Unknown Keyset
  174. #[error("Unknown Keyset")]
  175. UnknownKeySet,
  176. /// BlindedMessage is already signed
  177. #[error("Blinded Message is already signed")]
  178. BlindedMessageAlreadySigned,
  179. /// Inactive Keyset
  180. #[error("Inactive Keyset")]
  181. InactiveKeyset,
  182. /// Transaction unbalanced
  183. #[error("Inputs: `{0}`, Outputs: `{1}`, Expected Fee: `{2}`")]
  184. TransactionUnbalanced(u64, u64, u64),
  185. /// Duplicate proofs provided
  186. #[error("Duplicate Inputs")]
  187. DuplicateInputs,
  188. /// Duplicate output
  189. #[error("Duplicate outputs")]
  190. DuplicateOutputs,
  191. /// Maximum number of inputs exceeded
  192. #[error("Maximum inputs exceeded: {actual} provided, max {max}")]
  193. MaxInputsExceeded {
  194. /// Actual number of inputs provided
  195. actual: usize,
  196. /// Maximum allowed inputs
  197. max: usize,
  198. },
  199. /// Maximum number of outputs exceeded
  200. #[error("Maximum outputs exceeded: {actual} provided, max {max}")]
  201. MaxOutputsExceeded {
  202. /// Actual number of outputs provided
  203. actual: usize,
  204. /// Maximum allowed outputs
  205. max: usize,
  206. },
  207. /// Multiple units provided
  208. #[error("Cannot have multiple units")]
  209. MultipleUnits,
  210. /// Unit mismatch
  211. #[error("Input unit must match output")]
  212. UnitMismatch,
  213. /// Sig all cannot be used in melt
  214. #[error("Sig all cannot be used in melt")]
  215. SigAllUsedInMelt,
  216. /// Token is already spent
  217. #[error("Token Already Spent")]
  218. TokenAlreadySpent,
  219. /// Token is already pending
  220. #[error("Token Pending")]
  221. TokenPending,
  222. /// Internal Error
  223. #[error("Internal Error")]
  224. Internal,
  225. /// Oidc config not set
  226. #[error("Oidc client not set")]
  227. OidcNotSet,
  228. // Wallet Errors
  229. /// P2PK spending conditions not met
  230. #[error("P2PK condition not met `{0}`")]
  231. P2PKConditionsNotMet(String),
  232. /// Duplicate signature from same pubkey in P2PK
  233. #[error("Duplicate signature from same pubkey in P2PK")]
  234. DuplicateSignatureError,
  235. /// Spending Locktime not provided
  236. #[error("Spending condition locktime not provided")]
  237. LocktimeNotProvided,
  238. /// Invalid Spending Conditions
  239. #[error("Invalid spending conditions: `{0}`")]
  240. InvalidSpendConditions(String),
  241. /// Incorrect Wallet
  242. #[error("Incorrect wallet: `{0}`")]
  243. IncorrectWallet(String),
  244. /// Unknown Wallet
  245. #[error("Unknown wallet: `{0}`")]
  246. #[cfg(feature = "wallet")]
  247. UnknownWallet(WalletKey),
  248. /// Max Fee Ecxeded
  249. #[error("Max fee exceeded")]
  250. MaxFeeExceeded,
  251. /// Url path segments could not be joined
  252. #[error("Url path segments could not be joined")]
  253. UrlPathSegments,
  254. /// Unknown error response
  255. #[error("Unknown error response: `{0}`")]
  256. UnknownErrorResponse(String),
  257. /// Invalid DLEQ proof
  258. #[error("Could not verify DLEQ proof")]
  259. CouldNotVerifyDleq,
  260. /// Dleq Proof not provided for signature
  261. #[error("Dleq proof not provided for signature")]
  262. DleqProofNotProvided,
  263. /// Incorrect Mint
  264. /// Token does not match wallet mint
  265. #[error("Token does not match wallet mint")]
  266. IncorrectMint,
  267. /// Receive can only be used with tokens from single mint
  268. #[error("Multiple mint tokens not supported by receive. Please deconstruct the token and use receive with_proof")]
  269. MultiMintTokenNotSupported,
  270. /// Preimage not provided
  271. #[error("Preimage not provided")]
  272. PreimageNotProvided,
  273. /// Unknown mint
  274. #[error("Unknown mint: {mint_url}")]
  275. UnknownMint {
  276. /// URL of the unknown mint
  277. mint_url: String,
  278. },
  279. /// Transfer between mints timed out
  280. #[error("Transfer timeout: failed to transfer {amount} from {source_mint} to {target_mint}")]
  281. TransferTimeout {
  282. /// Source mint URL
  283. source_mint: String,
  284. /// Target mint URL
  285. target_mint: String,
  286. /// Amount that failed to transfer
  287. amount: Amount,
  288. },
  289. /// Insufficient Funds
  290. #[error("Insufficient funds")]
  291. InsufficientFunds,
  292. /// Unexpected proof state
  293. #[error("Unexpected proof state")]
  294. UnexpectedProofState,
  295. /// No active keyset
  296. #[error("No active keyset")]
  297. NoActiveKeyset,
  298. /// Incorrect quote amount
  299. #[error("Incorrect quote amount")]
  300. IncorrectQuoteAmount,
  301. /// Invoice Description not supported
  302. #[error("Invoice Description not supported")]
  303. InvoiceDescriptionUnsupported,
  304. /// Invalid transaction direction
  305. #[error("Invalid transaction direction")]
  306. InvalidTransactionDirection,
  307. /// Invalid transaction id
  308. #[error("Invalid transaction id")]
  309. InvalidTransactionId,
  310. /// Transaction not found
  311. #[error("Transaction not found")]
  312. TransactionNotFound,
  313. /// Invalid operation kind
  314. #[error("Invalid operation kind")]
  315. InvalidOperationKind,
  316. /// Invalid operation state
  317. #[error("Invalid operation state")]
  318. InvalidOperationState,
  319. /// Operation not found
  320. #[error("Operation not found")]
  321. OperationNotFound,
  322. /// KV Store invalid key or namespace
  323. #[error("Invalid KV store key or namespace: {0}")]
  324. KVStoreInvalidKey(String),
  325. /// Concurrent update detected
  326. #[error("Concurrent update detected")]
  327. ConcurrentUpdate,
  328. /// Invalid response from mint
  329. #[error("Invalid mint response: {0}")]
  330. InvalidMintResponse(String),
  331. /// Subscription error
  332. #[error("Subscription error: {0}")]
  333. SubscriptionError(String),
  334. /// Custom Error
  335. #[error("`{0}`")]
  336. Custom(String),
  337. // External Error conversions
  338. /// Parse invoice error
  339. #[error(transparent)]
  340. Invoice(#[from] lightning_invoice::ParseOrSemanticError),
  341. /// Bip32 error
  342. #[error(transparent)]
  343. Bip32(#[from] bitcoin::bip32::Error),
  344. /// Parse int error
  345. #[error(transparent)]
  346. ParseInt(#[from] std::num::ParseIntError),
  347. /// Parse 9rl Error
  348. #[error(transparent)]
  349. UrlParseError(#[from] url::ParseError),
  350. /// Utf8 parse error
  351. #[error(transparent)]
  352. Utf8ParseError(#[from] std::string::FromUtf8Error),
  353. /// Serde Json error
  354. #[error(transparent)]
  355. SerdeJsonError(#[from] serde_json::Error),
  356. /// Base64 error
  357. #[error(transparent)]
  358. Base64Error(#[from] bitcoin::base64::DecodeError),
  359. /// From hex error
  360. #[error(transparent)]
  361. HexError(#[from] hex::Error),
  362. /// Http transport error
  363. #[error("Http transport error {0:?}: {1}")]
  364. HttpError(Option<u16>, String),
  365. /// Parse invoice error
  366. #[cfg(feature = "mint")]
  367. #[error(transparent)]
  368. Uuid(#[from] uuid::Error),
  369. // Crate error conversions
  370. /// Cashu Url Error
  371. #[error(transparent)]
  372. CashuUrl(#[from] crate::mint_url::Error),
  373. /// Secret error
  374. #[error(transparent)]
  375. Secret(#[from] crate::secret::Error),
  376. /// Amount Error
  377. #[error(transparent)]
  378. AmountError(#[from] crate::amount::Error),
  379. /// DHKE Error
  380. #[error(transparent)]
  381. DHKE(#[from] crate::dhke::Error),
  382. /// NUT00 Error
  383. #[error(transparent)]
  384. NUT00(#[from] crate::nuts::nut00::Error),
  385. /// Nut01 error
  386. #[error(transparent)]
  387. NUT01(#[from] crate::nuts::nut01::Error),
  388. /// NUT02 error
  389. #[error(transparent)]
  390. NUT02(#[from] crate::nuts::nut02::Error),
  391. /// NUT03 error
  392. #[error(transparent)]
  393. NUT03(#[from] crate::nuts::nut03::Error),
  394. /// NUT04 error
  395. #[error(transparent)]
  396. NUT04(#[from] crate::nuts::nut04::Error),
  397. /// NUT05 error
  398. #[error(transparent)]
  399. NUT05(#[from] crate::nuts::nut05::Error),
  400. /// NUT11 Error
  401. #[error(transparent)]
  402. NUT11(#[from] crate::nuts::nut11::Error),
  403. /// NUT12 Error
  404. #[error(transparent)]
  405. NUT12(#[from] crate::nuts::nut12::Error),
  406. /// NUT13 Error
  407. #[error(transparent)]
  408. #[cfg(feature = "wallet")]
  409. NUT13(#[from] crate::nuts::nut13::Error),
  410. /// NUT14 Error
  411. #[error(transparent)]
  412. NUT14(#[from] crate::nuts::nut14::Error),
  413. /// NUT18 Error
  414. #[error(transparent)]
  415. NUT18(#[from] crate::nuts::nut18::Error),
  416. /// NUT20 Error
  417. #[error(transparent)]
  418. NUT20(#[from] crate::nuts::nut20::Error),
  419. /// NUT21 Error
  420. #[error(transparent)]
  421. NUT21(#[from] crate::nuts::nut21::Error),
  422. /// NUT22 Error
  423. #[error(transparent)]
  424. NUT22(#[from] crate::nuts::nut22::Error),
  425. /// NUT23 Error
  426. #[error(transparent)]
  427. NUT23(#[from] crate::nuts::nut23::Error),
  428. /// Quote ID Error
  429. #[error(transparent)]
  430. #[cfg(feature = "mint")]
  431. QuoteId(#[from] crate::quote_id::QuoteIdError),
  432. /// From slice error
  433. #[error(transparent)]
  434. TryFromSliceError(#[from] TryFromSliceError),
  435. /// Database Error
  436. #[error(transparent)]
  437. Database(crate::database::Error),
  438. /// Payment Error
  439. #[error(transparent)]
  440. #[cfg(feature = "mint")]
  441. Payment(#[from] crate::payment::Error),
  442. }
  443. #[cfg(test)]
  444. mod tests {
  445. use super::*;
  446. #[test]
  447. fn test_is_definitive_failure() {
  448. // Test definitive failures
  449. assert!(Error::AmountOverflow.is_definitive_failure());
  450. assert!(Error::TokenAlreadySpent.is_definitive_failure());
  451. assert!(Error::MintingDisabled.is_definitive_failure());
  452. // Test HTTP client errors (4xx) - simulated
  453. assert!(Error::HttpError(Some(400), "Bad Request".to_string()).is_definitive_failure());
  454. assert!(Error::HttpError(Some(404), "Not Found".to_string()).is_definitive_failure());
  455. assert!(
  456. Error::HttpError(Some(429), "Too Many Requests".to_string()).is_definitive_failure()
  457. );
  458. // Test ambiguous failures
  459. assert!(!Error::Timeout.is_definitive_failure());
  460. assert!(!Error::Internal.is_definitive_failure());
  461. assert!(!Error::ConcurrentUpdate.is_definitive_failure());
  462. // Test HTTP server errors (5xx)
  463. assert!(
  464. !Error::HttpError(Some(500), "Internal Server Error".to_string())
  465. .is_definitive_failure()
  466. );
  467. assert!(!Error::HttpError(Some(502), "Bad Gateway".to_string()).is_definitive_failure());
  468. assert!(
  469. !Error::HttpError(Some(503), "Service Unavailable".to_string()).is_definitive_failure()
  470. );
  471. // Test HTTP network errors (no status)
  472. assert!(!Error::HttpError(None, "Connection refused".to_string()).is_definitive_failure());
  473. }
  474. }
  475. impl Error {
  476. /// Check if the error is a definitive failure
  477. ///
  478. /// A definitive failure means the mint definitely rejected the request
  479. /// and did not update its state. In these cases, it is safe to revert
  480. /// the transaction locally.
  481. ///
  482. /// If false, the failure is ambiguous (e.g. timeout, network error, 500)
  483. /// and the transaction state at the mint is unknown.
  484. pub fn is_definitive_failure(&self) -> bool {
  485. match self {
  486. // Logic/Validation Errors (Safe to revert)
  487. Self::AmountKey
  488. | Self::KeysetUnknown(_)
  489. | Self::UnsupportedUnit
  490. | Self::InvoiceAmountUndefined
  491. | Self::SplitValuesGreater
  492. | Self::AmountOverflow
  493. | Self::OverIssue
  494. | Self::SignatureMissingOrInvalid
  495. | Self::AmountLessNotAllowed
  496. | Self::InternalMultiPartMeltQuote
  497. | Self::MppUnitMethodNotSupported(_, _)
  498. | Self::AmountlessInvoiceNotSupported(_, _)
  499. | Self::DuplicatePaymentId
  500. | Self::PubkeyRequired
  501. | Self::InvalidPaymentMethod
  502. | Self::UnsupportedPaymentMethod
  503. | Self::InvalidInvoice
  504. | Self::MintingDisabled
  505. | Self::UnknownQuote
  506. | Self::ExpiredQuote(_, _)
  507. | Self::AmountOutofLimitRange(_, _, _)
  508. | Self::UnpaidQuote
  509. | Self::PendingQuote
  510. | Self::IssuedQuote
  511. | Self::PaidQuote
  512. | Self::MeltingDisabled
  513. | Self::UnknownKeySet
  514. | Self::BlindedMessageAlreadySigned
  515. | Self::InactiveKeyset
  516. | Self::TransactionUnbalanced(_, _, _)
  517. | Self::DuplicateInputs
  518. | Self::DuplicateOutputs
  519. | Self::MultipleUnits
  520. | Self::UnitMismatch
  521. | Self::SigAllUsedInMelt
  522. | Self::TokenAlreadySpent
  523. | Self::TokenPending
  524. | Self::P2PKConditionsNotMet(_)
  525. | Self::DuplicateSignatureError
  526. | Self::LocktimeNotProvided
  527. | Self::InvalidSpendConditions(_)
  528. | Self::IncorrectWallet(_)
  529. | Self::MaxFeeExceeded
  530. | Self::DleqProofNotProvided
  531. | Self::IncorrectMint
  532. | Self::MultiMintTokenNotSupported
  533. | Self::PreimageNotProvided
  534. | Self::UnknownMint { .. }
  535. | Self::UnexpectedProofState
  536. | Self::NoActiveKeyset
  537. | Self::IncorrectQuoteAmount
  538. | Self::InvoiceDescriptionUnsupported
  539. | Self::InvalidTransactionDirection
  540. | Self::InvalidTransactionId
  541. | Self::InvalidOperationKind
  542. | Self::InvalidOperationState
  543. | Self::OperationNotFound
  544. | Self::KVStoreInvalidKey(_) => true,
  545. // HTTP Errors
  546. Self::HttpError(Some(status), _) => {
  547. // Client errors (400-499) are definitive failures
  548. // Server errors (500-599) are ambiguous
  549. (400..500).contains(status)
  550. }
  551. // Ambiguous Errors (Unsafe to revert)
  552. Self::Timeout
  553. | Self::Internal
  554. | Self::UnknownPaymentState
  555. | Self::CouldNotGetMintInfo
  556. | Self::UnknownErrorResponse(_)
  557. | Self::InvalidMintResponse(_)
  558. | Self::ConcurrentUpdate
  559. | Self::SendError(_)
  560. | Self::RecvError(_)
  561. | Self::TransferTimeout { .. } => false,
  562. // Network/IO/Parsing Errors (Usually ambiguous as they could happen reading response)
  563. Self::HttpError(None, _) // No status code means network error
  564. | Self::SerdeJsonError(_) // Could be malformed success response
  565. | Self::Database(_)
  566. | Self::Custom(_) => false,
  567. // Auth Errors (Generally definitive if rejected)
  568. Self::ClearAuthRequired
  569. | Self::BlindAuthRequired
  570. | Self::ClearAuthFailed
  571. | Self::BlindAuthFailed
  572. | Self::InsufficientBlindAuthTokens
  573. | Self::AuthSettingsUndefined
  574. | Self::AuthLocalstoreUndefined
  575. | Self::OidcNotSet => true,
  576. // External conversions - check specifically
  577. Self::Invoice(_) => true, // Parsing error
  578. Self::Bip32(_) => true, // Key derivation error
  579. Self::ParseInt(_) => true,
  580. Self::UrlParseError(_) => true,
  581. Self::Utf8ParseError(_) => true,
  582. Self::Base64Error(_) => true,
  583. Self::HexError(_) => true,
  584. #[cfg(feature = "mint")]
  585. Self::Uuid(_) => true,
  586. Self::CashuUrl(_) => true,
  587. Self::Secret(_) => true,
  588. Self::AmountError(_) => true,
  589. Self::DHKE(_) => true, // Crypto errors
  590. Self::NUT00(_) => true,
  591. Self::NUT01(_) => true,
  592. Self::NUT02(_) => true,
  593. Self::NUT03(_) => true,
  594. Self::NUT04(_) => true,
  595. Self::NUT05(_) => true,
  596. Self::NUT11(_) => true,
  597. Self::NUT12(_) => true,
  598. #[cfg(feature = "wallet")]
  599. Self::NUT13(_) => true,
  600. Self::NUT14(_) => true,
  601. Self::NUT18(_) => true,
  602. Self::NUT20(_) => true,
  603. Self::NUT21(_) => true,
  604. Self::NUT22(_) => true,
  605. Self::NUT23(_) => true,
  606. #[cfg(feature = "mint")]
  607. Self::QuoteId(_) => true,
  608. Self::TryFromSliceError(_) => true,
  609. #[cfg(feature = "mint")]
  610. Self::Payment(_) => false, // Payment errors could be ambiguous? assume ambiguous to be safe
  611. // Catch-all
  612. _ => false,
  613. }
  614. }
  615. }
  616. /// CDK Error Response
  617. ///
  618. /// See NUT definition in [00](https://github.com/cashubtc/nuts/blob/main/00.md)
  619. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
  620. #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
  621. pub struct ErrorResponse {
  622. /// Error Code
  623. pub code: ErrorCode,
  624. /// Human readable description
  625. #[serde(default)]
  626. pub detail: String,
  627. }
  628. impl fmt::Display for ErrorResponse {
  629. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  630. write!(f, "code: {}, detail: {}", self.code, self.detail)
  631. }
  632. }
  633. impl ErrorResponse {
  634. /// Create new [`ErrorResponse`]
  635. pub fn new(code: ErrorCode, detail: String) -> Self {
  636. Self { code, detail }
  637. }
  638. /// Error response from json
  639. pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
  640. let value: Value = serde_json::from_str(json)?;
  641. Self::from_value(value)
  642. }
  643. /// Error response from json Value
  644. pub fn from_value(value: Value) -> Result<Self, serde_json::Error> {
  645. match serde_json::from_value::<ErrorResponse>(value.clone()) {
  646. Ok(res) => Ok(res),
  647. Err(_) => Ok(Self {
  648. code: ErrorCode::Unknown(999),
  649. detail: value.to_string(),
  650. }),
  651. }
  652. }
  653. }
  654. /// Maps NUT11 errors to appropriate error codes
  655. /// All NUT11 errors are witness/signature related, so they map to WitnessMissingOrInvalid (20008)
  656. fn map_nut11_error(_nut11_error: &crate::nuts::nut11::Error) -> ErrorCode {
  657. // All NUT11 errors relate to P2PK/witness validation, which maps to 20008
  658. ErrorCode::WitnessMissingOrInvalid
  659. }
  660. impl From<Error> for ErrorResponse {
  661. fn from(err: Error) -> ErrorResponse {
  662. match err {
  663. Error::TokenAlreadySpent => ErrorResponse {
  664. code: ErrorCode::TokenAlreadySpent,
  665. detail: err.to_string(),
  666. },
  667. Error::UnsupportedUnit => ErrorResponse {
  668. code: ErrorCode::UnsupportedUnit,
  669. detail: err.to_string(),
  670. },
  671. Error::PaymentFailed => ErrorResponse {
  672. code: ErrorCode::LightningError,
  673. detail: err.to_string(),
  674. },
  675. Error::RequestAlreadyPaid => ErrorResponse {
  676. code: ErrorCode::InvoiceAlreadyPaid,
  677. detail: "Invoice already paid.".to_string(),
  678. },
  679. Error::TransactionUnbalanced(inputs_total, outputs_total, fee_expected) => {
  680. ErrorResponse {
  681. code: ErrorCode::TransactionUnbalanced,
  682. detail: format!(
  683. "Inputs: {inputs_total}, Outputs: {outputs_total}, expected_fee: {fee_expected}. Transaction inputs should equal outputs less fee"
  684. ),
  685. }
  686. }
  687. Error::MintingDisabled => ErrorResponse {
  688. code: ErrorCode::MintingDisabled,
  689. detail: err.to_string(),
  690. },
  691. Error::BlindedMessageAlreadySigned => ErrorResponse {
  692. code: ErrorCode::BlindedMessageAlreadySigned,
  693. detail: err.to_string(),
  694. },
  695. Error::InsufficientFunds => ErrorResponse {
  696. code: ErrorCode::TransactionUnbalanced,
  697. detail: err.to_string(),
  698. },
  699. Error::AmountOutofLimitRange(_min, _max, _amount) => ErrorResponse {
  700. code: ErrorCode::AmountOutofLimitRange,
  701. detail: err.to_string(),
  702. },
  703. Error::ExpiredQuote(_, _) => ErrorResponse {
  704. code: ErrorCode::QuoteExpired,
  705. detail: err.to_string(),
  706. },
  707. Error::PendingQuote => ErrorResponse {
  708. code: ErrorCode::QuotePending,
  709. detail: err.to_string(),
  710. },
  711. Error::TokenPending => ErrorResponse {
  712. code: ErrorCode::TokenPending,
  713. detail: err.to_string(),
  714. },
  715. Error::ClearAuthRequired => ErrorResponse {
  716. code: ErrorCode::ClearAuthRequired,
  717. detail: Error::ClearAuthRequired.to_string(),
  718. },
  719. Error::ClearAuthFailed => ErrorResponse {
  720. code: ErrorCode::ClearAuthFailed,
  721. detail: Error::ClearAuthFailed.to_string(),
  722. },
  723. Error::BlindAuthRequired => ErrorResponse {
  724. code: ErrorCode::BlindAuthRequired,
  725. detail: Error::BlindAuthRequired.to_string(),
  726. },
  727. Error::BlindAuthFailed => ErrorResponse {
  728. code: ErrorCode::BlindAuthFailed,
  729. detail: Error::BlindAuthFailed.to_string(),
  730. },
  731. Error::NUT20(err) => ErrorResponse {
  732. code: ErrorCode::WitnessMissingOrInvalid,
  733. detail: err.to_string(),
  734. },
  735. Error::DuplicateInputs => ErrorResponse {
  736. code: ErrorCode::DuplicateInputs,
  737. detail: err.to_string(),
  738. },
  739. Error::DuplicateOutputs => ErrorResponse {
  740. code: ErrorCode::DuplicateOutputs,
  741. detail: err.to_string(),
  742. },
  743. Error::MultipleUnits => ErrorResponse {
  744. code: ErrorCode::MultipleUnits,
  745. detail: err.to_string(),
  746. },
  747. Error::UnitMismatch => ErrorResponse {
  748. code: ErrorCode::UnitMismatch,
  749. detail: err.to_string(),
  750. },
  751. Error::UnpaidQuote => ErrorResponse {
  752. code: ErrorCode::QuoteNotPaid,
  753. detail: Error::UnpaidQuote.to_string(),
  754. },
  755. Error::NUT11(err) => {
  756. let code = map_nut11_error(&err);
  757. let extra = if matches!(err, crate::nuts::nut11::Error::SignaturesNotProvided) {
  758. Some("P2PK signatures are required but not provided".to_string())
  759. } else {
  760. None
  761. };
  762. ErrorResponse {
  763. code,
  764. detail: match extra {
  765. Some(extra) => format!("{err}. {extra}"),
  766. None => err.to_string(),
  767. },
  768. }
  769. },
  770. Error::DuplicateSignatureError => ErrorResponse {
  771. code: ErrorCode::WitnessMissingOrInvalid,
  772. detail: err.to_string(),
  773. },
  774. Error::IssuedQuote => ErrorResponse {
  775. code: ErrorCode::TokensAlreadyIssued,
  776. detail: err.to_string(),
  777. },
  778. Error::UnknownKeySet => ErrorResponse {
  779. code: ErrorCode::KeysetNotFound,
  780. detail: err.to_string(),
  781. },
  782. Error::InactiveKeyset => ErrorResponse {
  783. code: ErrorCode::KeysetInactive,
  784. detail: err.to_string(),
  785. },
  786. Error::AmountLessNotAllowed => ErrorResponse {
  787. code: ErrorCode::AmountlessInvoiceNotSupported,
  788. detail: err.to_string(),
  789. },
  790. Error::IncorrectQuoteAmount => ErrorResponse {
  791. code: ErrorCode::IncorrectQuoteAmount,
  792. detail: err.to_string(),
  793. },
  794. Error::PubkeyRequired => ErrorResponse {
  795. code: ErrorCode::PubkeyRequired,
  796. detail: err.to_string(),
  797. },
  798. Error::PaidQuote => ErrorResponse {
  799. code: ErrorCode::InvoiceAlreadyPaid,
  800. detail: err.to_string(),
  801. },
  802. Error::DuplicatePaymentId => ErrorResponse {
  803. code: ErrorCode::InvoiceAlreadyPaid,
  804. detail: err.to_string(),
  805. },
  806. // Database duplicate error indicates another quote with same invoice is already pending/paid
  807. Error::Database(crate::database::Error::Duplicate) => ErrorResponse {
  808. code: ErrorCode::InvoiceAlreadyPaid,
  809. detail: "Invoice already paid or pending".to_string(),
  810. },
  811. // DHKE errors - TokenNotVerified for actual verification failures
  812. Error::DHKE(crate::dhke::Error::TokenNotVerified) => ErrorResponse {
  813. code: ErrorCode::TokenNotVerified,
  814. detail: err.to_string(),
  815. },
  816. Error::DHKE(_) => ErrorResponse {
  817. code: ErrorCode::Unknown(50000),
  818. detail: err.to_string(),
  819. },
  820. // Verification errors
  821. Error::CouldNotVerifyDleq => ErrorResponse {
  822. code: ErrorCode::TokenNotVerified,
  823. detail: err.to_string(),
  824. },
  825. Error::SignatureMissingOrInvalid => ErrorResponse {
  826. code: ErrorCode::WitnessMissingOrInvalid,
  827. detail: err.to_string(),
  828. },
  829. Error::SigAllUsedInMelt => ErrorResponse {
  830. code: ErrorCode::WitnessMissingOrInvalid,
  831. detail: err.to_string(),
  832. },
  833. // Keyset/key errors
  834. Error::AmountKey => ErrorResponse {
  835. code: ErrorCode::KeysetNotFound,
  836. detail: err.to_string(),
  837. },
  838. Error::KeysetUnknown(_) => ErrorResponse {
  839. code: ErrorCode::KeysetNotFound,
  840. detail: err.to_string(),
  841. },
  842. Error::NoActiveKeyset => ErrorResponse {
  843. code: ErrorCode::KeysetInactive,
  844. detail: err.to_string(),
  845. },
  846. // Quote/payment errors
  847. Error::UnknownQuote => ErrorResponse {
  848. code: ErrorCode::Unknown(50000),
  849. detail: err.to_string(),
  850. },
  851. Error::MeltingDisabled => ErrorResponse {
  852. code: ErrorCode::MintingDisabled,
  853. detail: err.to_string(),
  854. },
  855. Error::PaymentPending => ErrorResponse {
  856. code: ErrorCode::QuotePending,
  857. detail: err.to_string(),
  858. },
  859. Error::UnknownPaymentState => ErrorResponse {
  860. code: ErrorCode::Unknown(50000),
  861. detail: err.to_string(),
  862. },
  863. // Transaction/amount errors
  864. Error::SplitValuesGreater => ErrorResponse {
  865. code: ErrorCode::TransactionUnbalanced,
  866. detail: err.to_string(),
  867. },
  868. Error::AmountOverflow => ErrorResponse {
  869. code: ErrorCode::TransactionUnbalanced,
  870. detail: err.to_string(),
  871. },
  872. Error::OverIssue => ErrorResponse {
  873. code: ErrorCode::TransactionUnbalanced,
  874. detail: err.to_string(),
  875. },
  876. // Invoice parsing errors - no spec code for invalid format
  877. Error::InvalidPaymentRequest => ErrorResponse {
  878. code: ErrorCode::Unknown(50000),
  879. detail: err.to_string(),
  880. },
  881. Error::InvoiceAmountUndefined => ErrorResponse {
  882. code: ErrorCode::AmountlessInvoiceNotSupported,
  883. detail: err.to_string(),
  884. },
  885. // Internal/system errors - use Unknown(99999)
  886. Error::Internal => ErrorResponse {
  887. code: ErrorCode::Unknown(50000),
  888. detail: err.to_string(),
  889. },
  890. Error::Database(_) => ErrorResponse {
  891. code: ErrorCode::Unknown(50000),
  892. detail: err.to_string(),
  893. },
  894. Error::ConcurrentUpdate => ErrorResponse {
  895. code: ErrorCode::ConcurrentUpdate,
  896. detail: err.to_string(),
  897. },
  898. Error::MaxInputsExceeded { .. } => ErrorResponse {
  899. code: ErrorCode::MaxInputsExceeded,
  900. detail: err.to_string()
  901. },
  902. Error::MaxOutputsExceeded { .. } => ErrorResponse {
  903. code: ErrorCode::MaxOutputsExceeded,
  904. detail: err.to_string()
  905. },
  906. // Fallback for any remaining errors - use Unknown(99999) instead of TokenNotVerified
  907. _ => ErrorResponse {
  908. code: ErrorCode::Unknown(50000),
  909. detail: err.to_string(),
  910. },
  911. }
  912. }
  913. }
  914. #[cfg(feature = "mint")]
  915. impl From<crate::database::Error> for Error {
  916. fn from(db_error: crate::database::Error) -> Self {
  917. match db_error {
  918. crate::database::Error::InvalidStateTransition(state) => match state {
  919. crate::state::Error::Pending => Self::TokenPending,
  920. crate::state::Error::AlreadySpent => Self::TokenAlreadySpent,
  921. crate::state::Error::AlreadyPaid => Self::RequestAlreadyPaid,
  922. state => Self::Database(crate::database::Error::InvalidStateTransition(state)),
  923. },
  924. crate::database::Error::ConcurrentUpdate => Self::ConcurrentUpdate,
  925. db_error => Self::Database(db_error),
  926. }
  927. }
  928. }
  929. #[cfg(not(feature = "mint"))]
  930. impl From<crate::database::Error> for Error {
  931. fn from(db_error: crate::database::Error) -> Self {
  932. match db_error {
  933. crate::database::Error::ConcurrentUpdate => Self::ConcurrentUpdate,
  934. db_error => Self::Database(db_error),
  935. }
  936. }
  937. }
  938. impl From<ErrorResponse> for Error {
  939. fn from(err: ErrorResponse) -> Error {
  940. match err.code {
  941. // 10xxx - Proof/Token verification errors
  942. ErrorCode::TokenNotVerified => Self::DHKE(crate::dhke::Error::TokenNotVerified),
  943. // 11xxx - Input/Output errors
  944. ErrorCode::TokenAlreadySpent => Self::TokenAlreadySpent,
  945. ErrorCode::TokenPending => Self::TokenPending,
  946. ErrorCode::BlindedMessageAlreadySigned => Self::BlindedMessageAlreadySigned,
  947. ErrorCode::OutputsPending => Self::TokenPending, // Map to closest equivalent
  948. ErrorCode::TransactionUnbalanced => Self::TransactionUnbalanced(0, 0, 0),
  949. ErrorCode::AmountOutofLimitRange => {
  950. Self::AmountOutofLimitRange(Amount::default(), Amount::default(), Amount::default())
  951. }
  952. ErrorCode::DuplicateInputs => Self::DuplicateInputs,
  953. ErrorCode::DuplicateOutputs => Self::DuplicateOutputs,
  954. ErrorCode::MultipleUnits => Self::MultipleUnits,
  955. ErrorCode::UnitMismatch => Self::UnitMismatch,
  956. ErrorCode::AmountlessInvoiceNotSupported => Self::AmountLessNotAllowed,
  957. ErrorCode::IncorrectQuoteAmount => Self::IncorrectQuoteAmount,
  958. ErrorCode::UnsupportedUnit => Self::UnsupportedUnit,
  959. // 12xxx - Keyset errors
  960. ErrorCode::KeysetNotFound => Self::UnknownKeySet,
  961. ErrorCode::KeysetInactive => Self::InactiveKeyset,
  962. // 20xxx - Quote/Payment errors
  963. ErrorCode::QuoteNotPaid => Self::UnpaidQuote,
  964. ErrorCode::TokensAlreadyIssued => Self::IssuedQuote,
  965. ErrorCode::MintingDisabled => Self::MintingDisabled,
  966. ErrorCode::LightningError => Self::PaymentFailed,
  967. ErrorCode::QuotePending => Self::PendingQuote,
  968. ErrorCode::InvoiceAlreadyPaid => Self::RequestAlreadyPaid,
  969. ErrorCode::QuoteExpired => Self::ExpiredQuote(0, 0),
  970. ErrorCode::WitnessMissingOrInvalid => Self::SignatureMissingOrInvalid,
  971. ErrorCode::PubkeyRequired => Self::PubkeyRequired,
  972. // 30xxx - Clear auth errors
  973. ErrorCode::ClearAuthRequired => Self::ClearAuthRequired,
  974. ErrorCode::ClearAuthFailed => Self::ClearAuthFailed,
  975. // 31xxx - Blind auth errors
  976. ErrorCode::BlindAuthRequired => Self::BlindAuthRequired,
  977. ErrorCode::BlindAuthFailed => Self::BlindAuthFailed,
  978. ErrorCode::BatMintMaxExceeded => Self::InsufficientBlindAuthTokens,
  979. ErrorCode::BatRateLimitExceeded => Self::InsufficientBlindAuthTokens,
  980. _ => Self::UnknownErrorResponse(err.to_string()),
  981. }
  982. }
  983. }
  984. /// Possible Error Codes
  985. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
  986. #[cfg_attr(feature = "swagger", derive(utoipa::ToSchema))]
  987. pub enum ErrorCode {
  988. // 10xxx - Proof/Token verification errors
  989. /// Proof verification failed (10001)
  990. TokenNotVerified,
  991. // 11xxx - Input/Output errors
  992. /// Proofs already spent (11001)
  993. TokenAlreadySpent,
  994. /// Proofs are pending (11002)
  995. TokenPending,
  996. /// Outputs already signed (11003)
  997. BlindedMessageAlreadySigned,
  998. /// Outputs are pending (11004)
  999. OutputsPending,
  1000. /// Transaction is not balanced (11005)
  1001. TransactionUnbalanced,
  1002. /// Amount outside of limit range (11006)
  1003. AmountOutofLimitRange,
  1004. /// Duplicate inputs provided (11007)
  1005. DuplicateInputs,
  1006. /// Duplicate outputs provided (11008)
  1007. DuplicateOutputs,
  1008. /// Inputs/Outputs of multiple units (11009)
  1009. MultipleUnits,
  1010. /// Inputs and outputs not of same unit (11010)
  1011. UnitMismatch,
  1012. /// Amountless invoice is not supported (11011)
  1013. AmountlessInvoiceNotSupported,
  1014. /// Amount in request does not equal invoice (11012)
  1015. IncorrectQuoteAmount,
  1016. /// Unit in request is not supported (11013)
  1017. UnsupportedUnit,
  1018. /// The max number of inputs is exceeded
  1019. MaxInputsExceeded,
  1020. /// The max number of outputs is exceeded
  1021. MaxOutputsExceeded,
  1022. // 12xxx - Keyset errors
  1023. /// Keyset is not known (12001)
  1024. KeysetNotFound,
  1025. /// Keyset is inactive, cannot sign messages (12002)
  1026. KeysetInactive,
  1027. // 20xxx - Quote/Payment errors
  1028. /// Quote request is not paid (20001)
  1029. QuoteNotPaid,
  1030. /// Quote has already been issued (20002)
  1031. TokensAlreadyIssued,
  1032. /// Minting is disabled (20003)
  1033. MintingDisabled,
  1034. /// Lightning payment failed (20004)
  1035. LightningError,
  1036. /// Quote is pending (20005)
  1037. QuotePending,
  1038. /// Invoice already paid (20006)
  1039. InvoiceAlreadyPaid,
  1040. /// Quote is expired (20007)
  1041. QuoteExpired,
  1042. /// Signature for mint request invalid (20008)
  1043. WitnessMissingOrInvalid,
  1044. /// Pubkey required for mint quote (20009)
  1045. PubkeyRequired,
  1046. // 30xxx - Clear auth errors
  1047. /// Endpoint requires clear auth (30001)
  1048. ClearAuthRequired,
  1049. /// Clear authentication failed (30002)
  1050. ClearAuthFailed,
  1051. // 31xxx - Blind auth errors
  1052. /// Endpoint requires blind auth (31001)
  1053. BlindAuthRequired,
  1054. /// Blind authentication failed (31002)
  1055. BlindAuthFailed,
  1056. /// Maximum BAT mint amount exceeded (31003)
  1057. BatMintMaxExceeded,
  1058. /// BAT mint rate limit exceeded (31004)
  1059. BatRateLimitExceeded,
  1060. /// Concurrent update detected
  1061. ConcurrentUpdate,
  1062. /// Unknown error code
  1063. Unknown(u16),
  1064. }
  1065. impl ErrorCode {
  1066. /// Error code from u16
  1067. pub fn from_code(code: u16) -> Self {
  1068. match code {
  1069. // 10xxx - Proof/Token verification errors
  1070. 10001 => Self::TokenNotVerified,
  1071. // 11xxx - Input/Output errors
  1072. 11001 => Self::TokenAlreadySpent,
  1073. 11002 => Self::TokenPending,
  1074. 11003 => Self::BlindedMessageAlreadySigned,
  1075. 11004 => Self::OutputsPending,
  1076. 11005 => Self::TransactionUnbalanced,
  1077. 11006 => Self::AmountOutofLimitRange,
  1078. 11007 => Self::DuplicateInputs,
  1079. 11008 => Self::DuplicateOutputs,
  1080. 11009 => Self::MultipleUnits,
  1081. 11010 => Self::UnitMismatch,
  1082. 11011 => Self::AmountlessInvoiceNotSupported,
  1083. 11012 => Self::IncorrectQuoteAmount,
  1084. 11013 => Self::UnsupportedUnit,
  1085. 11014 => Self::MaxInputsExceeded,
  1086. 11015 => Self::MaxOutputsExceeded,
  1087. // 12xxx - Keyset errors
  1088. 12001 => Self::KeysetNotFound,
  1089. 12002 => Self::KeysetInactive,
  1090. // 20xxx - Quote/Payment errors
  1091. 20001 => Self::QuoteNotPaid,
  1092. 20002 => Self::TokensAlreadyIssued,
  1093. 20003 => Self::MintingDisabled,
  1094. 20004 => Self::LightningError,
  1095. 20005 => Self::QuotePending,
  1096. 20006 => Self::InvoiceAlreadyPaid,
  1097. 20007 => Self::QuoteExpired,
  1098. 20008 => Self::WitnessMissingOrInvalid,
  1099. 20009 => Self::PubkeyRequired,
  1100. // 30xxx - Clear auth errors
  1101. 30001 => Self::ClearAuthRequired,
  1102. 30002 => Self::ClearAuthFailed,
  1103. // 31xxx - Blind auth errors
  1104. 31001 => Self::BlindAuthRequired,
  1105. 31002 => Self::BlindAuthFailed,
  1106. 31003 => Self::BatMintMaxExceeded,
  1107. 31004 => Self::BatRateLimitExceeded,
  1108. _ => Self::Unknown(code),
  1109. }
  1110. }
  1111. /// Error code to u16
  1112. pub fn to_code(&self) -> u16 {
  1113. match self {
  1114. // 10xxx - Proof/Token verification errors
  1115. Self::TokenNotVerified => 10001,
  1116. // 11xxx - Input/Output errors
  1117. Self::TokenAlreadySpent => 11001,
  1118. Self::TokenPending => 11002,
  1119. Self::BlindedMessageAlreadySigned => 11003,
  1120. Self::OutputsPending => 11004,
  1121. Self::TransactionUnbalanced => 11005,
  1122. Self::AmountOutofLimitRange => 11006,
  1123. Self::DuplicateInputs => 11007,
  1124. Self::DuplicateOutputs => 11008,
  1125. Self::MultipleUnits => 11009,
  1126. Self::UnitMismatch => 11010,
  1127. Self::AmountlessInvoiceNotSupported => 11011,
  1128. Self::IncorrectQuoteAmount => 11012,
  1129. Self::UnsupportedUnit => 11013,
  1130. Self::MaxInputsExceeded => 11014,
  1131. Self::MaxOutputsExceeded => 11015,
  1132. // 12xxx - Keyset errors
  1133. Self::KeysetNotFound => 12001,
  1134. Self::KeysetInactive => 12002,
  1135. // 20xxx - Quote/Payment errors
  1136. Self::QuoteNotPaid => 20001,
  1137. Self::TokensAlreadyIssued => 20002,
  1138. Self::MintingDisabled => 20003,
  1139. Self::LightningError => 20004,
  1140. Self::QuotePending => 20005,
  1141. Self::InvoiceAlreadyPaid => 20006,
  1142. Self::QuoteExpired => 20007,
  1143. Self::WitnessMissingOrInvalid => 20008,
  1144. Self::PubkeyRequired => 20009,
  1145. // 30xxx - Clear auth errors
  1146. Self::ClearAuthRequired => 30001,
  1147. Self::ClearAuthFailed => 30002,
  1148. // 31xxx - Blind auth errors
  1149. Self::BlindAuthRequired => 31001,
  1150. Self::BlindAuthFailed => 31002,
  1151. Self::BatMintMaxExceeded => 31003,
  1152. Self::BatRateLimitExceeded => 31004,
  1153. Self::ConcurrentUpdate => 50000,
  1154. Self::Unknown(code) => *code,
  1155. }
  1156. }
  1157. }
  1158. impl Serialize for ErrorCode {
  1159. fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
  1160. where
  1161. S: Serializer,
  1162. {
  1163. serializer.serialize_u16(self.to_code())
  1164. }
  1165. }
  1166. impl<'de> Deserialize<'de> for ErrorCode {
  1167. fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
  1168. where
  1169. D: Deserializer<'de>,
  1170. {
  1171. let code = u16::deserialize(deserializer)?;
  1172. Ok(ErrorCode::from_code(code))
  1173. }
  1174. }
  1175. impl fmt::Display for ErrorCode {
  1176. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  1177. write!(f, "{}", self.to_code())
  1178. }
  1179. }