mod.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. //! CDK Database
  2. use std::collections::HashMap;
  3. use async_trait::async_trait;
  4. use cashu::quote_id::QuoteId;
  5. use cashu::Amount;
  6. use super::{DbTransactionFinalizer, Error};
  7. use crate::database::Acquired;
  8. use crate::mint::{self, MeltQuote, MintKeySetInfo, MintQuote as MintMintQuote, Operation};
  9. use crate::nuts::{
  10. BlindSignature, BlindedMessage, CurrencyUnit, Id, MeltQuoteState, Proof, Proofs, PublicKey,
  11. State,
  12. };
  13. use crate::payment::PaymentIdentifier;
  14. #[cfg(feature = "auth")]
  15. mod auth;
  16. #[cfg(feature = "test")]
  17. pub mod test;
  18. #[cfg(feature = "auth")]
  19. pub use auth::{DynMintAuthDatabase, MintAuthDatabase, MintAuthTransaction};
  20. // Re-export KVStore types from shared module for backward compatibility
  21. pub use super::kvstore::{
  22. validate_kvstore_params, validate_kvstore_string, KVStore, KVStoreDatabase, KVStoreTransaction,
  23. KVSTORE_NAMESPACE_KEY_ALPHABET, KVSTORE_NAMESPACE_KEY_MAX_LEN,
  24. };
  25. /// Information about a melt request stored in the database
  26. #[derive(Debug, Clone, PartialEq, Eq)]
  27. pub struct MeltRequestInfo {
  28. /// Total amount of all input proofs in the melt request
  29. pub inputs_amount: Amount,
  30. /// Fee amount associated with the input proofs
  31. pub inputs_fee: Amount,
  32. /// Blinded messages for change outputs
  33. pub change_outputs: Vec<BlindedMessage>,
  34. }
  35. /// KeysDatabaseWriter
  36. #[async_trait]
  37. pub trait KeysDatabaseTransaction<'a, Error>: DbTransactionFinalizer<Err = Error> {
  38. /// Add Active Keyset
  39. async fn set_active_keyset(&mut self, unit: CurrencyUnit, id: Id) -> Result<(), Error>;
  40. /// Add [`MintKeySetInfo`]
  41. async fn add_keyset_info(&mut self, keyset: MintKeySetInfo) -> Result<(), Error>;
  42. }
  43. /// Mint Keys Database trait
  44. #[async_trait]
  45. pub trait KeysDatabase {
  46. /// Mint Keys Database Error
  47. type Err: Into<Error> + From<Error>;
  48. /// Begins a transaction
  49. async fn begin_transaction<'a>(
  50. &'a self,
  51. ) -> Result<Box<dyn KeysDatabaseTransaction<'a, Self::Err> + Send + Sync + 'a>, Error>;
  52. /// Get Active Keyset
  53. async fn get_active_keyset_id(&self, unit: &CurrencyUnit) -> Result<Option<Id>, Self::Err>;
  54. /// Get all Active Keyset
  55. async fn get_active_keysets(&self) -> Result<HashMap<CurrencyUnit, Id>, Self::Err>;
  56. /// Get [`MintKeySetInfo`]
  57. async fn get_keyset_info(&self, id: &Id) -> Result<Option<MintKeySetInfo>, Self::Err>;
  58. /// Get [`MintKeySetInfo`]s
  59. async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
  60. }
  61. /// Mint Quote Database writer trait
  62. #[async_trait]
  63. pub trait QuotesTransaction {
  64. /// Mint Quotes Database Error
  65. type Err: Into<Error> + From<Error>;
  66. /// Add melt_request with quote_id, inputs_amount, and inputs_fee
  67. async fn add_melt_request(
  68. &mut self,
  69. quote_id: &QuoteId,
  70. inputs_amount: Amount,
  71. inputs_fee: Amount,
  72. ) -> Result<(), Self::Err>;
  73. /// Add blinded_messages for a quote_id
  74. async fn add_blinded_messages(
  75. &mut self,
  76. quote_id: Option<&QuoteId>,
  77. blinded_messages: &[BlindedMessage],
  78. operation: &Operation,
  79. ) -> Result<(), Self::Err>;
  80. /// Delete blinded_messages by their blinded secrets
  81. async fn delete_blinded_messages(
  82. &mut self,
  83. blinded_secrets: &[PublicKey],
  84. ) -> Result<(), Self::Err>;
  85. /// Get melt_request and associated blinded_messages by quote_id
  86. async fn get_melt_request_and_blinded_messages(
  87. &mut self,
  88. quote_id: &QuoteId,
  89. ) -> Result<Option<MeltRequestInfo>, Self::Err>;
  90. /// Delete melt_request and associated blinded_messages by quote_id
  91. async fn delete_melt_request(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err>;
  92. /// Get [`MintMintQuote`] and lock it for update in this transaction
  93. async fn get_mint_quote(
  94. &mut self,
  95. quote_id: &QuoteId,
  96. ) -> Result<Option<Acquired<MintMintQuote>>, Self::Err>;
  97. /// Add [`MintMintQuote`]
  98. async fn add_mint_quote(
  99. &mut self,
  100. quote: MintMintQuote,
  101. ) -> Result<Acquired<MintMintQuote>, Self::Err>;
  102. /// Persists any pending changes made to the mint quote.
  103. ///
  104. /// This method extracts changes accumulated in the quote (via [`mint::MintQuote::take_changes`])
  105. /// and persists them to the database. Changes may include new payments received or new
  106. /// issuances recorded against the quote.
  107. ///
  108. /// If no changes are pending, this method returns successfully without performing
  109. /// any database operations.
  110. ///
  111. /// # Arguments
  112. ///
  113. /// * `quote` - A mutable reference to an acquired (row-locked) mint quote. The quote
  114. /// must be locked to ensure transactional consistency when persisting changes.
  115. ///
  116. /// # Implementation Notes
  117. ///
  118. /// Implementations should call [`mint::MintQuote::take_changes`] to retrieve pending
  119. /// changes, then persist each payment and issuance record, and finally update the
  120. /// quote's aggregate counters (`amount_paid`, `amount_issued`) in the database.
  121. async fn update_mint_quote(
  122. &mut self,
  123. quote: &mut Acquired<mint::MintQuote>,
  124. ) -> Result<(), Self::Err>;
  125. /// Get [`mint::MeltQuote`] and lock it for update in this transaction
  126. async fn get_melt_quote(
  127. &mut self,
  128. quote_id: &QuoteId,
  129. ) -> Result<Option<Acquired<mint::MeltQuote>>, Self::Err>;
  130. /// Add [`mint::MeltQuote`]
  131. async fn add_melt_quote(&mut self, quote: mint::MeltQuote) -> Result<(), Self::Err>;
  132. /// Retrieves all melt quotes matching a payment lookup identifier and locks them for update.
  133. ///
  134. /// This method returns multiple quotes because certain payment methods (notably BOLT12 offers)
  135. /// can generate multiple payment attempts that share the same lookup identifier. Locking all
  136. /// related quotes prevents race conditions where concurrent melt operations could interfere
  137. /// with each other, potentially leading to double-spending or state inconsistencies.
  138. ///
  139. /// The returned quotes are locked within the current transaction to ensure safe concurrent
  140. /// modification. This is essential during melt saga initiation and finalization to guarantee
  141. /// atomic state transitions across all related quotes.
  142. ///
  143. /// # Arguments
  144. ///
  145. /// * `request_lookup_id` - The payment identifier used by the Lightning backend to track
  146. /// payment state (e.g., payment hash, offer ID, or label).
  147. async fn get_melt_quotes_by_request_lookup_id(
  148. &mut self,
  149. request_lookup_id: &PaymentIdentifier,
  150. ) -> Result<Vec<Acquired<MeltQuote>>, Self::Err>;
  151. /// Updates the request lookup id for a melt quote.
  152. ///
  153. /// Requires an [`Acquired`] melt quote to ensure the row is locked before modification.
  154. async fn update_melt_quote_request_lookup_id(
  155. &mut self,
  156. quote: &mut Acquired<mint::MeltQuote>,
  157. new_request_lookup_id: &PaymentIdentifier,
  158. ) -> Result<(), Self::Err>;
  159. /// Update [`mint::MeltQuote`] state.
  160. ///
  161. /// Requires an [`Acquired`] melt quote to ensure the row is locked before modification.
  162. /// Returns the previous state.
  163. async fn update_melt_quote_state(
  164. &mut self,
  165. quote: &mut Acquired<mint::MeltQuote>,
  166. new_state: MeltQuoteState,
  167. payment_proof: Option<String>,
  168. ) -> Result<MeltQuoteState, Self::Err>;
  169. /// Get all [`MintMintQuote`]s and lock it for update in this transaction
  170. async fn get_mint_quote_by_request(
  171. &mut self,
  172. request: &str,
  173. ) -> Result<Option<Acquired<MintMintQuote>>, Self::Err>;
  174. /// Get all [`MintMintQuote`]s
  175. async fn get_mint_quote_by_request_lookup_id(
  176. &mut self,
  177. request_lookup_id: &PaymentIdentifier,
  178. ) -> Result<Option<Acquired<MintMintQuote>>, Self::Err>;
  179. }
  180. /// Mint Quote Database trait
  181. #[async_trait]
  182. pub trait QuotesDatabase {
  183. /// Mint Quotes Database Error
  184. type Err: Into<Error> + From<Error>;
  185. /// Get [`MintMintQuote`]
  186. async fn get_mint_quote(&self, quote_id: &QuoteId) -> Result<Option<MintMintQuote>, Self::Err>;
  187. /// Get all [`MintMintQuote`]s
  188. async fn get_mint_quote_by_request(
  189. &self,
  190. request: &str,
  191. ) -> Result<Option<MintMintQuote>, Self::Err>;
  192. /// Get all [`MintMintQuote`]s
  193. async fn get_mint_quote_by_request_lookup_id(
  194. &self,
  195. request_lookup_id: &PaymentIdentifier,
  196. ) -> Result<Option<MintMintQuote>, Self::Err>;
  197. /// Get Mint Quotes
  198. async fn get_mint_quotes(&self) -> Result<Vec<MintMintQuote>, Self::Err>;
  199. /// Get [`mint::MeltQuote`]
  200. async fn get_melt_quote(
  201. &self,
  202. quote_id: &QuoteId,
  203. ) -> Result<Option<mint::MeltQuote>, Self::Err>;
  204. /// Get all [`mint::MeltQuote`]s
  205. async fn get_melt_quotes(&self) -> Result<Vec<mint::MeltQuote>, Self::Err>;
  206. }
  207. /// Mint Proof Transaction trait
  208. #[async_trait]
  209. pub trait ProofsTransaction {
  210. /// Mint Proof Database Error
  211. type Err: Into<Error> + From<Error>;
  212. /// Add [`Proofs`]
  213. ///
  214. /// Adds proofs to the database. The database should error if the proof already exits, with a
  215. /// `AttemptUpdateSpentProof` if the proof is already spent or a `Duplicate` error otherwise.
  216. async fn add_proofs(
  217. &mut self,
  218. proof: Proofs,
  219. quote_id: Option<QuoteId>,
  220. operation: &Operation,
  221. ) -> Result<(), Self::Err>;
  222. /// Updates the proofs to a given states and return the previous states
  223. async fn update_proofs_states(
  224. &mut self,
  225. ys: &[PublicKey],
  226. proofs_state: State,
  227. ) -> Result<Vec<Option<State>>, Self::Err>;
  228. /// get proofs states
  229. async fn get_proofs_states(
  230. &mut self,
  231. ys: &[PublicKey],
  232. ) -> Result<Vec<Option<State>>, Self::Err>;
  233. /// Remove [`Proofs`]
  234. async fn remove_proofs(
  235. &mut self,
  236. ys: &[PublicKey],
  237. quote_id: Option<QuoteId>,
  238. ) -> Result<(), Self::Err>;
  239. /// Get ys by quote id
  240. async fn get_proof_ys_by_quote_id(
  241. &mut self,
  242. quote_id: &QuoteId,
  243. ) -> Result<Vec<PublicKey>, Self::Err>;
  244. /// Get proof ys by operation id
  245. async fn get_proof_ys_by_operation_id(
  246. &self,
  247. operation_id: &uuid::Uuid,
  248. ) -> Result<Vec<PublicKey>, Self::Err>;
  249. }
  250. /// Mint Proof Database trait
  251. #[async_trait]
  252. pub trait ProofsDatabase {
  253. /// Mint Proof Database Error
  254. type Err: Into<Error> + From<Error>;
  255. /// Get [`Proofs`] by ys
  256. async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
  257. /// Get ys by quote id
  258. async fn get_proof_ys_by_quote_id(
  259. &self,
  260. quote_id: &QuoteId,
  261. ) -> Result<Vec<PublicKey>, Self::Err>;
  262. /// Get [`Proofs`] state
  263. async fn get_proofs_states(&self, ys: &[PublicKey]) -> Result<Vec<Option<State>>, Self::Err>;
  264. /// Get [`Proofs`] by state
  265. async fn get_proofs_by_keyset_id(
  266. &self,
  267. keyset_id: &Id,
  268. ) -> Result<(Proofs, Vec<Option<State>>), Self::Err>;
  269. /// Get total proofs redeemed by keyset id
  270. async fn get_total_redeemed(&self) -> Result<HashMap<Id, Amount>, Self::Err>;
  271. /// Get proof ys by operation id
  272. async fn get_proof_ys_by_operation_id(
  273. &self,
  274. operation_id: &uuid::Uuid,
  275. ) -> Result<Vec<PublicKey>, Self::Err>;
  276. }
  277. #[async_trait]
  278. /// Mint Signatures Transaction trait
  279. pub trait SignaturesTransaction {
  280. /// Mint Signature Database Error
  281. type Err: Into<Error> + From<Error>;
  282. /// Add [`BlindSignature`]
  283. async fn add_blind_signatures(
  284. &mut self,
  285. blinded_messages: &[PublicKey],
  286. blind_signatures: &[BlindSignature],
  287. quote_id: Option<QuoteId>,
  288. ) -> Result<(), Self::Err>;
  289. /// Get [`BlindSignature`]s
  290. async fn get_blind_signatures(
  291. &mut self,
  292. blinded_messages: &[PublicKey],
  293. ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
  294. }
  295. #[async_trait]
  296. /// Mint Signatures Database trait
  297. pub trait SignaturesDatabase {
  298. /// Mint Signature Database Error
  299. type Err: Into<Error> + From<Error>;
  300. /// Get [`BlindSignature`]s
  301. async fn get_blind_signatures(
  302. &self,
  303. blinded_messages: &[PublicKey],
  304. ) -> Result<Vec<Option<BlindSignature>>, Self::Err>;
  305. /// Get [`BlindSignature`]s for keyset_id
  306. async fn get_blind_signatures_for_keyset(
  307. &self,
  308. keyset_id: &Id,
  309. ) -> Result<Vec<BlindSignature>, Self::Err>;
  310. /// Get [`BlindSignature`]s for quote
  311. async fn get_blind_signatures_for_quote(
  312. &self,
  313. quote_id: &QuoteId,
  314. ) -> Result<Vec<BlindSignature>, Self::Err>;
  315. /// Get total amount issued by keyset id
  316. async fn get_total_issued(&self) -> Result<HashMap<Id, Amount>, Self::Err>;
  317. /// Get blinded secrets (B values) by operation id
  318. async fn get_blinded_secrets_by_operation_id(
  319. &self,
  320. operation_id: &uuid::Uuid,
  321. ) -> Result<Vec<PublicKey>, Self::Err>;
  322. }
  323. #[async_trait]
  324. /// Saga Transaction trait
  325. pub trait SagaTransaction {
  326. /// Saga Database Error
  327. type Err: Into<Error> + From<Error>;
  328. /// Get saga by operation_id
  329. async fn get_saga(
  330. &mut self,
  331. operation_id: &uuid::Uuid,
  332. ) -> Result<Option<mint::Saga>, Self::Err>;
  333. /// Add saga
  334. async fn add_saga(&mut self, saga: &mint::Saga) -> Result<(), Self::Err>;
  335. /// Update saga state (only updates state and updated_at fields)
  336. async fn update_saga(
  337. &mut self,
  338. operation_id: &uuid::Uuid,
  339. new_state: mint::SagaStateEnum,
  340. ) -> Result<(), Self::Err>;
  341. /// Delete saga
  342. async fn delete_saga(&mut self, operation_id: &uuid::Uuid) -> Result<(), Self::Err>;
  343. }
  344. #[async_trait]
  345. /// Saga Database trait
  346. pub trait SagaDatabase {
  347. /// Saga Database Error
  348. type Err: Into<Error> + From<Error>;
  349. /// Get all incomplete sagas for a given operation kind
  350. async fn get_incomplete_sagas(
  351. &self,
  352. operation_kind: mint::OperationKind,
  353. ) -> Result<Vec<mint::Saga>, Self::Err>;
  354. }
  355. #[async_trait]
  356. /// Completed Operations Transaction trait
  357. pub trait CompletedOperationsTransaction {
  358. /// Completed Operations Database Error
  359. type Err: Into<Error> + From<Error>;
  360. /// Add completed operation
  361. async fn add_completed_operation(
  362. &mut self,
  363. operation: &mint::Operation,
  364. fee_by_keyset: &std::collections::HashMap<crate::nuts::Id, crate::Amount>,
  365. ) -> Result<(), Self::Err>;
  366. }
  367. #[async_trait]
  368. /// Completed Operations Database trait
  369. pub trait CompletedOperationsDatabase {
  370. /// Completed Operations Database Error
  371. type Err: Into<Error> + From<Error>;
  372. /// Get completed operation by operation_id
  373. async fn get_completed_operation(
  374. &self,
  375. operation_id: &uuid::Uuid,
  376. ) -> Result<Option<mint::Operation>, Self::Err>;
  377. /// Get completed operations by operation kind
  378. async fn get_completed_operations_by_kind(
  379. &self,
  380. operation_kind: mint::OperationKind,
  381. ) -> Result<Vec<mint::Operation>, Self::Err>;
  382. /// Get all completed operations
  383. async fn get_completed_operations(&self) -> Result<Vec<mint::Operation>, Self::Err>;
  384. }
  385. /// Base database writer
  386. pub trait Transaction<Error>:
  387. DbTransactionFinalizer<Err = Error>
  388. + QuotesTransaction<Err = Error>
  389. + SignaturesTransaction<Err = Error>
  390. + ProofsTransaction<Err = Error>
  391. + KVStoreTransaction<Error>
  392. + SagaTransaction<Err = Error>
  393. + CompletedOperationsTransaction<Err = Error>
  394. {
  395. }
  396. /// Mint Database trait
  397. #[async_trait]
  398. pub trait Database<Error>:
  399. KVStoreDatabase<Err = Error>
  400. + QuotesDatabase<Err = Error>
  401. + ProofsDatabase<Err = Error>
  402. + SignaturesDatabase<Err = Error>
  403. + SagaDatabase<Err = Error>
  404. + CompletedOperationsDatabase<Err = Error>
  405. {
  406. /// Begins a transaction
  407. async fn begin_transaction(&self) -> Result<Box<dyn Transaction<Error> + Send + Sync>, Error>;
  408. }
  409. /// Type alias for Mint Database
  410. pub type DynMintDatabase = std::sync::Arc<dyn Database<Error> + Send + Sync>;