bdkffi/
store.rs

1use crate::error::{PersistenceError, PreV1MigrationError};
2use crate::types::{ChangeSet, KeychainKind};
3
4use bdk_wallet::migration::{
5    get_pre_v1_wallet_keychains as bdk_get_pre_v1_wallet_keychains,
6    PreV1WalletKeychain as BdkPreV1WalletKeychain,
7};
8use bdk_wallet::{rusqlite::Connection as BdkConnection, WalletPersister};
9
10use std::ops::DerefMut;
11use std::sync::{Arc, Mutex};
12
13/// Definition of a wallet persistence implementation.
14#[uniffi::export(with_foreign)]
15pub trait Persistence: Send + Sync {
16    /// Initialize the total aggregate `ChangeSet` for the underlying wallet.
17    fn initialize(&self) -> Result<Arc<ChangeSet>, PersistenceError>;
18
19    /// Persist a `ChangeSet` to the total aggregate changeset of the wallet.
20    fn persist(&self, changeset: Arc<ChangeSet>) -> Result<(), PersistenceError>;
21}
22
23pub(crate) enum PersistenceType {
24    Custom(Arc<dyn Persistence>),
25    Sql(Mutex<BdkConnection>),
26}
27
28/// `PreV1WalletKeychain` represents a structure that holds the keychain details
29/// and metadata required for managing a wallet's keys.
30#[derive(Debug, Clone, uniffi::Record)]
31pub struct PreV1WalletKeychain {
32    /// The name of the wallet keychains, "External" or "Internal".
33    pub keychain: KeychainKind,
34    /// The index of the last derived key in the wallet keychain.
35    pub last_derivation_index: u32,
36    /// Checksum of the keychain descriptor, it must match the corresponding
37    /// post-1.0 bdk wallet descriptor checksum.
38    pub checksum: String,
39}
40
41/// Wallet backend implementations.
42#[derive(uniffi::Object)]
43pub struct Persister {
44    pub(crate) inner: Mutex<PersistenceType>,
45}
46
47#[uniffi::export]
48impl Persister {
49    /// Create a new Sqlite connection at the specified file path.
50    #[uniffi::constructor]
51    pub fn new_sqlite(path: String) -> Result<Self, PersistenceError> {
52        let conn = BdkConnection::open(path)?;
53        Ok(Self {
54            inner: PersistenceType::Sql(conn.into()).into(),
55        })
56    }
57
58    /// Create a new connection in memory.
59    #[uniffi::constructor]
60    pub fn new_in_memory() -> Result<Self, PersistenceError> {
61        let conn = BdkConnection::open_in_memory()?;
62        Ok(Self {
63            inner: PersistenceType::Sql(conn.into()).into(),
64        })
65    }
66
67    /// Use a native persistence layer.
68    #[uniffi::constructor]
69    pub fn custom(persistence: Arc<dyn Persistence>) -> Self {
70        Self {
71            inner: PersistenceType::Custom(persistence).into(),
72        }
73    }
74
75    /// Retrieve keychain metadata from a pre-v1 BDK SQLite wallet database.
76    pub fn get_pre_v1_wallet_keychains(
77        &self,
78    ) -> Result<Vec<PreV1WalletKeychain>, PreV1MigrationError> {
79        let mut lock = self.inner.lock().unwrap();
80        match lock.deref_mut() {
81            PersistenceType::Sql(ref conn) => {
82                let mut conn_lock = conn.lock().unwrap();
83                bdk_get_pre_v1_wallet_keychains(conn_lock.deref_mut())
84                    .map(|keychains| keychains.into_iter().map(Into::into).collect())
85                    .map_err(Into::into)
86            }
87            PersistenceType::Custom(_) => Err(PreV1MigrationError::SqliteOnly),
88        }
89    }
90}
91
92impl From<BdkPreV1WalletKeychain> for PreV1WalletKeychain {
93    fn from(value: BdkPreV1WalletKeychain) -> Self {
94        Self {
95            keychain: value.keychain,
96            last_derivation_index: value.last_derivation_index,
97            checksum: value.checksum,
98        }
99    }
100}
101
102impl WalletPersister for PersistenceType {
103    type Error = PersistenceError;
104
105    fn initialize(persister: &mut Self) -> Result<bdk_wallet::ChangeSet, Self::Error> {
106        match persister {
107            PersistenceType::Sql(ref conn) => {
108                let mut lock = conn.lock().unwrap();
109                let deref = lock.deref_mut();
110                Ok(BdkConnection::initialize(deref)?)
111            }
112            PersistenceType::Custom(any) => any
113                .initialize()
114                .map(|changeset| changeset.as_ref().clone().into()),
115        }
116    }
117
118    fn persist(persister: &mut Self, changeset: &bdk_wallet::ChangeSet) -> Result<(), Self::Error> {
119        match persister {
120            PersistenceType::Sql(ref conn) => {
121                let mut lock = conn.lock().unwrap();
122                let deref = lock.deref_mut();
123                Ok(BdkConnection::persist(deref, changeset)?)
124            }
125            PersistenceType::Custom(any) => {
126                let ffi_changeset: ChangeSet = changeset.clone().into();
127                any.persist(Arc::new(ffi_changeset))
128            }
129        }
130    }
131}