sqlite.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. use std::collections::HashMap;
  2. use std::sync::Arc;
  3. use cdk_sqlite::wallet::WalletSqliteDatabase as CdkWalletSqliteDatabase;
  4. use crate::{
  5. CurrencyUnit, FfiError, Id, KeySet, KeySetInfo, Keys, MeltQuote, MintInfo, MintQuote, MintUrl,
  6. ProofInfo, ProofState, PublicKey, SpendingConditions, Transaction, TransactionDirection,
  7. TransactionId, WalletDatabase,
  8. };
  9. /// FFI-compatible WalletSqliteDatabase implementation that implements the WalletDatabase trait
  10. #[derive(uniffi::Object)]
  11. pub struct WalletSqliteDatabase {
  12. inner: Arc<CdkWalletSqliteDatabase>,
  13. }
  14. use cdk::cdk_database::WalletDatabase as CdkWalletDatabase;
  15. impl WalletSqliteDatabase {
  16. // No additional methods needed beyond the trait implementation
  17. }
  18. #[uniffi::export]
  19. impl WalletSqliteDatabase {
  20. /// Create a new WalletSqliteDatabase with the given work directory
  21. #[uniffi::constructor]
  22. pub fn new(file_path: String) -> Result<Arc<Self>, FfiError> {
  23. let db = match tokio::runtime::Handle::try_current() {
  24. Ok(handle) => tokio::task::block_in_place(|| {
  25. handle
  26. .block_on(async move { CdkWalletSqliteDatabase::new(file_path.as_str()).await })
  27. }),
  28. Err(_) => {
  29. // No current runtime, create a new one
  30. tokio::runtime::Runtime::new()
  31. .map_err(|e| FfiError::Database {
  32. msg: format!("Failed to create runtime: {}", e),
  33. })?
  34. .block_on(async move { CdkWalletSqliteDatabase::new(file_path.as_str()).await })
  35. }
  36. }
  37. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  38. Ok(Arc::new(Self {
  39. inner: Arc::new(db),
  40. }))
  41. }
  42. /// Create an in-memory database
  43. #[uniffi::constructor]
  44. pub fn new_in_memory() -> Result<Arc<Self>, FfiError> {
  45. let db = match tokio::runtime::Handle::try_current() {
  46. Ok(handle) => tokio::task::block_in_place(|| {
  47. handle.block_on(async move { cdk_sqlite::wallet::memory::empty().await })
  48. }),
  49. Err(_) => {
  50. // No current runtime, create a new one
  51. tokio::runtime::Runtime::new()
  52. .map_err(|e| FfiError::Database {
  53. msg: format!("Failed to create runtime: {}", e),
  54. })?
  55. .block_on(async move { cdk_sqlite::wallet::memory::empty().await })
  56. }
  57. }
  58. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  59. Ok(Arc::new(Self {
  60. inner: Arc::new(db),
  61. }))
  62. }
  63. }
  64. #[uniffi::export(async_runtime = "tokio")]
  65. #[async_trait::async_trait]
  66. impl WalletDatabase for WalletSqliteDatabase {
  67. // Mint Management
  68. async fn add_mint(
  69. &self,
  70. mint_url: MintUrl,
  71. mint_info: Option<MintInfo>,
  72. ) -> Result<(), FfiError> {
  73. let cdk_mint_url = mint_url.try_into()?;
  74. let cdk_mint_info = mint_info.map(Into::into);
  75. self.inner
  76. .add_mint(cdk_mint_url, cdk_mint_info)
  77. .await
  78. .map_err(|e| FfiError::Database { msg: e.to_string() })
  79. }
  80. async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
  81. let cdk_mint_url = mint_url.try_into()?;
  82. self.inner
  83. .remove_mint(cdk_mint_url)
  84. .await
  85. .map_err(|e| FfiError::Database { msg: e.to_string() })
  86. }
  87. async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
  88. let cdk_mint_url = mint_url.try_into()?;
  89. let result = self
  90. .inner
  91. .get_mint(cdk_mint_url)
  92. .await
  93. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  94. Ok(result.map(Into::into))
  95. }
  96. async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError> {
  97. let result = self
  98. .inner
  99. .get_mints()
  100. .await
  101. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  102. Ok(result
  103. .into_iter()
  104. .map(|(k, v)| (k.into(), v.map(Into::into)))
  105. .collect())
  106. }
  107. async fn update_mint_url(
  108. &self,
  109. old_mint_url: MintUrl,
  110. new_mint_url: MintUrl,
  111. ) -> Result<(), FfiError> {
  112. let cdk_old_mint_url = old_mint_url.try_into()?;
  113. let cdk_new_mint_url = new_mint_url.try_into()?;
  114. self.inner
  115. .update_mint_url(cdk_old_mint_url, cdk_new_mint_url)
  116. .await
  117. .map_err(|e| FfiError::Database { msg: e.to_string() })
  118. }
  119. // Keyset Management
  120. async fn add_mint_keysets(
  121. &self,
  122. mint_url: MintUrl,
  123. keysets: Vec<KeySetInfo>,
  124. ) -> Result<(), FfiError> {
  125. let cdk_mint_url = mint_url.try_into()?;
  126. let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
  127. self.inner
  128. .add_mint_keysets(cdk_mint_url, cdk_keysets)
  129. .await
  130. .map_err(|e| FfiError::Database { msg: e.to_string() })
  131. }
  132. async fn get_mint_keysets(
  133. &self,
  134. mint_url: MintUrl,
  135. ) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
  136. let cdk_mint_url = mint_url.try_into()?;
  137. let result = self
  138. .inner
  139. .get_mint_keysets(cdk_mint_url)
  140. .await
  141. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  142. Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
  143. }
  144. async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
  145. let cdk_id = keyset_id.into();
  146. let result = self
  147. .inner
  148. .get_keyset_by_id(&cdk_id)
  149. .await
  150. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  151. Ok(result.map(Into::into))
  152. }
  153. // Mint Quote Management
  154. async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
  155. let cdk_quote = quote.try_into()?;
  156. self.inner
  157. .add_mint_quote(cdk_quote)
  158. .await
  159. .map_err(|e| FfiError::Database { msg: e.to_string() })
  160. }
  161. async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
  162. let result = self
  163. .inner
  164. .get_mint_quote(&quote_id)
  165. .await
  166. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  167. Ok(result.map(|q| q.into()))
  168. }
  169. async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
  170. let result = self
  171. .inner
  172. .get_mint_quotes()
  173. .await
  174. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  175. Ok(result.into_iter().map(|q| q.into()).collect())
  176. }
  177. async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
  178. self.inner
  179. .remove_mint_quote(&quote_id)
  180. .await
  181. .map_err(|e| FfiError::Database { msg: e.to_string() })
  182. }
  183. // Melt Quote Management
  184. async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
  185. let cdk_quote = quote.try_into()?;
  186. self.inner
  187. .add_melt_quote(cdk_quote)
  188. .await
  189. .map_err(|e| FfiError::Database { msg: e.to_string() })
  190. }
  191. async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
  192. let result = self
  193. .inner
  194. .get_melt_quote(&quote_id)
  195. .await
  196. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  197. Ok(result.map(|q| q.into()))
  198. }
  199. async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
  200. let result = self
  201. .inner
  202. .get_melt_quotes()
  203. .await
  204. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  205. Ok(result.into_iter().map(|q| q.into()).collect())
  206. }
  207. async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
  208. self.inner
  209. .remove_melt_quote(&quote_id)
  210. .await
  211. .map_err(|e| FfiError::Database { msg: e.to_string() })
  212. }
  213. // Keys Management
  214. async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
  215. // Convert FFI KeySet to cdk::nuts::KeySet
  216. let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
  217. self.inner
  218. .add_keys(cdk_keyset)
  219. .await
  220. .map_err(|e| FfiError::Database { msg: e.to_string() })
  221. }
  222. async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
  223. let cdk_id = id.into();
  224. let result = self
  225. .inner
  226. .get_keys(&cdk_id)
  227. .await
  228. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  229. Ok(result.map(Into::into))
  230. }
  231. async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
  232. let cdk_id = id.into();
  233. self.inner
  234. .remove_keys(&cdk_id)
  235. .await
  236. .map_err(|e| FfiError::Database { msg: e.to_string() })
  237. }
  238. // Proof Management
  239. async fn update_proofs(
  240. &self,
  241. added: Vec<ProofInfo>,
  242. removed_ys: Vec<PublicKey>,
  243. ) -> Result<(), FfiError> {
  244. // Convert FFI types to CDK types
  245. let cdk_added: Result<Vec<cdk::types::ProofInfo>, FfiError> = added
  246. .into_iter()
  247. .map(|info| {
  248. Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
  249. proof: info.proof.inner.clone(),
  250. y: info.y.try_into()?,
  251. mint_url: info.mint_url.try_into()?,
  252. state: info.state.into(),
  253. spending_condition: info
  254. .spending_condition
  255. .map(|sc| sc.try_into())
  256. .transpose()?,
  257. unit: info.unit.into(),
  258. })
  259. })
  260. .collect();
  261. let cdk_added = cdk_added?;
  262. let cdk_removed_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
  263. removed_ys.into_iter().map(|pk| pk.try_into()).collect();
  264. let cdk_removed_ys = cdk_removed_ys?;
  265. self.inner
  266. .update_proofs(cdk_added, cdk_removed_ys)
  267. .await
  268. .map_err(|e| FfiError::Database { msg: e.to_string() })
  269. }
  270. async fn get_proofs(
  271. &self,
  272. mint_url: Option<MintUrl>,
  273. unit: Option<CurrencyUnit>,
  274. state: Option<Vec<ProofState>>,
  275. spending_conditions: Option<Vec<SpendingConditions>>,
  276. ) -> Result<Vec<ProofInfo>, FfiError> {
  277. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  278. let cdk_unit = unit.map(Into::into);
  279. let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
  280. let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
  281. spending_conditions
  282. .map(|sc| {
  283. sc.into_iter()
  284. .map(|c| c.try_into())
  285. .collect::<Result<Vec<_>, FfiError>>()
  286. })
  287. .transpose()?;
  288. let result = self
  289. .inner
  290. .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
  291. .await
  292. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  293. Ok(result.into_iter().map(Into::into).collect())
  294. }
  295. async fn get_balance(
  296. &self,
  297. mint_url: Option<MintUrl>,
  298. unit: Option<CurrencyUnit>,
  299. state: Option<Vec<ProofState>>,
  300. ) -> Result<u64, FfiError> {
  301. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  302. let cdk_unit = unit.map(Into::into);
  303. let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
  304. self.inner
  305. .get_balance(cdk_mint_url, cdk_unit, cdk_state)
  306. .await
  307. .map_err(|e| FfiError::Database { msg: e.to_string() })
  308. }
  309. async fn update_proofs_state(
  310. &self,
  311. ys: Vec<PublicKey>,
  312. state: ProofState,
  313. ) -> Result<(), FfiError> {
  314. let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
  315. ys.into_iter().map(|pk| pk.try_into()).collect();
  316. let cdk_ys = cdk_ys?;
  317. let cdk_state = state.into();
  318. self.inner
  319. .update_proofs_state(cdk_ys, cdk_state)
  320. .await
  321. .map_err(|e| FfiError::Database { msg: e.to_string() })
  322. }
  323. // Keyset Counter Management
  324. async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
  325. let cdk_id = keyset_id.into();
  326. self.inner
  327. .increment_keyset_counter(&cdk_id, count)
  328. .await
  329. .map_err(|e| FfiError::Database { msg: e.to_string() })
  330. }
  331. // Transaction Management
  332. async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
  333. // Convert FFI Transaction to CDK Transaction using TryFrom
  334. let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
  335. self.inner
  336. .add_transaction(cdk_transaction)
  337. .await
  338. .map_err(|e| FfiError::Database { msg: e.to_string() })
  339. }
  340. async fn get_transaction(
  341. &self,
  342. transaction_id: TransactionId,
  343. ) -> Result<Option<Transaction>, FfiError> {
  344. let cdk_id = transaction_id.try_into()?;
  345. let result = self
  346. .inner
  347. .get_transaction(cdk_id)
  348. .await
  349. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  350. Ok(result.map(Into::into))
  351. }
  352. async fn list_transactions(
  353. &self,
  354. mint_url: Option<MintUrl>,
  355. direction: Option<TransactionDirection>,
  356. unit: Option<CurrencyUnit>,
  357. ) -> Result<Vec<Transaction>, FfiError> {
  358. let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
  359. let cdk_direction = direction.map(Into::into);
  360. let cdk_unit = unit.map(Into::into);
  361. let result = self
  362. .inner
  363. .list_transactions(cdk_mint_url, cdk_direction, cdk_unit)
  364. .await
  365. .map_err(|e| FfiError::Database { msg: e.to_string() })?;
  366. Ok(result.into_iter().map(Into::into).collect())
  367. }
  368. async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
  369. let cdk_id = transaction_id.try_into()?;
  370. self.inner
  371. .remove_transaction(cdk_id)
  372. .await
  373. .map_err(|e| FfiError::Database { msg: e.to_string() })
  374. }
  375. }