database.rs 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646
  1. //! FFI Database bindings
  2. use std::collections::HashMap;
  3. use std::sync::atomic::AtomicBool;
  4. use std::sync::Arc;
  5. use cdk_common::database::{
  6. DbTransactionFinalizer, DynWalletDatabaseTransaction, WalletDatabase as CdkWalletDatabase,
  7. WalletDatabaseTransaction as CdkWalletDatabaseTransaction,
  8. };
  9. use cdk_common::task::spawn;
  10. use cdk_sql_common::pool::DatabasePool;
  11. use cdk_sql_common::SQLWalletDatabase;
  12. use tokio::sync::Mutex;
  13. use crate::error::FfiError;
  14. #[cfg(feature = "postgres")]
  15. use crate::postgres::WalletPostgresDatabase;
  16. use crate::sqlite::WalletSqliteDatabase;
  17. use crate::types::*;
  18. /// FFI-compatible wallet database trait (read-only operations + begin_db_transaction)
  19. /// This trait mirrors the CDK WalletDatabase trait structure
  20. #[uniffi::export]
  21. #[async_trait::async_trait]
  22. pub trait WalletDatabase: Send + Sync {
  23. /// Begin a database transaction
  24. async fn begin_db_transaction(&self)
  25. -> Result<Arc<WalletDatabaseTransactionWrapper>, FfiError>;
  26. /// Get mint from storage
  27. async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError>;
  28. /// Get all mints from storage
  29. async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError>;
  30. /// Get mint keysets for mint url
  31. async fn get_mint_keysets(
  32. &self,
  33. mint_url: MintUrl,
  34. ) -> Result<Option<Vec<KeySetInfo>>, FfiError>;
  35. /// Get mint keyset by id
  36. async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError>;
  37. /// Get mint quote from storage
  38. async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError>;
  39. /// Get mint quotes from storage
  40. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
  41. /// Get unissued mint quotes from storage
  42. /// Returns bolt11 quotes where nothing has been issued yet (amount_issued = 0) and all bolt12 quotes.
  43. async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
  44. /// Get melt quote from storage
  45. async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError>;
  46. /// Get melt quotes from storage
  47. async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError>;
  48. /// Get Keys from storage
  49. async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError>;
  50. /// Get proofs from storage
  51. async fn get_proofs(
  52. &self,
  53. mint_url: Option<MintUrl>,
  54. unit: Option<CurrencyUnit>,
  55. state: Option<Vec<ProofState>>,
  56. spending_conditions: Option<Vec<SpendingConditions>>,
  57. ) -> Result<Vec<ProofInfo>, FfiError>;
  58. /// Get proofs by Y values
  59. async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError>;
  60. /// Get balance efficiently using SQL aggregation
  61. async fn get_balance(
  62. &self,
  63. mint_url: Option<MintUrl>,
  64. unit: Option<CurrencyUnit>,
  65. state: Option<Vec<ProofState>>,
  66. ) -> Result<u64, FfiError>;
  67. /// Get transaction from storage
  68. async fn get_transaction(
  69. &self,
  70. transaction_id: TransactionId,
  71. ) -> Result<Option<Transaction>, FfiError>;
  72. /// List transactions from storage
  73. async fn list_transactions(
  74. &self,
  75. mint_url: Option<MintUrl>,
  76. direction: Option<TransactionDirection>,
  77. unit: Option<CurrencyUnit>,
  78. ) -> Result<Vec<Transaction>, FfiError>;
  79. }
  80. /// FFI-compatible transaction trait for wallet database write operations
  81. /// This trait mirrors the CDK WalletDatabaseTransaction trait but uses FFI-compatible types
  82. #[uniffi::export(with_foreign)]
  83. #[async_trait::async_trait]
  84. pub trait WalletDatabaseTransaction: Send + Sync {
  85. /// Commit the transaction
  86. async fn commit(self: Arc<Self>) -> Result<(), FfiError>;
  87. /// Rollback the transaction
  88. async fn rollback(self: Arc<Self>) -> Result<(), FfiError>;
  89. // Mint Management
  90. /// Add Mint to storage
  91. async fn add_mint(
  92. &self,
  93. mint_url: MintUrl,
  94. mint_info: Option<MintInfo>,
  95. ) -> Result<(), FfiError>;
  96. /// Remove Mint from storage
  97. async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError>;
  98. /// Update mint url
  99. async fn update_mint_url(
  100. &self,
  101. old_mint_url: MintUrl,
  102. new_mint_url: MintUrl,
  103. ) -> Result<(), FfiError>;
  104. // Keyset Management
  105. /// Add mint keyset to storage
  106. async fn add_mint_keysets(
  107. &self,
  108. mint_url: MintUrl,
  109. keysets: Vec<KeySetInfo>,
  110. ) -> Result<(), FfiError>;
  111. /// Get mint keyset by id (transaction-scoped)
  112. async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError>;
  113. /// Get Keys from storage (transaction-scoped)
  114. async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError>;
  115. // Mint Quote Management
  116. /// Get mint quote from storage (transaction-scoped, with locking)
  117. async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError>;
  118. /// Add mint quote to storage
  119. async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError>;
  120. /// Remove mint quote from storage
  121. async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError>;
  122. // Melt Quote Management
  123. /// Get melt quote from storage (transaction-scoped)
  124. async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError>;
  125. /// Add melt quote to storage
  126. async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError>;
  127. /// Remove melt quote from storage
  128. async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError>;
  129. // Keys Management
  130. /// Add Keys to storage
  131. async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError>;
  132. /// Remove Keys from storage
  133. async fn remove_keys(&self, id: Id) -> Result<(), FfiError>;
  134. // Proof Management
  135. /// Get proofs from storage (transaction-scoped, with locking)
  136. async fn get_proofs(
  137. &self,
  138. mint_url: Option<MintUrl>,
  139. unit: Option<CurrencyUnit>,
  140. state: Option<Vec<ProofState>>,
  141. spending_conditions: Option<Vec<SpendingConditions>>,
  142. ) -> Result<Vec<ProofInfo>, FfiError>;
  143. /// Update the proofs in storage by adding new proofs or removing proofs by their Y value
  144. async fn update_proofs(
  145. &self,
  146. added: Vec<ProofInfo>,
  147. removed_ys: Vec<PublicKey>,
  148. ) -> Result<(), FfiError>;
  149. /// Update proofs state in storage
  150. async fn update_proofs_state(
  151. &self,
  152. ys: Vec<PublicKey>,
  153. state: ProofState,
  154. ) -> Result<(), FfiError>;
  155. // Keyset Counter Management
  156. /// Increment Keyset counter
  157. async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError>;
  158. // Transaction Management
  159. /// Add transaction to storage
  160. async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError>;
  161. /// Remove transaction from storage
  162. async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError>;
  163. }
  164. /// Wallet database transaction wrapper
  165. #[derive(uniffi::Object)]
  166. pub struct WalletDatabaseTransactionWrapper {
  167. inner: Arc<dyn WalletDatabaseTransaction>,
  168. }
  169. #[uniffi::export(async_runtime = "tokio")]
  170. impl WalletDatabaseTransactionWrapper {
  171. /// Commit the transaction
  172. pub async fn commit(&self) -> Result<(), FfiError> {
  173. self.inner.clone().commit().await
  174. }
  175. /// Rollback the transaction
  176. pub async fn rollback(&self) -> Result<(), FfiError> {
  177. self.inner.clone().rollback().await
  178. }
  179. /// Add Mint to storage
  180. pub async fn add_mint(
  181. &self,
  182. mint_url: MintUrl,
  183. mint_info: Option<MintInfo>,
  184. ) -> Result<(), FfiError> {
  185. self.inner.add_mint(mint_url, mint_info).await
  186. }
  187. /// Remove Mint from storage
  188. pub async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
  189. self.inner.remove_mint(mint_url).await
  190. }
  191. /// Update mint url
  192. pub async fn update_mint_url(
  193. &self,
  194. old_mint_url: MintUrl,
  195. new_mint_url: MintUrl,
  196. ) -> Result<(), FfiError> {
  197. self.inner.update_mint_url(old_mint_url, new_mint_url).await
  198. }
  199. /// Add mint keyset to storage
  200. pub async fn add_mint_keysets(
  201. &self,
  202. mint_url: MintUrl,
  203. keysets: Vec<KeySetInfo>,
  204. ) -> Result<(), FfiError> {
  205. self.inner.add_mint_keysets(mint_url, keysets).await
  206. }
  207. /// Get mint keyset by id (transaction-scoped)
  208. pub async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
  209. self.inner.get_keyset_by_id(keyset_id).await
  210. }
  211. /// Get Keys from storage (transaction-scoped)
  212. pub async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
  213. self.inner.get_keys(id).await
  214. }
  215. /// Get mint quote from storage (transaction-scoped, with locking)
  216. pub async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
  217. self.inner.get_mint_quote(quote_id).await
  218. }
  219. /// Add mint quote to storage
  220. pub async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
  221. self.inner.add_mint_quote(quote).await
  222. }
  223. /// Remove mint quote from storage
  224. pub async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
  225. self.inner.remove_mint_quote(quote_id).await
  226. }
  227. /// Get melt quote from storage (transaction-scoped)
  228. pub async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
  229. self.inner.get_melt_quote(quote_id).await
  230. }
  231. /// Add melt quote to storage
  232. pub async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
  233. self.inner.add_melt_quote(quote).await
  234. }
  235. /// Remove melt quote from storage
  236. pub async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
  237. self.inner.remove_melt_quote(quote_id).await
  238. }
  239. /// Add Keys to storage
  240. pub async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
  241. self.inner.add_keys(keyset).await
  242. }
  243. /// Remove Keys from storage
  244. pub async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
  245. self.inner.remove_keys(id).await
  246. }
  247. /// Get proofs from storage (transaction-scoped, with locking)
  248. pub async fn get_proofs(
  249. &self,
  250. mint_url: Option<MintUrl>,
  251. unit: Option<CurrencyUnit>,
  252. state: Option<Vec<ProofState>>,
  253. spending_conditions: Option<Vec<SpendingConditions>>,
  254. ) -> Result<Vec<ProofInfo>, FfiError> {
  255. self.inner
  256. .get_proofs(mint_url, unit, state, spending_conditions)
  257. .await
  258. }
  259. /// Update the proofs in storage by adding new proofs or removing proofs by their Y value
  260. pub async fn update_proofs(
  261. &self,
  262. added: Vec<ProofInfo>,
  263. removed_ys: Vec<PublicKey>,
  264. ) -> Result<(), FfiError> {
  265. self.inner.update_proofs(added, removed_ys).await
  266. }
  267. /// Update proofs state in storage
  268. pub async fn update_proofs_state(
  269. &self,
  270. ys: Vec<PublicKey>,
  271. state: ProofState,
  272. ) -> Result<(), FfiError> {
  273. self.inner.update_proofs_state(ys, state).await
  274. }
  275. /// Increment Keyset counter
  276. pub async fn increment_keyset_counter(
  277. &self,
  278. keyset_id: Id,
  279. count: u32,
  280. ) -> Result<u32, FfiError> {
  281. self.inner.increment_keyset_counter(keyset_id, count).await
  282. }
  283. /// Add transaction to storage
  284. pub async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
  285. self.inner.add_transaction(transaction).await
  286. }
  287. /// Remove transaction from storage
  288. pub async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
  289. self.inner.remove_transaction(transaction_id).await
  290. }
  291. }
  292. /// Internal bridge trait to convert from the FFI trait to the CDK database trait
  293. /// This allows us to bridge between the UniFFI trait and the CDK's internal database trait
  294. struct WalletDatabaseBridge {
  295. ffi_db: Arc<dyn WalletDatabase>,
  296. }
  297. impl WalletDatabaseBridge {
  298. fn new(ffi_db: Arc<dyn WalletDatabase>) -> Self {
  299. Self { ffi_db }
  300. }
  301. }
  302. impl std::fmt::Debug for WalletDatabaseBridge {
  303. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  304. write!(f, "WalletDatabaseBridge")
  305. }
  306. }
  307. #[async_trait::async_trait]
  308. impl CdkWalletDatabase for WalletDatabaseBridge {
  309. type Err = cdk::cdk_database::Error;
  310. // Mint Management
  311. async fn get_mint(
  312. &self,
  313. mint_url: cdk::mint_url::MintUrl,
  314. ) -> Result<Option<cdk::nuts::MintInfo>, Self::Err> {
  315. let ffi_mint_url = mint_url.into();
  316. let result = self
  317. .ffi_db
  318. .get_mint(ffi_mint_url)
  319. .await
  320. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  321. Ok(result.map(Into::into))
  322. }
  323. async fn get_mints(
  324. &self,
  325. ) -> Result<HashMap<cdk::mint_url::MintUrl, Option<cdk::nuts::MintInfo>>, Self::Err> {
  326. let result = self
  327. .ffi_db
  328. .get_mints()
  329. .await
  330. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  331. let mut cdk_result = HashMap::new();
  332. for (ffi_mint_url, mint_info_opt) in result {
  333. let cdk_url = ffi_mint_url
  334. .try_into()
  335. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  336. cdk_result.insert(cdk_url, mint_info_opt.map(Into::into));
  337. }
  338. Ok(cdk_result)
  339. }
  340. // Keyset Management
  341. async fn get_mint_keysets(
  342. &self,
  343. mint_url: cdk::mint_url::MintUrl,
  344. ) -> Result<Option<Vec<cdk::nuts::KeySetInfo>>, Self::Err> {
  345. let ffi_mint_url = mint_url.into();
  346. let result = self
  347. .ffi_db
  348. .get_mint_keysets(ffi_mint_url)
  349. .await
  350. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  351. Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
  352. }
  353. async fn get_keyset_by_id(
  354. &self,
  355. keyset_id: &cdk::nuts::Id,
  356. ) -> Result<Option<cdk::nuts::KeySetInfo>, Self::Err> {
  357. let ffi_id = (*keyset_id).into();
  358. let result = self
  359. .ffi_db
  360. .get_keyset_by_id(ffi_id)
  361. .await
  362. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  363. Ok(result.map(Into::into))
  364. }
  365. // Mint Quote Management
  366. async fn get_mint_quote(
  367. &self,
  368. quote_id: &str,
  369. ) -> Result<Option<cdk::wallet::MintQuote>, Self::Err> {
  370. let result = self
  371. .ffi_db
  372. .get_mint_quote(quote_id.to_string())
  373. .await
  374. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  375. Ok(result
  376. .map(|q| {
  377. q.try_into()
  378. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  379. })
  380. .transpose()?)
  381. }
  382. async fn get_mint_quotes(&self) -> Result<Vec<cdk::wallet::MintQuote>, Self::Err> {
  383. let result = self
  384. .ffi_db
  385. .get_mint_quotes()
  386. .await
  387. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  388. Ok(result
  389. .into_iter()
  390. .map(|q| {
  391. q.try_into()
  392. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  393. })
  394. .collect::<Result<Vec<_>, _>>()?)
  395. }
  396. async fn get_unissued_mint_quotes(&self) -> Result<Vec<cdk::wallet::MintQuote>, Self::Err> {
  397. let result = self
  398. .ffi_db
  399. .get_unissued_mint_quotes()
  400. .await
  401. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  402. Ok(result
  403. .into_iter()
  404. .map(|q| {
  405. q.try_into()
  406. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  407. })
  408. .collect::<Result<Vec<_>, _>>()?)
  409. }
  410. // Melt Quote Management
  411. async fn get_melt_quote(
  412. &self,
  413. quote_id: &str,
  414. ) -> Result<Option<cdk::wallet::MeltQuote>, Self::Err> {
  415. let result = self
  416. .ffi_db
  417. .get_melt_quote(quote_id.to_string())
  418. .await
  419. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  420. Ok(result
  421. .map(|q| {
  422. q.try_into()
  423. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  424. })
  425. .transpose()?)
  426. }
  427. async fn get_melt_quotes(&self) -> Result<Vec<cdk::wallet::MeltQuote>, Self::Err> {
  428. let result = self
  429. .ffi_db
  430. .get_melt_quotes()
  431. .await
  432. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  433. Ok(result
  434. .into_iter()
  435. .map(|q| {
  436. q.try_into()
  437. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  438. })
  439. .collect::<Result<Vec<_>, _>>()?)
  440. }
  441. // Keys Management
  442. async fn get_keys(&self, id: &cdk::nuts::Id) -> Result<Option<cdk::nuts::Keys>, Self::Err> {
  443. let ffi_id: Id = (*id).into();
  444. let result = self
  445. .ffi_db
  446. .get_keys(ffi_id)
  447. .await
  448. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  449. // Convert FFI Keys back to CDK Keys using TryFrom
  450. result
  451. .map(|ffi_keys| {
  452. ffi_keys
  453. .try_into()
  454. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  455. })
  456. .transpose()
  457. }
  458. // Proof Management
  459. async fn get_proofs(
  460. &self,
  461. mint_url: Option<cdk::mint_url::MintUrl>,
  462. unit: Option<cdk::nuts::CurrencyUnit>,
  463. state: Option<Vec<cdk::nuts::State>>,
  464. spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>>,
  465. ) -> Result<Vec<cdk::types::ProofInfo>, Self::Err> {
  466. let ffi_mint_url = mint_url.map(Into::into);
  467. let ffi_unit = unit.map(Into::into);
  468. let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
  469. let ffi_spending_conditions =
  470. spending_conditions.map(|sc| sc.into_iter().map(Into::into).collect());
  471. let result = self
  472. .ffi_db
  473. .get_proofs(ffi_mint_url, ffi_unit, ffi_state, ffi_spending_conditions)
  474. .await
  475. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  476. // Convert back to CDK ProofInfo
  477. let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
  478. .into_iter()
  479. .map(|info| {
  480. Ok(cdk::types::ProofInfo {
  481. proof: info.proof.try_into().map_err(|e: FfiError| {
  482. cdk::cdk_database::Error::Database(e.to_string().into())
  483. })?,
  484. y: info.y.try_into().map_err(|e: FfiError| {
  485. cdk::cdk_database::Error::Database(e.to_string().into())
  486. })?,
  487. mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
  488. cdk::cdk_database::Error::Database(e.to_string().into())
  489. })?,
  490. state: info.state.into(),
  491. spending_condition: info
  492. .spending_condition
  493. .map(|sc| sc.try_into())
  494. .transpose()
  495. .map_err(|e: FfiError| {
  496. cdk::cdk_database::Error::Database(e.to_string().into())
  497. })?,
  498. unit: info.unit.into(),
  499. })
  500. })
  501. .collect();
  502. cdk_result
  503. }
  504. async fn get_proofs_by_ys(
  505. &self,
  506. ys: Vec<cdk::nuts::PublicKey>,
  507. ) -> Result<Vec<cdk::types::ProofInfo>, Self::Err> {
  508. let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
  509. let result = self
  510. .ffi_db
  511. .get_proofs_by_ys(ffi_ys)
  512. .await
  513. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  514. // Convert back to CDK ProofInfo
  515. let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
  516. .into_iter()
  517. .map(|info| {
  518. Ok(cdk::types::ProofInfo {
  519. proof: info.proof.try_into().map_err(|e: FfiError| {
  520. cdk::cdk_database::Error::Database(e.to_string().into())
  521. })?,
  522. y: info.y.try_into().map_err(|e: FfiError| {
  523. cdk::cdk_database::Error::Database(e.to_string().into())
  524. })?,
  525. mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
  526. cdk::cdk_database::Error::Database(e.to_string().into())
  527. })?,
  528. state: info.state.into(),
  529. spending_condition: info
  530. .spending_condition
  531. .map(|sc| sc.try_into())
  532. .transpose()
  533. .map_err(|e: FfiError| {
  534. cdk::cdk_database::Error::Database(e.to_string().into())
  535. })?,
  536. unit: info.unit.into(),
  537. })
  538. })
  539. .collect();
  540. cdk_result
  541. }
  542. async fn get_balance(
  543. &self,
  544. mint_url: Option<cdk::mint_url::MintUrl>,
  545. unit: Option<cdk::nuts::CurrencyUnit>,
  546. state: Option<Vec<cdk::nuts::State>>,
  547. ) -> Result<u64, Self::Err> {
  548. let ffi_mint_url = mint_url.map(Into::into);
  549. let ffi_unit = unit.map(Into::into);
  550. let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
  551. self.ffi_db
  552. .get_balance(ffi_mint_url, ffi_unit, ffi_state)
  553. .await
  554. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  555. }
  556. // Transaction Management
  557. async fn get_transaction(
  558. &self,
  559. transaction_id: cdk::wallet::types::TransactionId,
  560. ) -> Result<Option<cdk::wallet::types::Transaction>, Self::Err> {
  561. let ffi_id = transaction_id.into();
  562. let result = self
  563. .ffi_db
  564. .get_transaction(ffi_id)
  565. .await
  566. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  567. result
  568. .map(|tx| tx.try_into())
  569. .transpose()
  570. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  571. }
  572. async fn list_transactions(
  573. &self,
  574. mint_url: Option<cdk::mint_url::MintUrl>,
  575. direction: Option<cdk::wallet::types::TransactionDirection>,
  576. unit: Option<cdk::nuts::CurrencyUnit>,
  577. ) -> Result<Vec<cdk::wallet::types::Transaction>, Self::Err> {
  578. let ffi_mint_url = mint_url.map(Into::into);
  579. let ffi_direction = direction.map(Into::into);
  580. let ffi_unit = unit.map(Into::into);
  581. let result = self
  582. .ffi_db
  583. .list_transactions(ffi_mint_url, ffi_direction, ffi_unit)
  584. .await
  585. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  586. result
  587. .into_iter()
  588. .map(|tx| tx.try_into())
  589. .collect::<Result<Vec<_>, FfiError>>()
  590. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  591. }
  592. async fn begin_db_transaction(
  593. &self,
  594. ) -> Result<Box<dyn CdkWalletDatabaseTransaction<Self::Err> + Send + Sync>, Self::Err> {
  595. let ffi_tx = self
  596. .ffi_db
  597. .begin_db_transaction()
  598. .await
  599. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  600. Ok(Box::new(WalletDatabaseTransactionBridge {
  601. ffi_tx,
  602. is_finalized: false,
  603. }))
  604. }
  605. }
  606. /// Transaction bridge for FFI wallet database
  607. struct WalletDatabaseTransactionBridge {
  608. ffi_tx: Arc<WalletDatabaseTransactionWrapper>,
  609. is_finalized: bool,
  610. }
  611. #[async_trait::async_trait]
  612. impl CdkWalletDatabaseTransaction<cdk::cdk_database::Error> for WalletDatabaseTransactionBridge {
  613. async fn add_mint(
  614. &mut self,
  615. mint_url: cdk::mint_url::MintUrl,
  616. mint_info: Option<cdk::nuts::MintInfo>,
  617. ) -> Result<(), cdk::cdk_database::Error> {
  618. let ffi_mint_url = mint_url.into();
  619. let ffi_mint_info = mint_info.map(Into::into);
  620. self.ffi_tx
  621. .add_mint(ffi_mint_url, ffi_mint_info)
  622. .await
  623. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  624. }
  625. async fn remove_mint(
  626. &mut self,
  627. mint_url: cdk::mint_url::MintUrl,
  628. ) -> Result<(), cdk::cdk_database::Error> {
  629. let ffi_mint_url = mint_url.into();
  630. self.ffi_tx
  631. .remove_mint(ffi_mint_url)
  632. .await
  633. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  634. }
  635. async fn update_mint_url(
  636. &mut self,
  637. old_mint_url: cdk::mint_url::MintUrl,
  638. new_mint_url: cdk::mint_url::MintUrl,
  639. ) -> Result<(), cdk::cdk_database::Error> {
  640. let ffi_old_mint_url = old_mint_url.into();
  641. let ffi_new_mint_url = new_mint_url.into();
  642. self.ffi_tx
  643. .update_mint_url(ffi_old_mint_url, ffi_new_mint_url)
  644. .await
  645. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  646. }
  647. async fn add_mint_keysets(
  648. &mut self,
  649. mint_url: cdk::mint_url::MintUrl,
  650. keysets: Vec<cdk::nuts::KeySetInfo>,
  651. ) -> Result<(), cdk::cdk_database::Error> {
  652. let ffi_mint_url = mint_url.into();
  653. let ffi_keysets: Vec<KeySetInfo> = keysets.into_iter().map(Into::into).collect();
  654. self.ffi_tx
  655. .add_mint_keysets(ffi_mint_url, ffi_keysets)
  656. .await
  657. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  658. }
  659. async fn add_mint_quote(
  660. &mut self,
  661. quote: cdk::wallet::MintQuote,
  662. ) -> Result<(), cdk::cdk_database::Error> {
  663. let ffi_quote = quote.into();
  664. self.ffi_tx
  665. .add_mint_quote(ffi_quote)
  666. .await
  667. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  668. }
  669. async fn remove_mint_quote(&mut self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
  670. self.ffi_tx
  671. .remove_mint_quote(quote_id.to_string())
  672. .await
  673. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  674. }
  675. async fn add_melt_quote(
  676. &mut self,
  677. quote: cdk::wallet::MeltQuote,
  678. ) -> Result<(), cdk::cdk_database::Error> {
  679. let ffi_quote = quote.into();
  680. self.ffi_tx
  681. .add_melt_quote(ffi_quote)
  682. .await
  683. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  684. }
  685. async fn remove_melt_quote(&mut self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
  686. self.ffi_tx
  687. .remove_melt_quote(quote_id.to_string())
  688. .await
  689. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  690. }
  691. async fn add_keys(
  692. &mut self,
  693. keyset: cdk::nuts::KeySet,
  694. ) -> Result<(), cdk::cdk_database::Error> {
  695. let ffi_keyset: KeySet = keyset.into();
  696. self.ffi_tx
  697. .add_keys(ffi_keyset)
  698. .await
  699. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  700. }
  701. async fn remove_keys(&mut self, id: &cdk::nuts::Id) -> Result<(), cdk::cdk_database::Error> {
  702. let ffi_id = (*id).into();
  703. self.ffi_tx
  704. .remove_keys(ffi_id)
  705. .await
  706. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  707. }
  708. async fn update_proofs(
  709. &mut self,
  710. added: Vec<cdk::types::ProofInfo>,
  711. removed_ys: Vec<cdk::nuts::PublicKey>,
  712. ) -> Result<(), cdk::cdk_database::Error> {
  713. let ffi_added: Vec<ProofInfo> = added.into_iter().map(Into::into).collect();
  714. let ffi_removed_ys: Vec<PublicKey> = removed_ys.into_iter().map(Into::into).collect();
  715. self.ffi_tx
  716. .update_proofs(ffi_added, ffi_removed_ys)
  717. .await
  718. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  719. }
  720. async fn update_proofs_state(
  721. &mut self,
  722. ys: Vec<cdk::nuts::PublicKey>,
  723. state: cdk::nuts::State,
  724. ) -> Result<(), cdk::cdk_database::Error> {
  725. let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
  726. let ffi_state = state.into();
  727. self.ffi_tx
  728. .update_proofs_state(ffi_ys, ffi_state)
  729. .await
  730. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  731. }
  732. async fn increment_keyset_counter(
  733. &mut self,
  734. keyset_id: &cdk::nuts::Id,
  735. count: u32,
  736. ) -> Result<u32, cdk::cdk_database::Error> {
  737. let ffi_id = (*keyset_id).into();
  738. self.ffi_tx
  739. .increment_keyset_counter(ffi_id, count)
  740. .await
  741. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  742. }
  743. async fn add_transaction(
  744. &mut self,
  745. transaction: cdk::wallet::types::Transaction,
  746. ) -> Result<(), cdk::cdk_database::Error> {
  747. let ffi_transaction = transaction.into();
  748. self.ffi_tx
  749. .add_transaction(ffi_transaction)
  750. .await
  751. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  752. }
  753. async fn remove_transaction(
  754. &mut self,
  755. transaction_id: cdk::wallet::types::TransactionId,
  756. ) -> Result<(), cdk::cdk_database::Error> {
  757. let ffi_id = transaction_id.into();
  758. self.ffi_tx
  759. .remove_transaction(ffi_id)
  760. .await
  761. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
  762. }
  763. // Read methods needed during transactions
  764. async fn get_keyset_by_id(
  765. &mut self,
  766. keyset_id: &cdk::nuts::Id,
  767. ) -> Result<Option<cdk::nuts::KeySetInfo>, cdk::cdk_database::Error> {
  768. let ffi_id = (*keyset_id).into();
  769. let result = self
  770. .ffi_tx
  771. .get_keyset_by_id(ffi_id)
  772. .await
  773. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  774. Ok(result.map(Into::into))
  775. }
  776. async fn get_keys(
  777. &mut self,
  778. id: &cdk::nuts::Id,
  779. ) -> Result<Option<cdk::nuts::Keys>, cdk::cdk_database::Error> {
  780. let ffi_id = (*id).into();
  781. let result = self
  782. .ffi_tx
  783. .get_keys(ffi_id)
  784. .await
  785. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  786. match result {
  787. Some(keys) => Ok(Some(keys.try_into().map_err(|e: FfiError| {
  788. cdk::cdk_database::Error::Database(e.to_string().into())
  789. })?)),
  790. None => Ok(None),
  791. }
  792. }
  793. async fn get_mint_quote(
  794. &mut self,
  795. quote_id: &str,
  796. ) -> Result<Option<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
  797. let result = self
  798. .ffi_tx
  799. .get_mint_quote(quote_id.to_string())
  800. .await
  801. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  802. Ok(result
  803. .map(|q| {
  804. q.try_into()
  805. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  806. })
  807. .transpose()?)
  808. }
  809. async fn get_melt_quote(
  810. &mut self,
  811. quote_id: &str,
  812. ) -> Result<Option<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
  813. let result = self
  814. .ffi_tx
  815. .get_melt_quote(quote_id.to_string())
  816. .await
  817. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  818. Ok(result
  819. .map(|q| {
  820. q.try_into()
  821. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  822. })
  823. .transpose()?)
  824. }
  825. async fn get_proofs(
  826. &mut self,
  827. mint_url: Option<cdk::mint_url::MintUrl>,
  828. unit: Option<cdk::nuts::CurrencyUnit>,
  829. state: Option<Vec<cdk::nuts::State>>,
  830. spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>>,
  831. ) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
  832. let ffi_mint_url = mint_url.map(Into::into);
  833. let ffi_unit = unit.map(Into::into);
  834. let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
  835. let ffi_spending_conditions =
  836. spending_conditions.map(|sc| sc.into_iter().map(Into::into).collect());
  837. let result = self
  838. .ffi_tx
  839. .get_proofs(ffi_mint_url, ffi_unit, ffi_state, ffi_spending_conditions)
  840. .await
  841. .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
  842. // Convert back to CDK ProofInfo
  843. let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
  844. .into_iter()
  845. .map(|info| {
  846. Ok(cdk::types::ProofInfo {
  847. proof: info.proof.try_into().map_err(|e: FfiError| {
  848. cdk::cdk_database::Error::Database(e.to_string().into())
  849. })?,
  850. y: info.y.try_into().map_err(|e: FfiError| {
  851. cdk::cdk_database::Error::Database(e.to_string().into())
  852. })?,
  853. mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
  854. cdk::cdk_database::Error::Database(e.to_string().into())
  855. })?,
  856. state: info.state.into(),
  857. spending_condition: info
  858. .spending_condition
  859. .map(|sc| sc.try_into())
  860. .transpose()
  861. .map_err(|e: FfiError| {
  862. cdk::cdk_database::Error::Database(e.to_string().into())
  863. })?,
  864. unit: info.unit.into(),
  865. })
  866. })
  867. .collect();
  868. cdk_result
  869. }
  870. }
  871. #[async_trait::async_trait]
  872. impl DbTransactionFinalizer for WalletDatabaseTransactionBridge {
  873. type Err = cdk::cdk_database::Error;
  874. async fn commit(mut self: Box<Self>) -> Result<(), cdk::cdk_database::Error> {
  875. self.is_finalized = true;
  876. let tx = self.ffi_tx.clone();
  877. tx.commit()
  878. .await
  879. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  880. }
  881. async fn rollback(mut self: Box<Self>) -> Result<(), cdk::cdk_database::Error> {
  882. self.is_finalized = true;
  883. let tx = self.ffi_tx.clone();
  884. tx.rollback()
  885. .await
  886. .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
  887. }
  888. }
  889. impl Drop for WalletDatabaseTransactionBridge {
  890. fn drop(&mut self) {
  891. if !self.is_finalized {
  892. let tx = self.ffi_tx.clone();
  893. spawn(async move {
  894. let _ = tx.rollback().await;
  895. });
  896. }
  897. }
  898. }
  899. pub(crate) struct FfiWalletSQLDatabase<RM>
  900. where
  901. RM: DatabasePool + 'static,
  902. {
  903. inner: SQLWalletDatabase<RM>,
  904. }
  905. impl<RM> FfiWalletSQLDatabase<RM>
  906. where
  907. RM: DatabasePool + 'static,
  908. {
  909. /// Creates a new instance
  910. pub fn new(inner: SQLWalletDatabase<RM>) -> Arc<Self> {
  911. Arc::new(Self { inner })
  912. }
  913. }
  914. /// Transaction wrapper for FFI
  915. pub(crate) struct FfiWalletTransaction {
  916. tx: Arc<Mutex<Option<DynWalletDatabaseTransaction>>>,
  917. is_finalized: AtomicBool,
  918. }
  919. impl Drop for FfiWalletTransaction {
  920. fn drop(&mut self) {
  921. if !self.is_finalized.load(std::sync::atomic::Ordering::SeqCst) {
  922. let tx = self.tx.clone();
  923. spawn(async move {
  924. if let Some(s) = tx.lock().await.take() {
  925. let _ = s.rollback().await;
  926. }
  927. });
  928. }
  929. }
  930. }
  931. impl FfiWalletTransaction {
  932. pub fn new(tx: DynWalletDatabaseTransaction) -> Arc<Self> {
  933. Arc::new(Self {
  934. tx: Arc::new(Mutex::new(Some(tx))),
  935. is_finalized: false.into(),
  936. })
  937. }
  938. }
  939. // Implement WalletDatabaseFfi trait - only read methods + begin_db_transaction
  940. #[async_trait::async_trait]
  941. impl<RM> WalletDatabase for FfiWalletSQLDatabase<RM>
  942. where
  943. RM: DatabasePool + 'static,
  944. {
  945. async fn begin_db_transaction(
  946. &self,
  947. ) -> Result<Arc<WalletDatabaseTransactionWrapper>, FfiError> {
  948. let tx = self
  949. .inner
  950. .begin_db_transaction()
  951. .await
  952. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  953. Ok(Arc::new(WalletDatabaseTransactionWrapper {
  954. inner: FfiWalletTransaction::new(tx),
  955. }))
  956. }
  957. async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError> {
  958. let cdk_ys: Vec<cdk::nuts::PublicKey> = ys
  959. .into_iter()
  960. .map(|y| y.try_into())
  961. .collect::<Result<Vec<_>, FfiError>>()?;
  962. let result = self
  963. .inner
  964. .get_proofs_by_ys(cdk_ys)
  965. .await
  966. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  967. Ok(result.into_iter().map(Into::into).collect())
  968. }
  969. async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
  970. let cdk_mint_url = mint_url.try_into()?;
  971. let result = self
  972. .inner
  973. .get_mint(cdk_mint_url)
  974. .await
  975. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  976. Ok(result.map(Into::into))
  977. }
  978. async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError> {
  979. let result = self
  980. .inner
  981. .get_mints()
  982. .await
  983. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  984. Ok(result
  985. .into_iter()
  986. .map(|(k, v)| (k.into(), v.map(Into::into)))
  987. .collect())
  988. }
  989. async fn get_mint_keysets(
  990. &self,
  991. mint_url: MintUrl,
  992. ) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
  993. let cdk_mint_url = mint_url.try_into()?;
  994. let result = self
  995. .inner
  996. .get_mint_keysets(cdk_mint_url)
  997. .await
  998. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  999. Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
  1000. }
  1001. async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
  1002. let cdk_id = keyset_id.into();
  1003. let result = self
  1004. .inner
  1005. .get_keyset_by_id(&cdk_id)
  1006. .await
  1007. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1008. Ok(result.map(Into::into))
  1009. }
  1010. async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
  1011. let result = self
  1012. .inner
  1013. .get_mint_quote(&quote_id)
  1014. .await
  1015. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1016. Ok(result.map(|q| q.into()))
  1017. }
  1018. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
  1019. let result = self
  1020. .inner
  1021. .get_mint_quotes()
  1022. .await
  1023. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1024. Ok(result.into_iter().map(|q| q.into()).collect())
  1025. }
  1026. async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
  1027. let result = self
  1028. .inner
  1029. .get_unissued_mint_quotes()
  1030. .await
  1031. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1032. Ok(result.into_iter().map(|q| q.into()).collect())
  1033. }
  1034. async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
  1035. let result = self
  1036. .inner
  1037. .get_melt_quote(&quote_id)
  1038. .await
  1039. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1040. Ok(result.map(|q| q.into()))
  1041. }
  1042. async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
  1043. let result = self
  1044. .inner
  1045. .get_melt_quotes()
  1046. .await
  1047. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1048. Ok(result.into_iter().map(|q| q.into()).collect())
  1049. }
  1050. async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
  1051. let cdk_id = id.into();
  1052. let result = self
  1053. .inner
  1054. .get_keys(&cdk_id)
  1055. .await
  1056. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1057. Ok(result.map(Into::into))
  1058. }
  1059. async fn get_proofs(
  1060. &self,
  1061. mint_url: Option<MintUrl>,
  1062. unit: Option<CurrencyUnit>,
  1063. state: Option<Vec<ProofState>>,
  1064. spending_conditions: Option<Vec<SpendingConditions>>,
  1065. ) -> Result<Vec<ProofInfo>, FfiError> {
  1066. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  1067. let cdk_unit = unit.map(Into::into);
  1068. let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
  1069. let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
  1070. spending_conditions
  1071. .map(|sc| {
  1072. sc.into_iter()
  1073. .map(|c| c.try_into())
  1074. .collect::<Result<Vec<_>, FfiError>>()
  1075. })
  1076. .transpose()?;
  1077. let result = self
  1078. .inner
  1079. .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
  1080. .await
  1081. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1082. Ok(result.into_iter().map(Into::into).collect())
  1083. }
  1084. async fn get_balance(
  1085. &self,
  1086. mint_url: Option<MintUrl>,
  1087. unit: Option<CurrencyUnit>,
  1088. state: Option<Vec<ProofState>>,
  1089. ) -> Result<u64, FfiError> {
  1090. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  1091. let cdk_unit = unit.map(Into::into);
  1092. let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
  1093. self.inner
  1094. .get_balance(cdk_mint_url, cdk_unit, cdk_state)
  1095. .await
  1096. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1097. }
  1098. async fn get_transaction(
  1099. &self,
  1100. transaction_id: TransactionId,
  1101. ) -> Result<Option<Transaction>, FfiError> {
  1102. let cdk_id = transaction_id.try_into()?;
  1103. let result = self
  1104. .inner
  1105. .get_transaction(cdk_id)
  1106. .await
  1107. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1108. Ok(result.map(Into::into))
  1109. }
  1110. async fn list_transactions(
  1111. &self,
  1112. mint_url: Option<MintUrl>,
  1113. direction: Option<TransactionDirection>,
  1114. unit: Option<CurrencyUnit>,
  1115. ) -> Result<Vec<Transaction>, FfiError> {
  1116. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  1117. let cdk_direction = direction.map(Into::into);
  1118. let cdk_unit = unit.map(Into::into);
  1119. let result = self
  1120. .inner
  1121. .list_transactions(cdk_mint_url, cdk_direction, cdk_unit)
  1122. .await
  1123. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1124. Ok(result.into_iter().map(Into::into).collect())
  1125. }
  1126. }
  1127. // Implement WalletDatabaseTransactionFfi trait - all write methods
  1128. #[async_trait::async_trait]
  1129. impl WalletDatabaseTransaction for FfiWalletTransaction {
  1130. async fn commit(self: Arc<Self>) -> Result<(), FfiError> {
  1131. self.is_finalized
  1132. .store(true, std::sync::atomic::Ordering::SeqCst);
  1133. self.tx
  1134. .lock()
  1135. .await
  1136. .take()
  1137. .ok_or(FfiError::Database {
  1138. msg: "Transaction already finalized".to_owned(),
  1139. })?
  1140. .commit()
  1141. .await
  1142. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1143. }
  1144. async fn rollback(self: Arc<Self>) -> Result<(), FfiError> {
  1145. self.is_finalized
  1146. .store(true, std::sync::atomic::Ordering::SeqCst);
  1147. self.tx
  1148. .lock()
  1149. .await
  1150. .take()
  1151. .ok_or(FfiError::Database {
  1152. msg: "Transaction already finalized".to_owned(),
  1153. })?
  1154. .rollback()
  1155. .await
  1156. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1157. }
  1158. async fn add_mint(
  1159. &self,
  1160. mint_url: MintUrl,
  1161. mint_info: Option<MintInfo>,
  1162. ) -> Result<(), FfiError> {
  1163. let mut tx_guard = self.tx.lock().await;
  1164. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1165. msg: "Transaction already finalized".to_owned(),
  1166. })?;
  1167. let cdk_mint_url = mint_url.try_into()?;
  1168. let cdk_mint_info = mint_info.map(Into::into);
  1169. tx.add_mint(cdk_mint_url, cdk_mint_info)
  1170. .await
  1171. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1172. }
  1173. async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
  1174. let mut tx_guard = self.tx.lock().await;
  1175. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1176. msg: "Transaction already finalized".to_owned(),
  1177. })?;
  1178. let cdk_mint_url = mint_url.try_into()?;
  1179. tx.remove_mint(cdk_mint_url)
  1180. .await
  1181. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1182. }
  1183. async fn update_mint_url(
  1184. &self,
  1185. old_mint_url: MintUrl,
  1186. new_mint_url: MintUrl,
  1187. ) -> Result<(), FfiError> {
  1188. let mut tx_guard = self.tx.lock().await;
  1189. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1190. msg: "Transaction already finalized".to_owned(),
  1191. })?;
  1192. let cdk_old_mint_url = old_mint_url.try_into()?;
  1193. let cdk_new_mint_url = new_mint_url.try_into()?;
  1194. tx.update_mint_url(cdk_old_mint_url, cdk_new_mint_url)
  1195. .await
  1196. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1197. }
  1198. async fn add_mint_keysets(
  1199. &self,
  1200. mint_url: MintUrl,
  1201. keysets: Vec<KeySetInfo>,
  1202. ) -> Result<(), FfiError> {
  1203. let mut tx_guard = self.tx.lock().await;
  1204. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1205. msg: "Transaction already finalized".to_owned(),
  1206. })?;
  1207. let cdk_mint_url = mint_url.try_into()?;
  1208. let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
  1209. tx.add_mint_keysets(cdk_mint_url, cdk_keysets)
  1210. .await
  1211. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1212. }
  1213. async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
  1214. let mut tx_guard = self.tx.lock().await;
  1215. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1216. msg: "Transaction already finalized".to_owned(),
  1217. })?;
  1218. let cdk_id = keyset_id.into();
  1219. let result = tx
  1220. .get_keyset_by_id(&cdk_id)
  1221. .await
  1222. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1223. Ok(result.map(Into::into))
  1224. }
  1225. async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
  1226. let mut tx_guard = self.tx.lock().await;
  1227. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1228. msg: "Transaction already finalized".to_owned(),
  1229. })?;
  1230. let cdk_id = id.into();
  1231. let result = tx
  1232. .get_keys(&cdk_id)
  1233. .await
  1234. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1235. Ok(result.map(Into::into))
  1236. }
  1237. async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
  1238. let mut tx_guard = self.tx.lock().await;
  1239. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1240. msg: "Transaction already finalized".to_owned(),
  1241. })?;
  1242. let result = tx
  1243. .get_mint_quote(&quote_id)
  1244. .await
  1245. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1246. Ok(result.map(|q| q.into()))
  1247. }
  1248. async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
  1249. let mut tx_guard = self.tx.lock().await;
  1250. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1251. msg: "Transaction already finalized".to_owned(),
  1252. })?;
  1253. let cdk_quote = quote.try_into()?;
  1254. tx.add_mint_quote(cdk_quote)
  1255. .await
  1256. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1257. }
  1258. async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
  1259. let mut tx_guard = self.tx.lock().await;
  1260. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1261. msg: "Transaction already finalized".to_owned(),
  1262. })?;
  1263. tx.remove_mint_quote(&quote_id)
  1264. .await
  1265. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1266. }
  1267. async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
  1268. let mut tx_guard = self.tx.lock().await;
  1269. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1270. msg: "Transaction already finalized".to_owned(),
  1271. })?;
  1272. let result = tx
  1273. .get_melt_quote(&quote_id)
  1274. .await
  1275. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1276. Ok(result.map(|q| q.into()))
  1277. }
  1278. async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
  1279. let mut tx_guard = self.tx.lock().await;
  1280. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1281. msg: "Transaction already finalized".to_owned(),
  1282. })?;
  1283. let cdk_quote = quote.try_into()?;
  1284. tx.add_melt_quote(cdk_quote)
  1285. .await
  1286. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1287. }
  1288. async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
  1289. let mut tx_guard = self.tx.lock().await;
  1290. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1291. msg: "Transaction already finalized".to_owned(),
  1292. })?;
  1293. tx.remove_melt_quote(&quote_id)
  1294. .await
  1295. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1296. }
  1297. async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
  1298. let mut tx_guard = self.tx.lock().await;
  1299. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1300. msg: "Transaction already finalized".to_owned(),
  1301. })?;
  1302. let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
  1303. tx.add_keys(cdk_keyset)
  1304. .await
  1305. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1306. }
  1307. async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
  1308. let mut tx_guard = self.tx.lock().await;
  1309. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1310. msg: "Transaction already finalized".to_owned(),
  1311. })?;
  1312. let cdk_id = id.into();
  1313. tx.remove_keys(&cdk_id)
  1314. .await
  1315. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1316. }
  1317. async fn get_proofs(
  1318. &self,
  1319. mint_url: Option<MintUrl>,
  1320. unit: Option<CurrencyUnit>,
  1321. state: Option<Vec<ProofState>>,
  1322. spending_conditions: Option<Vec<SpendingConditions>>,
  1323. ) -> Result<Vec<ProofInfo>, FfiError> {
  1324. let mut tx_guard = self.tx.lock().await;
  1325. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1326. msg: "Transaction already finalized".to_owned(),
  1327. })?;
  1328. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  1329. let cdk_unit = unit.map(Into::into);
  1330. let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
  1331. let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
  1332. spending_conditions
  1333. .map(|sc| {
  1334. sc.into_iter()
  1335. .map(|c| c.try_into())
  1336. .collect::<Result<Vec<_>, FfiError>>()
  1337. })
  1338. .transpose()?;
  1339. let result = tx
  1340. .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
  1341. .await
  1342. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  1343. Ok(result.into_iter().map(Into::into).collect())
  1344. }
  1345. async fn update_proofs(
  1346. &self,
  1347. added: Vec<ProofInfo>,
  1348. removed_ys: Vec<PublicKey>,
  1349. ) -> Result<(), FfiError> {
  1350. let mut tx_guard = self.tx.lock().await;
  1351. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1352. msg: "Transaction already finalized".to_owned(),
  1353. })?;
  1354. let cdk_added: Result<Vec<cdk::types::ProofInfo>, FfiError> = added
  1355. .into_iter()
  1356. .map(|info| {
  1357. Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
  1358. proof: info.proof.try_into()?,
  1359. y: info.y.try_into()?,
  1360. mint_url: info.mint_url.try_into()?,
  1361. state: info.state.into(),
  1362. spending_condition: info
  1363. .spending_condition
  1364. .map(|sc| sc.try_into())
  1365. .transpose()?,
  1366. unit: info.unit.into(),
  1367. })
  1368. })
  1369. .collect();
  1370. let cdk_added = cdk_added?;
  1371. let cdk_removed_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
  1372. removed_ys.into_iter().map(|pk| pk.try_into()).collect();
  1373. let cdk_removed_ys = cdk_removed_ys?;
  1374. tx.update_proofs(cdk_added, cdk_removed_ys)
  1375. .await
  1376. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1377. }
  1378. async fn update_proofs_state(
  1379. &self,
  1380. ys: Vec<PublicKey>,
  1381. state: ProofState,
  1382. ) -> Result<(), FfiError> {
  1383. let mut tx_guard = self.tx.lock().await;
  1384. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1385. msg: "Transaction already finalized".to_owned(),
  1386. })?;
  1387. let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
  1388. ys.into_iter().map(|pk| pk.try_into()).collect();
  1389. let cdk_ys = cdk_ys?;
  1390. let cdk_state = state.into();
  1391. tx.update_proofs_state(cdk_ys, cdk_state)
  1392. .await
  1393. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1394. }
  1395. async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
  1396. let mut tx_guard = self.tx.lock().await;
  1397. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1398. msg: "Transaction already finalized".to_owned(),
  1399. })?;
  1400. let cdk_id = keyset_id.into();
  1401. tx.increment_keyset_counter(&cdk_id, count)
  1402. .await
  1403. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1404. }
  1405. async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
  1406. let mut tx_guard = self.tx.lock().await;
  1407. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1408. msg: "Transaction already finalized".to_owned(),
  1409. })?;
  1410. let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
  1411. tx.add_transaction(cdk_transaction)
  1412. .await
  1413. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1414. }
  1415. async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
  1416. let mut tx_guard = self.tx.lock().await;
  1417. let tx = tx_guard.as_mut().ok_or(FfiError::Database {
  1418. msg: "Transaction already finalized".to_owned(),
  1419. })?;
  1420. let cdk_id = transaction_id.try_into()?;
  1421. tx.remove_transaction(cdk_id)
  1422. .await
  1423. .map_err(|e| FfiError::Database { msg: e.to_string() })
  1424. }
  1425. }
  1426. /// FFI-safe database type enum
  1427. #[derive(uniffi::Enum, Clone)]
  1428. pub enum WalletDbBackend {
  1429. Sqlite {
  1430. path: String,
  1431. },
  1432. #[cfg(feature = "postgres")]
  1433. Postgres {
  1434. url: String,
  1435. },
  1436. }
  1437. /// Factory helpers returning a CDK wallet database behind the FFI trait
  1438. #[uniffi::export]
  1439. pub fn create_wallet_db(backend: WalletDbBackend) -> Result<Arc<dyn WalletDatabase>, FfiError> {
  1440. match backend {
  1441. WalletDbBackend::Sqlite { path } => {
  1442. let sqlite = WalletSqliteDatabase::new(path)?;
  1443. Ok(sqlite as Arc<dyn WalletDatabase>)
  1444. }
  1445. #[cfg(feature = "postgres")]
  1446. WalletDbBackend::Postgres { url } => {
  1447. let pg = WalletPostgresDatabase::new(url)?;
  1448. Ok(pg as Arc<dyn WalletDatabase>)
  1449. }
  1450. }
  1451. }
  1452. /// Helper function to create a CDK database from the FFI trait
  1453. pub fn create_cdk_database_from_ffi(
  1454. ffi_db: Arc<dyn WalletDatabase>,
  1455. ) -> Arc<dyn CdkWalletDatabase<Err = cdk::cdk_database::Error> + Send + Sync> {
  1456. Arc::new(WalletDatabaseBridge::new(ffi_db))
  1457. }