| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646 |
- //! FFI Database bindings
- use std::collections::HashMap;
- use std::sync::atomic::AtomicBool;
- use std::sync::Arc;
- use cdk_common::database::{
- DbTransactionFinalizer, DynWalletDatabaseTransaction, WalletDatabase as CdkWalletDatabase,
- WalletDatabaseTransaction as CdkWalletDatabaseTransaction,
- };
- use cdk_common::task::spawn;
- use cdk_sql_common::pool::DatabasePool;
- use cdk_sql_common::SQLWalletDatabase;
- use tokio::sync::Mutex;
- use crate::error::FfiError;
- #[cfg(feature = "postgres")]
- use crate::postgres::WalletPostgresDatabase;
- use crate::sqlite::WalletSqliteDatabase;
- use crate::types::*;
- /// FFI-compatible wallet database trait (read-only operations + begin_db_transaction)
- /// This trait mirrors the CDK WalletDatabase trait structure
- #[uniffi::export]
- #[async_trait::async_trait]
- pub trait WalletDatabase: Send + Sync {
- /// Begin a database transaction
- async fn begin_db_transaction(&self)
- -> Result<Arc<WalletDatabaseTransactionWrapper>, FfiError>;
- /// Get mint from storage
- async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError>;
- /// Get all mints from storage
- async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError>;
- /// Get mint keysets for mint url
- async fn get_mint_keysets(
- &self,
- mint_url: MintUrl,
- ) -> Result<Option<Vec<KeySetInfo>>, FfiError>;
- /// Get mint keyset by id
- async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError>;
- /// Get mint quote from storage
- async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError>;
- /// Get mint quotes from storage
- async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
- /// Get unissued mint quotes from storage
- /// Returns bolt11 quotes where nothing has been issued yet (amount_issued = 0) and all bolt12 quotes.
- async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError>;
- /// Get melt quote from storage
- async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError>;
- /// Get melt quotes from storage
- async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError>;
- /// Get Keys from storage
- async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError>;
- /// Get proofs from storage
- async fn get_proofs(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- spending_conditions: Option<Vec<SpendingConditions>>,
- ) -> Result<Vec<ProofInfo>, FfiError>;
- /// Get proofs by Y values
- async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError>;
- /// Get balance efficiently using SQL aggregation
- async fn get_balance(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- ) -> Result<u64, FfiError>;
- /// Get transaction from storage
- async fn get_transaction(
- &self,
- transaction_id: TransactionId,
- ) -> Result<Option<Transaction>, FfiError>;
- /// List transactions from storage
- async fn list_transactions(
- &self,
- mint_url: Option<MintUrl>,
- direction: Option<TransactionDirection>,
- unit: Option<CurrencyUnit>,
- ) -> Result<Vec<Transaction>, FfiError>;
- }
- /// FFI-compatible transaction trait for wallet database write operations
- /// This trait mirrors the CDK WalletDatabaseTransaction trait but uses FFI-compatible types
- #[uniffi::export(with_foreign)]
- #[async_trait::async_trait]
- pub trait WalletDatabaseTransaction: Send + Sync {
- /// Commit the transaction
- async fn commit(self: Arc<Self>) -> Result<(), FfiError>;
- /// Rollback the transaction
- async fn rollback(self: Arc<Self>) -> Result<(), FfiError>;
- // Mint Management
- /// Add Mint to storage
- async fn add_mint(
- &self,
- mint_url: MintUrl,
- mint_info: Option<MintInfo>,
- ) -> Result<(), FfiError>;
- /// Remove Mint from storage
- async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError>;
- /// Update mint url
- async fn update_mint_url(
- &self,
- old_mint_url: MintUrl,
- new_mint_url: MintUrl,
- ) -> Result<(), FfiError>;
- // Keyset Management
- /// Add mint keyset to storage
- async fn add_mint_keysets(
- &self,
- mint_url: MintUrl,
- keysets: Vec<KeySetInfo>,
- ) -> Result<(), FfiError>;
- /// Get mint keyset by id (transaction-scoped)
- async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError>;
- /// Get Keys from storage (transaction-scoped)
- async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError>;
- // Mint Quote Management
- /// Get mint quote from storage (transaction-scoped, with locking)
- async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError>;
- /// Add mint quote to storage
- async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError>;
- /// Remove mint quote from storage
- async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError>;
- // Melt Quote Management
- /// Get melt quote from storage (transaction-scoped)
- async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError>;
- /// Add melt quote to storage
- async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError>;
- /// Remove melt quote from storage
- async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError>;
- // Keys Management
- /// Add Keys to storage
- async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError>;
- /// Remove Keys from storage
- async fn remove_keys(&self, id: Id) -> Result<(), FfiError>;
- // Proof Management
- /// Get proofs from storage (transaction-scoped, with locking)
- async fn get_proofs(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- spending_conditions: Option<Vec<SpendingConditions>>,
- ) -> Result<Vec<ProofInfo>, FfiError>;
- /// Update the proofs in storage by adding new proofs or removing proofs by their Y value
- async fn update_proofs(
- &self,
- added: Vec<ProofInfo>,
- removed_ys: Vec<PublicKey>,
- ) -> Result<(), FfiError>;
- /// Update proofs state in storage
- async fn update_proofs_state(
- &self,
- ys: Vec<PublicKey>,
- state: ProofState,
- ) -> Result<(), FfiError>;
- // Keyset Counter Management
- /// Increment Keyset counter
- async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError>;
- // Transaction Management
- /// Add transaction to storage
- async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError>;
- /// Remove transaction from storage
- async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError>;
- }
- /// Wallet database transaction wrapper
- #[derive(uniffi::Object)]
- pub struct WalletDatabaseTransactionWrapper {
- inner: Arc<dyn WalletDatabaseTransaction>,
- }
- #[uniffi::export(async_runtime = "tokio")]
- impl WalletDatabaseTransactionWrapper {
- /// Commit the transaction
- pub async fn commit(&self) -> Result<(), FfiError> {
- self.inner.clone().commit().await
- }
- /// Rollback the transaction
- pub async fn rollback(&self) -> Result<(), FfiError> {
- self.inner.clone().rollback().await
- }
- /// Add Mint to storage
- pub async fn add_mint(
- &self,
- mint_url: MintUrl,
- mint_info: Option<MintInfo>,
- ) -> Result<(), FfiError> {
- self.inner.add_mint(mint_url, mint_info).await
- }
- /// Remove Mint from storage
- pub async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
- self.inner.remove_mint(mint_url).await
- }
- /// Update mint url
- pub async fn update_mint_url(
- &self,
- old_mint_url: MintUrl,
- new_mint_url: MintUrl,
- ) -> Result<(), FfiError> {
- self.inner.update_mint_url(old_mint_url, new_mint_url).await
- }
- /// Add mint keyset to storage
- pub async fn add_mint_keysets(
- &self,
- mint_url: MintUrl,
- keysets: Vec<KeySetInfo>,
- ) -> Result<(), FfiError> {
- self.inner.add_mint_keysets(mint_url, keysets).await
- }
- /// Get mint keyset by id (transaction-scoped)
- pub async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
- self.inner.get_keyset_by_id(keyset_id).await
- }
- /// Get Keys from storage (transaction-scoped)
- pub async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
- self.inner.get_keys(id).await
- }
- /// Get mint quote from storage (transaction-scoped, with locking)
- pub async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
- self.inner.get_mint_quote(quote_id).await
- }
- /// Add mint quote to storage
- pub async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
- self.inner.add_mint_quote(quote).await
- }
- /// Remove mint quote from storage
- pub async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
- self.inner.remove_mint_quote(quote_id).await
- }
- /// Get melt quote from storage (transaction-scoped)
- pub async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
- self.inner.get_melt_quote(quote_id).await
- }
- /// Add melt quote to storage
- pub async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
- self.inner.add_melt_quote(quote).await
- }
- /// Remove melt quote from storage
- pub async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
- self.inner.remove_melt_quote(quote_id).await
- }
- /// Add Keys to storage
- pub async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
- self.inner.add_keys(keyset).await
- }
- /// Remove Keys from storage
- pub async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
- self.inner.remove_keys(id).await
- }
- /// Get proofs from storage (transaction-scoped, with locking)
- pub async fn get_proofs(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- spending_conditions: Option<Vec<SpendingConditions>>,
- ) -> Result<Vec<ProofInfo>, FfiError> {
- self.inner
- .get_proofs(mint_url, unit, state, spending_conditions)
- .await
- }
- /// Update the proofs in storage by adding new proofs or removing proofs by their Y value
- pub async fn update_proofs(
- &self,
- added: Vec<ProofInfo>,
- removed_ys: Vec<PublicKey>,
- ) -> Result<(), FfiError> {
- self.inner.update_proofs(added, removed_ys).await
- }
- /// Update proofs state in storage
- pub async fn update_proofs_state(
- &self,
- ys: Vec<PublicKey>,
- state: ProofState,
- ) -> Result<(), FfiError> {
- self.inner.update_proofs_state(ys, state).await
- }
- /// Increment Keyset counter
- pub async fn increment_keyset_counter(
- &self,
- keyset_id: Id,
- count: u32,
- ) -> Result<u32, FfiError> {
- self.inner.increment_keyset_counter(keyset_id, count).await
- }
- /// Add transaction to storage
- pub async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
- self.inner.add_transaction(transaction).await
- }
- /// Remove transaction from storage
- pub async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
- self.inner.remove_transaction(transaction_id).await
- }
- }
- /// Internal bridge trait to convert from the FFI trait to the CDK database trait
- /// This allows us to bridge between the UniFFI trait and the CDK's internal database trait
- struct WalletDatabaseBridge {
- ffi_db: Arc<dyn WalletDatabase>,
- }
- impl WalletDatabaseBridge {
- fn new(ffi_db: Arc<dyn WalletDatabase>) -> Self {
- Self { ffi_db }
- }
- }
- impl std::fmt::Debug for WalletDatabaseBridge {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "WalletDatabaseBridge")
- }
- }
- #[async_trait::async_trait]
- impl CdkWalletDatabase for WalletDatabaseBridge {
- type Err = cdk::cdk_database::Error;
- // Mint Management
- async fn get_mint(
- &self,
- mint_url: cdk::mint_url::MintUrl,
- ) -> Result<Option<cdk::nuts::MintInfo>, Self::Err> {
- let ffi_mint_url = mint_url.into();
- let result = self
- .ffi_db
- .get_mint(ffi_mint_url)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result.map(Into::into))
- }
- async fn get_mints(
- &self,
- ) -> Result<HashMap<cdk::mint_url::MintUrl, Option<cdk::nuts::MintInfo>>, Self::Err> {
- let result = self
- .ffi_db
- .get_mints()
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- let mut cdk_result = HashMap::new();
- for (ffi_mint_url, mint_info_opt) in result {
- let cdk_url = ffi_mint_url
- .try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- cdk_result.insert(cdk_url, mint_info_opt.map(Into::into));
- }
- Ok(cdk_result)
- }
- // Keyset Management
- async fn get_mint_keysets(
- &self,
- mint_url: cdk::mint_url::MintUrl,
- ) -> Result<Option<Vec<cdk::nuts::KeySetInfo>>, Self::Err> {
- let ffi_mint_url = mint_url.into();
- let result = self
- .ffi_db
- .get_mint_keysets(ffi_mint_url)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
- }
- async fn get_keyset_by_id(
- &self,
- keyset_id: &cdk::nuts::Id,
- ) -> Result<Option<cdk::nuts::KeySetInfo>, Self::Err> {
- let ffi_id = (*keyset_id).into();
- let result = self
- .ffi_db
- .get_keyset_by_id(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result.map(Into::into))
- }
- // Mint Quote Management
- async fn get_mint_quote(
- &self,
- quote_id: &str,
- ) -> Result<Option<cdk::wallet::MintQuote>, Self::Err> {
- let result = self
- .ffi_db
- .get_mint_quote(quote_id.to_string())
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .transpose()?)
- }
- async fn get_mint_quotes(&self) -> Result<Vec<cdk::wallet::MintQuote>, Self::Err> {
- let result = self
- .ffi_db
- .get_mint_quotes()
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .into_iter()
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .collect::<Result<Vec<_>, _>>()?)
- }
- async fn get_unissued_mint_quotes(&self) -> Result<Vec<cdk::wallet::MintQuote>, Self::Err> {
- let result = self
- .ffi_db
- .get_unissued_mint_quotes()
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .into_iter()
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .collect::<Result<Vec<_>, _>>()?)
- }
- // Melt Quote Management
- async fn get_melt_quote(
- &self,
- quote_id: &str,
- ) -> Result<Option<cdk::wallet::MeltQuote>, Self::Err> {
- let result = self
- .ffi_db
- .get_melt_quote(quote_id.to_string())
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .transpose()?)
- }
- async fn get_melt_quotes(&self) -> Result<Vec<cdk::wallet::MeltQuote>, Self::Err> {
- let result = self
- .ffi_db
- .get_melt_quotes()
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .into_iter()
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .collect::<Result<Vec<_>, _>>()?)
- }
- // Keys Management
- async fn get_keys(&self, id: &cdk::nuts::Id) -> Result<Option<cdk::nuts::Keys>, Self::Err> {
- let ffi_id: Id = (*id).into();
- let result = self
- .ffi_db
- .get_keys(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- // Convert FFI Keys back to CDK Keys using TryFrom
- result
- .map(|ffi_keys| {
- ffi_keys
- .try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .transpose()
- }
- // Proof Management
- async fn get_proofs(
- &self,
- mint_url: Option<cdk::mint_url::MintUrl>,
- unit: Option<cdk::nuts::CurrencyUnit>,
- state: Option<Vec<cdk::nuts::State>>,
- spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>>,
- ) -> Result<Vec<cdk::types::ProofInfo>, Self::Err> {
- let ffi_mint_url = mint_url.map(Into::into);
- let ffi_unit = unit.map(Into::into);
- let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
- let ffi_spending_conditions =
- spending_conditions.map(|sc| sc.into_iter().map(Into::into).collect());
- let result = self
- .ffi_db
- .get_proofs(ffi_mint_url, ffi_unit, ffi_state, ffi_spending_conditions)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- // Convert back to CDK ProofInfo
- let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
- .into_iter()
- .map(|info| {
- Ok(cdk::types::ProofInfo {
- proof: info.proof.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- y: info.y.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- state: info.state.into(),
- spending_condition: info
- .spending_condition
- .map(|sc| sc.try_into())
- .transpose()
- .map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- unit: info.unit.into(),
- })
- })
- .collect();
- cdk_result
- }
- async fn get_proofs_by_ys(
- &self,
- ys: Vec<cdk::nuts::PublicKey>,
- ) -> Result<Vec<cdk::types::ProofInfo>, Self::Err> {
- let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
- let result = self
- .ffi_db
- .get_proofs_by_ys(ffi_ys)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- // Convert back to CDK ProofInfo
- let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
- .into_iter()
- .map(|info| {
- Ok(cdk::types::ProofInfo {
- proof: info.proof.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- y: info.y.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- state: info.state.into(),
- spending_condition: info
- .spending_condition
- .map(|sc| sc.try_into())
- .transpose()
- .map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- unit: info.unit.into(),
- })
- })
- .collect();
- cdk_result
- }
- async fn get_balance(
- &self,
- mint_url: Option<cdk::mint_url::MintUrl>,
- unit: Option<cdk::nuts::CurrencyUnit>,
- state: Option<Vec<cdk::nuts::State>>,
- ) -> Result<u64, Self::Err> {
- let ffi_mint_url = mint_url.map(Into::into);
- let ffi_unit = unit.map(Into::into);
- let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
- self.ffi_db
- .get_balance(ffi_mint_url, ffi_unit, ffi_state)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- // Transaction Management
- async fn get_transaction(
- &self,
- transaction_id: cdk::wallet::types::TransactionId,
- ) -> Result<Option<cdk::wallet::types::Transaction>, Self::Err> {
- let ffi_id = transaction_id.into();
- let result = self
- .ffi_db
- .get_transaction(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- result
- .map(|tx| tx.try_into())
- .transpose()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn list_transactions(
- &self,
- mint_url: Option<cdk::mint_url::MintUrl>,
- direction: Option<cdk::wallet::types::TransactionDirection>,
- unit: Option<cdk::nuts::CurrencyUnit>,
- ) -> Result<Vec<cdk::wallet::types::Transaction>, Self::Err> {
- let ffi_mint_url = mint_url.map(Into::into);
- let ffi_direction = direction.map(Into::into);
- let ffi_unit = unit.map(Into::into);
- let result = self
- .ffi_db
- .list_transactions(ffi_mint_url, ffi_direction, ffi_unit)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- result
- .into_iter()
- .map(|tx| tx.try_into())
- .collect::<Result<Vec<_>, FfiError>>()
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn begin_db_transaction(
- &self,
- ) -> Result<Box<dyn CdkWalletDatabaseTransaction<Self::Err> + Send + Sync>, Self::Err> {
- let ffi_tx = self
- .ffi_db
- .begin_db_transaction()
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(Box::new(WalletDatabaseTransactionBridge {
- ffi_tx,
- is_finalized: false,
- }))
- }
- }
- /// Transaction bridge for FFI wallet database
- struct WalletDatabaseTransactionBridge {
- ffi_tx: Arc<WalletDatabaseTransactionWrapper>,
- is_finalized: bool,
- }
- #[async_trait::async_trait]
- impl CdkWalletDatabaseTransaction<cdk::cdk_database::Error> for WalletDatabaseTransactionBridge {
- async fn add_mint(
- &mut self,
- mint_url: cdk::mint_url::MintUrl,
- mint_info: Option<cdk::nuts::MintInfo>,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_mint_url = mint_url.into();
- let ffi_mint_info = mint_info.map(Into::into);
- self.ffi_tx
- .add_mint(ffi_mint_url, ffi_mint_info)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn remove_mint(
- &mut self,
- mint_url: cdk::mint_url::MintUrl,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_mint_url = mint_url.into();
- self.ffi_tx
- .remove_mint(ffi_mint_url)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn update_mint_url(
- &mut self,
- old_mint_url: cdk::mint_url::MintUrl,
- new_mint_url: cdk::mint_url::MintUrl,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_old_mint_url = old_mint_url.into();
- let ffi_new_mint_url = new_mint_url.into();
- self.ffi_tx
- .update_mint_url(ffi_old_mint_url, ffi_new_mint_url)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn add_mint_keysets(
- &mut self,
- mint_url: cdk::mint_url::MintUrl,
- keysets: Vec<cdk::nuts::KeySetInfo>,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_mint_url = mint_url.into();
- let ffi_keysets: Vec<KeySetInfo> = keysets.into_iter().map(Into::into).collect();
- self.ffi_tx
- .add_mint_keysets(ffi_mint_url, ffi_keysets)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn add_mint_quote(
- &mut self,
- quote: cdk::wallet::MintQuote,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_quote = quote.into();
- self.ffi_tx
- .add_mint_quote(ffi_quote)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn remove_mint_quote(&mut self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
- self.ffi_tx
- .remove_mint_quote(quote_id.to_string())
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn add_melt_quote(
- &mut self,
- quote: cdk::wallet::MeltQuote,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_quote = quote.into();
- self.ffi_tx
- .add_melt_quote(ffi_quote)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn remove_melt_quote(&mut self, quote_id: &str) -> Result<(), cdk::cdk_database::Error> {
- self.ffi_tx
- .remove_melt_quote(quote_id.to_string())
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn add_keys(
- &mut self,
- keyset: cdk::nuts::KeySet,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_keyset: KeySet = keyset.into();
- self.ffi_tx
- .add_keys(ffi_keyset)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn remove_keys(&mut self, id: &cdk::nuts::Id) -> Result<(), cdk::cdk_database::Error> {
- let ffi_id = (*id).into();
- self.ffi_tx
- .remove_keys(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn update_proofs(
- &mut self,
- added: Vec<cdk::types::ProofInfo>,
- removed_ys: Vec<cdk::nuts::PublicKey>,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_added: Vec<ProofInfo> = added.into_iter().map(Into::into).collect();
- let ffi_removed_ys: Vec<PublicKey> = removed_ys.into_iter().map(Into::into).collect();
- self.ffi_tx
- .update_proofs(ffi_added, ffi_removed_ys)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn update_proofs_state(
- &mut self,
- ys: Vec<cdk::nuts::PublicKey>,
- state: cdk::nuts::State,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_ys: Vec<PublicKey> = ys.into_iter().map(Into::into).collect();
- let ffi_state = state.into();
- self.ffi_tx
- .update_proofs_state(ffi_ys, ffi_state)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn increment_keyset_counter(
- &mut self,
- keyset_id: &cdk::nuts::Id,
- count: u32,
- ) -> Result<u32, cdk::cdk_database::Error> {
- let ffi_id = (*keyset_id).into();
- self.ffi_tx
- .increment_keyset_counter(ffi_id, count)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn add_transaction(
- &mut self,
- transaction: cdk::wallet::types::Transaction,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_transaction = transaction.into();
- self.ffi_tx
- .add_transaction(ffi_transaction)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn remove_transaction(
- &mut self,
- transaction_id: cdk::wallet::types::TransactionId,
- ) -> Result<(), cdk::cdk_database::Error> {
- let ffi_id = transaction_id.into();
- self.ffi_tx
- .remove_transaction(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- // Read methods needed during transactions
- async fn get_keyset_by_id(
- &mut self,
- keyset_id: &cdk::nuts::Id,
- ) -> Result<Option<cdk::nuts::KeySetInfo>, cdk::cdk_database::Error> {
- let ffi_id = (*keyset_id).into();
- let result = self
- .ffi_tx
- .get_keyset_by_id(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result.map(Into::into))
- }
- async fn get_keys(
- &mut self,
- id: &cdk::nuts::Id,
- ) -> Result<Option<cdk::nuts::Keys>, cdk::cdk_database::Error> {
- let ffi_id = (*id).into();
- let result = self
- .ffi_tx
- .get_keys(ffi_id)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- match result {
- Some(keys) => Ok(Some(keys.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?)),
- None => Ok(None),
- }
- }
- async fn get_mint_quote(
- &mut self,
- quote_id: &str,
- ) -> Result<Option<cdk::wallet::MintQuote>, cdk::cdk_database::Error> {
- let result = self
- .ffi_tx
- .get_mint_quote(quote_id.to_string())
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .transpose()?)
- }
- async fn get_melt_quote(
- &mut self,
- quote_id: &str,
- ) -> Result<Option<cdk::wallet::MeltQuote>, cdk::cdk_database::Error> {
- let result = self
- .ffi_tx
- .get_melt_quote(quote_id.to_string())
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- Ok(result
- .map(|q| {
- q.try_into()
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- })
- .transpose()?)
- }
- async fn get_proofs(
- &mut self,
- mint_url: Option<cdk::mint_url::MintUrl>,
- unit: Option<cdk::nuts::CurrencyUnit>,
- state: Option<Vec<cdk::nuts::State>>,
- spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>>,
- ) -> Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> {
- let ffi_mint_url = mint_url.map(Into::into);
- let ffi_unit = unit.map(Into::into);
- let ffi_state = state.map(|s| s.into_iter().map(Into::into).collect());
- let ffi_spending_conditions =
- spending_conditions.map(|sc| sc.into_iter().map(Into::into).collect());
- let result = self
- .ffi_tx
- .get_proofs(ffi_mint_url, ffi_unit, ffi_state, ffi_spending_conditions)
- .await
- .map_err(|e| cdk::cdk_database::Error::Database(e.to_string().into()))?;
- // Convert back to CDK ProofInfo
- let cdk_result: Result<Vec<cdk::types::ProofInfo>, cdk::cdk_database::Error> = result
- .into_iter()
- .map(|info| {
- Ok(cdk::types::ProofInfo {
- proof: info.proof.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- y: info.y.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- mint_url: info.mint_url.try_into().map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- state: info.state.into(),
- spending_condition: info
- .spending_condition
- .map(|sc| sc.try_into())
- .transpose()
- .map_err(|e: FfiError| {
- cdk::cdk_database::Error::Database(e.to_string().into())
- })?,
- unit: info.unit.into(),
- })
- })
- .collect();
- cdk_result
- }
- }
- #[async_trait::async_trait]
- impl DbTransactionFinalizer for WalletDatabaseTransactionBridge {
- type Err = cdk::cdk_database::Error;
- async fn commit(mut self: Box<Self>) -> Result<(), cdk::cdk_database::Error> {
- self.is_finalized = true;
- let tx = self.ffi_tx.clone();
- tx.commit()
- .await
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- async fn rollback(mut self: Box<Self>) -> Result<(), cdk::cdk_database::Error> {
- self.is_finalized = true;
- let tx = self.ffi_tx.clone();
- tx.rollback()
- .await
- .map_err(|e: FfiError| cdk::cdk_database::Error::Database(e.to_string().into()))
- }
- }
- impl Drop for WalletDatabaseTransactionBridge {
- fn drop(&mut self) {
- if !self.is_finalized {
- let tx = self.ffi_tx.clone();
- spawn(async move {
- let _ = tx.rollback().await;
- });
- }
- }
- }
- pub(crate) struct FfiWalletSQLDatabase<RM>
- where
- RM: DatabasePool + 'static,
- {
- inner: SQLWalletDatabase<RM>,
- }
- impl<RM> FfiWalletSQLDatabase<RM>
- where
- RM: DatabasePool + 'static,
- {
- /// Creates a new instance
- pub fn new(inner: SQLWalletDatabase<RM>) -> Arc<Self> {
- Arc::new(Self { inner })
- }
- }
- /// Transaction wrapper for FFI
- pub(crate) struct FfiWalletTransaction {
- tx: Arc<Mutex<Option<DynWalletDatabaseTransaction>>>,
- is_finalized: AtomicBool,
- }
- impl Drop for FfiWalletTransaction {
- fn drop(&mut self) {
- if !self.is_finalized.load(std::sync::atomic::Ordering::SeqCst) {
- let tx = self.tx.clone();
- spawn(async move {
- if let Some(s) = tx.lock().await.take() {
- let _ = s.rollback().await;
- }
- });
- }
- }
- }
- impl FfiWalletTransaction {
- pub fn new(tx: DynWalletDatabaseTransaction) -> Arc<Self> {
- Arc::new(Self {
- tx: Arc::new(Mutex::new(Some(tx))),
- is_finalized: false.into(),
- })
- }
- }
- // Implement WalletDatabaseFfi trait - only read methods + begin_db_transaction
- #[async_trait::async_trait]
- impl<RM> WalletDatabase for FfiWalletSQLDatabase<RM>
- where
- RM: DatabasePool + 'static,
- {
- async fn begin_db_transaction(
- &self,
- ) -> Result<Arc<WalletDatabaseTransactionWrapper>, FfiError> {
- let tx = self
- .inner
- .begin_db_transaction()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(Arc::new(WalletDatabaseTransactionWrapper {
- inner: FfiWalletTransaction::new(tx),
- }))
- }
- async fn get_proofs_by_ys(&self, ys: Vec<PublicKey>) -> Result<Vec<ProofInfo>, FfiError> {
- let cdk_ys: Vec<cdk::nuts::PublicKey> = ys
- .into_iter()
- .map(|y| y.try_into())
- .collect::<Result<Vec<_>, FfiError>>()?;
- let result = self
- .inner
- .get_proofs_by_ys(cdk_ys)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(Into::into).collect())
- }
- async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
- let cdk_mint_url = mint_url.try_into()?;
- let result = self
- .inner
- .get_mint(cdk_mint_url)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(Into::into))
- }
- async fn get_mints(&self) -> Result<HashMap<MintUrl, Option<MintInfo>>, FfiError> {
- let result = self
- .inner
- .get_mints()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result
- .into_iter()
- .map(|(k, v)| (k.into(), v.map(Into::into)))
- .collect())
- }
- async fn get_mint_keysets(
- &self,
- mint_url: MintUrl,
- ) -> Result<Option<Vec<KeySetInfo>>, FfiError> {
- let cdk_mint_url = mint_url.try_into()?;
- let result = self
- .inner
- .get_mint_keysets(cdk_mint_url)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(|keysets| keysets.into_iter().map(Into::into).collect()))
- }
- async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
- let cdk_id = keyset_id.into();
- let result = self
- .inner
- .get_keyset_by_id(&cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(Into::into))
- }
- async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
- let result = self
- .inner
- .get_mint_quote("e_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(|q| q.into()))
- }
- async fn get_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
- let result = self
- .inner
- .get_mint_quotes()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(|q| q.into()).collect())
- }
- async fn get_unissued_mint_quotes(&self) -> Result<Vec<MintQuote>, FfiError> {
- let result = self
- .inner
- .get_unissued_mint_quotes()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(|q| q.into()).collect())
- }
- async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
- let result = self
- .inner
- .get_melt_quote("e_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(|q| q.into()))
- }
- async fn get_melt_quotes(&self) -> Result<Vec<MeltQuote>, FfiError> {
- let result = self
- .inner
- .get_melt_quotes()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(|q| q.into()).collect())
- }
- async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
- let cdk_id = id.into();
- let result = self
- .inner
- .get_keys(&cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(Into::into))
- }
- async fn get_proofs(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- spending_conditions: Option<Vec<SpendingConditions>>,
- ) -> Result<Vec<ProofInfo>, FfiError> {
- let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
- let cdk_unit = unit.map(Into::into);
- let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
- let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
- spending_conditions
- .map(|sc| {
- sc.into_iter()
- .map(|c| c.try_into())
- .collect::<Result<Vec<_>, FfiError>>()
- })
- .transpose()?;
- let result = self
- .inner
- .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(Into::into).collect())
- }
- async fn get_balance(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- ) -> Result<u64, FfiError> {
- let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
- let cdk_unit = unit.map(Into::into);
- let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
- self.inner
- .get_balance(cdk_mint_url, cdk_unit, cdk_state)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn get_transaction(
- &self,
- transaction_id: TransactionId,
- ) -> Result<Option<Transaction>, FfiError> {
- let cdk_id = transaction_id.try_into()?;
- let result = self
- .inner
- .get_transaction(cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(Into::into))
- }
- async fn list_transactions(
- &self,
- mint_url: Option<MintUrl>,
- direction: Option<TransactionDirection>,
- unit: Option<CurrencyUnit>,
- ) -> Result<Vec<Transaction>, FfiError> {
- let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
- let cdk_direction = direction.map(Into::into);
- let cdk_unit = unit.map(Into::into);
- let result = self
- .inner
- .list_transactions(cdk_mint_url, cdk_direction, cdk_unit)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(Into::into).collect())
- }
- }
- // Implement WalletDatabaseTransactionFfi trait - all write methods
- #[async_trait::async_trait]
- impl WalletDatabaseTransaction for FfiWalletTransaction {
- async fn commit(self: Arc<Self>) -> Result<(), FfiError> {
- self.is_finalized
- .store(true, std::sync::atomic::Ordering::SeqCst);
- self.tx
- .lock()
- .await
- .take()
- .ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?
- .commit()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn rollback(self: Arc<Self>) -> Result<(), FfiError> {
- self.is_finalized
- .store(true, std::sync::atomic::Ordering::SeqCst);
- self.tx
- .lock()
- .await
- .take()
- .ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?
- .rollback()
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn add_mint(
- &self,
- mint_url: MintUrl,
- mint_info: Option<MintInfo>,
- ) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_mint_url = mint_url.try_into()?;
- let cdk_mint_info = mint_info.map(Into::into);
- tx.add_mint(cdk_mint_url, cdk_mint_info)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn remove_mint(&self, mint_url: MintUrl) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_mint_url = mint_url.try_into()?;
- tx.remove_mint(cdk_mint_url)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn update_mint_url(
- &self,
- old_mint_url: MintUrl,
- new_mint_url: MintUrl,
- ) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_old_mint_url = old_mint_url.try_into()?;
- let cdk_new_mint_url = new_mint_url.try_into()?;
- tx.update_mint_url(cdk_old_mint_url, cdk_new_mint_url)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn add_mint_keysets(
- &self,
- mint_url: MintUrl,
- keysets: Vec<KeySetInfo>,
- ) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_mint_url = mint_url.try_into()?;
- let cdk_keysets: Vec<cdk::nuts::KeySetInfo> = keysets.into_iter().map(Into::into).collect();
- tx.add_mint_keysets(cdk_mint_url, cdk_keysets)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn get_keyset_by_id(&self, keyset_id: Id) -> Result<Option<KeySetInfo>, FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_id = keyset_id.into();
- let result = tx
- .get_keyset_by_id(&cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(Into::into))
- }
- async fn get_keys(&self, id: Id) -> Result<Option<Keys>, FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_id = id.into();
- let result = tx
- .get_keys(&cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(Into::into))
- }
- async fn get_mint_quote(&self, quote_id: String) -> Result<Option<MintQuote>, FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let result = tx
- .get_mint_quote("e_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(|q| q.into()))
- }
- async fn add_mint_quote(&self, quote: MintQuote) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_quote = quote.try_into()?;
- tx.add_mint_quote(cdk_quote)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn remove_mint_quote(&self, quote_id: String) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- tx.remove_mint_quote("e_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn get_melt_quote(&self, quote_id: String) -> Result<Option<MeltQuote>, FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let result = tx
- .get_melt_quote("e_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.map(|q| q.into()))
- }
- async fn add_melt_quote(&self, quote: MeltQuote) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_quote = quote.try_into()?;
- tx.add_melt_quote(cdk_quote)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn remove_melt_quote(&self, quote_id: String) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- tx.remove_melt_quote("e_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn add_keys(&self, keyset: KeySet) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_keyset: cdk::nuts::KeySet = keyset.try_into()?;
- tx.add_keys(cdk_keyset)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn remove_keys(&self, id: Id) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_id = id.into();
- tx.remove_keys(&cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn get_proofs(
- &self,
- mint_url: Option<MintUrl>,
- unit: Option<CurrencyUnit>,
- state: Option<Vec<ProofState>>,
- spending_conditions: Option<Vec<SpendingConditions>>,
- ) -> Result<Vec<ProofInfo>, FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_mint_url = mint_url.map(|u| u.try_into()).transpose()?;
- let cdk_unit = unit.map(Into::into);
- let cdk_state = state.map(|s| s.into_iter().map(Into::into).collect());
- let cdk_spending_conditions: Option<Vec<cdk::nuts::SpendingConditions>> =
- spending_conditions
- .map(|sc| {
- sc.into_iter()
- .map(|c| c.try_into())
- .collect::<Result<Vec<_>, FfiError>>()
- })
- .transpose()?;
- let result = tx
- .get_proofs(cdk_mint_url, cdk_unit, cdk_state, cdk_spending_conditions)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })?;
- Ok(result.into_iter().map(Into::into).collect())
- }
- async fn update_proofs(
- &self,
- added: Vec<ProofInfo>,
- removed_ys: Vec<PublicKey>,
- ) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_added: Result<Vec<cdk::types::ProofInfo>, FfiError> = added
- .into_iter()
- .map(|info| {
- Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
- proof: info.proof.try_into()?,
- y: info.y.try_into()?,
- mint_url: info.mint_url.try_into()?,
- state: info.state.into(),
- spending_condition: info
- .spending_condition
- .map(|sc| sc.try_into())
- .transpose()?,
- unit: info.unit.into(),
- })
- })
- .collect();
- let cdk_added = cdk_added?;
- let cdk_removed_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
- removed_ys.into_iter().map(|pk| pk.try_into()).collect();
- let cdk_removed_ys = cdk_removed_ys?;
- tx.update_proofs(cdk_added, cdk_removed_ys)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn update_proofs_state(
- &self,
- ys: Vec<PublicKey>,
- state: ProofState,
- ) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_ys: Result<Vec<cdk::nuts::PublicKey>, FfiError> =
- ys.into_iter().map(|pk| pk.try_into()).collect();
- let cdk_ys = cdk_ys?;
- let cdk_state = state.into();
- tx.update_proofs_state(cdk_ys, cdk_state)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn increment_keyset_counter(&self, keyset_id: Id, count: u32) -> Result<u32, FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_id = keyset_id.into();
- tx.increment_keyset_counter(&cdk_id, count)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn add_transaction(&self, transaction: Transaction) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_transaction: cdk::wallet::types::Transaction = transaction.try_into()?;
- tx.add_transaction(cdk_transaction)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError> {
- let mut tx_guard = self.tx.lock().await;
- let tx = tx_guard.as_mut().ok_or(FfiError::Database {
- msg: "Transaction already finalized".to_owned(),
- })?;
- let cdk_id = transaction_id.try_into()?;
- tx.remove_transaction(cdk_id)
- .await
- .map_err(|e| FfiError::Database { msg: e.to_string() })
- }
- }
- /// FFI-safe database type enum
- #[derive(uniffi::Enum, Clone)]
- pub enum WalletDbBackend {
- Sqlite {
- path: String,
- },
- #[cfg(feature = "postgres")]
- Postgres {
- url: String,
- },
- }
- /// Factory helpers returning a CDK wallet database behind the FFI trait
- #[uniffi::export]
- pub fn create_wallet_db(backend: WalletDbBackend) -> Result<Arc<dyn WalletDatabase>, FfiError> {
- match backend {
- WalletDbBackend::Sqlite { path } => {
- let sqlite = WalletSqliteDatabase::new(path)?;
- Ok(sqlite as Arc<dyn WalletDatabase>)
- }
- #[cfg(feature = "postgres")]
- WalletDbBackend::Postgres { url } => {
- let pg = WalletPostgresDatabase::new(url)?;
- Ok(pg as Arc<dyn WalletDatabase>)
- }
- }
- }
- /// Helper function to create a CDK database from the FFI trait
- pub fn create_cdk_database_from_ffi(
- ffi_db: Arc<dyn WalletDatabase>,
- ) -> Arc<dyn CdkWalletDatabase<Err = cdk::cdk_database::Error> + Send + Sync> {
- Arc::new(WalletDatabaseBridge::new(ffi_db))
- }
|