database.rs 51 KB

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