Sfoglia il codice sorgente

Use WalletDatabaseTransactionWrapper for FFI database transactions

Replace trait object return type with a concrete wrapper type to enable
UniFFI's with_foreign attribute. This change introduces
WalletDatabaseTransactionWrapper as a Record type that wraps Arc<dyn
WalletDatabaseTransaction>, allowing the WalletDatabase trait to work with
foreign implementations while avoiding Send safety issues with raw trait object
returns.
Cesar Rodas 2 mesi fa
parent
commit
dd47e2494a

+ 40 - 20
crates/cdk-ffi/src/database.rs

@@ -1,11 +1,9 @@
 //! FFI Database bindings
 
 use std::collections::HashMap;
+use std::ops::Deref;
 use std::sync::Arc;
 
-// Re-export the CDK database types for 100% compatibility
-pub use cdk_common::database::WalletDatabase;
-pub use cdk_common::database::WalletDatabaseTransaction as WalletDatabaseTx;
 use cdk_common::database::{
     DbTransactionFinalizer, DynWalletDatabaseTransaction, WalletDatabase as CdkWalletDatabase,
     WalletDatabaseTransaction as CdkWalletDatabaseTransaction,
@@ -22,11 +20,11 @@ use crate::types::*;
 
 /// FFI-compatible wallet database trait (read-only operations + begin_db_transaction)
 /// This trait mirrors the CDK WalletDatabase trait structure
+#[uniffi::export(with_foreign)]
 #[async_trait::async_trait]
-pub trait WalletDatabaseFfi: Send + Sync {
+pub trait WalletDatabase: Send + Sync {
     /// Begin a database transaction
-    async fn begin_db_transaction(&self)
-        -> Result<Arc<dyn WalletDatabaseTransactionFfi>, FfiError>;
+    async fn begin_db_transaction(&self) -> Result<WalletDatabaseTransactionWrapper, FfiError>;
 
     /// Get mint from storage
     async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError>;
@@ -94,7 +92,7 @@ pub trait WalletDatabaseFfi: Send + Sync {
 /// This trait mirrors the CDK WalletDatabaseTransaction trait but uses FFI-compatible types
 #[uniffi::export(with_foreign)]
 #[async_trait::async_trait]
-pub trait WalletDatabaseTransactionFfi: Send + Sync {
+pub trait WalletDatabaseTransaction: Send + Sync {
     /// Commit the transaction
     async fn commit(self: Arc<Self>) -> Result<(), FfiError>;
 
@@ -196,14 +194,36 @@ pub trait WalletDatabaseTransactionFfi: Send + Sync {
     async fn remove_transaction(&self, transaction_id: TransactionId) -> Result<(), FfiError>;
 }
 
+/// Wallet database transaction wrapper
+#[derive(uniffi::Record)]
+pub struct WalletDatabaseTransactionWrapper {
+    inner: Arc<dyn WalletDatabaseTransaction>,
+}
+
+#[uniffi::export]
+impl WalletDatabaseTransactionWrapper {
+    #[uniffi::constructor]
+    pub fn new(inner: Arc<dyn WalletDatabaseTransaction>) -> Self {
+        Self { inner }
+    }
+}
+
+impl Deref for WalletDatabaseTransactionWrapper {
+    type Target = Arc<dyn WalletDatabaseTransaction>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.inner
+    }
+}
+
 /// 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 WalletDatabaseFfi>,
+    ffi_db: Arc<dyn WalletDatabase>,
 }
 
 impl WalletDatabaseBridge {
-    fn new(ffi_db: Arc<dyn WalletDatabaseFfi>) -> Self {
+    fn new(ffi_db: Arc<dyn WalletDatabase>) -> Self {
         Self { ffi_db }
     }
 }
@@ -488,7 +508,7 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
 
 /// Transaction bridge for FFI wallet database
 struct WalletDatabaseTransactionBridge {
-    ffi_tx: Arc<dyn WalletDatabaseTransactionFfi>,
+    ffi_tx: WalletDatabaseTransactionWrapper,
     is_finalized: bool,
 }
 
@@ -850,20 +870,20 @@ impl FfiWalletTransaction {
 
 // Implement WalletDatabaseFfi trait - only read methods + begin_db_transaction
 #[async_trait::async_trait]
-impl<RM> WalletDatabaseFfi for FfiWalletSQLDatabase<RM>
+impl<RM> WalletDatabase for FfiWalletSQLDatabase<RM>
 where
     RM: DatabasePool + 'static,
 {
-    async fn begin_db_transaction(
-        &self,
-    ) -> Result<Arc<dyn WalletDatabaseTransactionFfi>, FfiError> {
+    async fn begin_db_transaction(&self) -> Result<WalletDatabaseTransactionWrapper, FfiError> {
         let tx = self
             .inner
             .begin_db_transaction()
             .await
             .map_err(|e| FfiError::Database { msg: e.to_string() })?;
 
-        Ok(FfiWalletTransaction::new(tx))
+        Ok(WalletDatabaseTransactionWrapper {
+            inner: FfiWalletTransaction::new(tx),
+        })
     }
 
     async fn get_mint(&self, mint_url: MintUrl) -> Result<Option<MintInfo>, FfiError> {
@@ -1036,7 +1056,7 @@ where
 
 // Implement WalletDatabaseTransactionFfi trait - all write methods
 #[async_trait::async_trait]
-impl WalletDatabaseTransactionFfi for FfiWalletTransaction {
+impl WalletDatabaseTransaction for FfiWalletTransaction {
     async fn commit(self: Arc<Self>) -> Result<(), FfiError> {
         self.tx
             .lock()
@@ -1374,18 +1394,18 @@ pub enum WalletDatabaseType {
 }
 
 impl WalletDatabaseType {
-    pub fn as_trait(&self) -> Arc<dyn WalletDatabaseFfi> {
+    pub fn as_trait(&self) -> Arc<dyn WalletDatabase> {
         match self {
-            WalletDatabaseType::Sqlite { db } => db.clone() as Arc<dyn WalletDatabaseFfi>,
+            WalletDatabaseType::Sqlite { db } => db.clone() as Arc<dyn WalletDatabase>,
             #[cfg(feature = "postgres")]
-            WalletDatabaseType::Postgres { db } => db.clone() as Arc<dyn WalletDatabaseFfi>,
+            WalletDatabaseType::Postgres { db } => db.clone() 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 WalletDatabaseFfi>,
+    ffi_db: Arc<dyn WalletDatabase>,
 ) -> Arc<dyn CdkWalletDatabase<Err = cdk::cdk_database::Error> + Send + Sync> {
     Arc::new(WalletDatabaseBridge::new(ffi_db))
 }

+ 4 - 4
crates/cdk-ffi/src/multi_mint_wallet.rs

@@ -28,7 +28,7 @@ impl MultiMintWallet {
     pub fn new(
         unit: CurrencyUnit,
         mnemonic: String,
-        db: crate::database::WalletDatabaseType,
+        db: Arc<dyn crate::database::WalletDatabase>,
     ) -> Result<Self, FfiError> {
         // Parse mnemonic and generate seed without passphrase
         let m = Mnemonic::parse(&mnemonic)
@@ -36,7 +36,7 @@ impl MultiMintWallet {
         let seed = m.to_seed_normalized("");
 
         // Convert the FFI database trait to a CDK database implementation
-        let localstore = crate::database::create_cdk_database_from_ffi(db.as_trait());
+        let localstore = crate::database::create_cdk_database_from_ffi(db);
 
         let wallet = match tokio::runtime::Handle::try_current() {
             Ok(handle) => tokio::task::block_in_place(|| {
@@ -66,7 +66,7 @@ impl MultiMintWallet {
     pub fn new_with_proxy(
         unit: CurrencyUnit,
         mnemonic: String,
-        db: crate::database::WalletDatabaseType,
+        db: Arc<dyn crate::database::WalletDatabase>,
         proxy_url: String,
     ) -> Result<Self, FfiError> {
         // Parse mnemonic and generate seed without passphrase
@@ -75,7 +75,7 @@ impl MultiMintWallet {
         let seed = m.to_seed_normalized("");
 
         // Convert the FFI database trait to a CDK database implementation
-        let localstore = crate::database::create_cdk_database_from_ffi(db.as_trait());
+        let localstore = crate::database::create_cdk_database_from_ffi(db);
 
         // Parse proxy URL
         let proxy_url =

+ 3 - 5
crates/cdk-ffi/src/postgres.rs

@@ -7,7 +7,7 @@ use cdk_postgres::PgConnectionPool;
 use crate::{
     CurrencyUnit, FfiError, FfiWalletSQLDatabase, Id, KeySetInfo, Keys, MeltQuote, MintInfo,
     MintQuote, MintUrl, ProofInfo, ProofState, SpendingConditions, Transaction,
-    TransactionDirection, TransactionId, WalletDatabaseFfi,
+    TransactionDirection, TransactionId, WalletDatabase, WalletDatabaseTransactionWrapper,
 };
 
 #[derive(uniffi::Object)]
@@ -61,10 +61,8 @@ impl WalletPostgresDatabase {
 
 #[uniffi::export(async_runtime = "tokio")]
 #[async_trait::async_trait]
-impl WalletDatabaseFfi for WalletPostgresDatabase {
-    async fn begin_db_transaction(
-        &self,
-    ) -> Result<Arc<dyn crate::database::WalletDatabaseTransactionFfi>, FfiError> {
+impl WalletDatabase for WalletPostgresDatabase {
+    async fn begin_db_transaction(&self) -> Result<WalletDatabaseTransactionWrapper, FfiError> {
         self.inner.begin_db_transaction().await
     }
 

+ 3 - 3
crates/cdk-ffi/src/sqlite.rs

@@ -7,7 +7,7 @@ use cdk_sqlite::SqliteConnectionManager;
 use crate::{
     CurrencyUnit, FfiError, FfiWalletSQLDatabase, Id, KeySetInfo, Keys, MeltQuote, MintInfo,
     MintQuote, MintUrl, ProofInfo, ProofState, SpendingConditions, Transaction,
-    TransactionDirection, TransactionId, WalletDatabaseFfi,
+    TransactionDirection, TransactionId, WalletDatabase,
 };
 
 /// FFI-compatible WalletSqliteDatabase implementation that implements the WalletDatabaseFfi trait
@@ -66,10 +66,10 @@ impl WalletSqliteDatabase {
 
 #[uniffi::export(async_runtime = "tokio")]
 #[async_trait::async_trait]
-impl WalletDatabaseFfi for WalletSqliteDatabase {
+impl WalletDatabase for WalletSqliteDatabase {
     async fn begin_db_transaction(
         &self,
-    ) -> Result<Arc<dyn crate::database::WalletDatabaseTransactionFfi>, FfiError> {
+    ) -> Result<crate::database::WalletDatabaseTransactionWrapper, FfiError> {
         self.inner.begin_db_transaction().await
     }
 

+ 2 - 2
crates/cdk-ffi/src/wallet.rs

@@ -31,7 +31,7 @@ impl Wallet {
         mint_url: String,
         unit: CurrencyUnit,
         mnemonic: String,
-        db: crate::database::WalletDatabaseType,
+        db: Arc<dyn crate::database::WalletDatabase>,
         config: WalletConfig,
     ) -> Result<Self, FfiError> {
         // Parse mnemonic and generate seed without passphrase
@@ -40,7 +40,7 @@ impl Wallet {
         let seed = m.to_seed_normalized("");
 
         // Convert the FFI database trait to a CDK database implementation
-        let localstore = crate::database::create_cdk_database_from_ffi(db.as_trait());
+        let localstore = crate::database::create_cdk_database_from_ffi(db);
 
         let wallet =
             CdkWalletBuilder::new()