jitash_bdk/database/
any.rs

1// Bitcoin Dev Kit
2// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
3//
4// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
5//
6// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
7// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
8// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
9// You may not use this file except in accordance with one or both of these
10// licenses.
11
12//! Runtime-checked database types
13//!
14//! This module provides the implementation of [`AnyDatabase`] which allows switching the
15//! inner [`Database`] type at runtime.
16//!
17//! ## Example
18//!
19//! In this example, `wallet_memory` and `wallet_sled` have the same type of `Wallet<(), AnyDatabase>`.
20//!
21//! ```no_run
22//! # use bitcoin::Network;
23//! # use jitash_bdk::database::{AnyDatabase, MemoryDatabase};
24//! # use jitash_bdk::{Wallet};
25//! let memory = MemoryDatabase::default();
26//! let wallet_memory = Wallet::new("...", None, Network::Testnet, memory)?;
27//!
28//! # #[cfg(feature = "key-value-db")]
29//! # {
30//! let sled = sled::open("my-database")?.open_tree("default_tree")?;
31//! let wallet_sled = Wallet::new("...", None, Network::Testnet, sled)?;
32//! # }
33//! # Ok::<(), jitash_bdk::Error>(())
34//! ```
35//!
36//! When paired with the use of [`ConfigurableDatabase`], it allows creating wallets with any
37//! database supported using a single line of code:
38//!
39//! ```no_run
40//! # use bitcoin::Network;
41//! # use jitash_bdk::database::*;
42//! # use jitash_bdk::{Wallet};
43//! let config = serde_json::from_str("...")?;
44//! let database = AnyDatabase::from_config(&config)?;
45//! let wallet = Wallet::new("...", None, Network::Testnet, database)?;
46//! # Ok::<(), jitash_bdk::Error>(())
47//! ```
48
49use super::*;
50
51macro_rules! impl_from {
52    ( $from:ty, $to:ty, $variant:ident, $( $cfg:tt )* ) => {
53        $( $cfg )*
54        impl From<$from> for $to {
55            fn from(inner: $from) -> Self {
56                <$to>::$variant(inner)
57            }
58        }
59    };
60}
61
62macro_rules! impl_inner_method {
63    ( $enum_name:ident, $self:expr, $name:ident $(, $args:expr)* ) => {
64        #[allow(deprecated)]
65        match $self {
66            $enum_name::Memory(inner) => inner.$name( $($args, )* ),
67            #[cfg(feature = "key-value-db")]
68            $enum_name::Sled(inner) => inner.$name( $($args, )* ),
69            #[cfg(feature = "sqlite")]
70            $enum_name::Sqlite(inner) => inner.$name( $($args, )* ),
71        }
72    }
73}
74
75/// Type that can contain any of the [`Database`] types defined by the library
76///
77/// It allows switching database type at runtime.
78///
79/// See [this module](crate::database::any)'s documentation for a usage example.
80#[derive(Debug)]
81pub enum AnyDatabase {
82    /// In-memory ephemeral database
83    Memory(memory::MemoryDatabase),
84    #[cfg(feature = "key-value-db")]
85    #[cfg_attr(docsrs, doc(cfg(feature = "key-value-db")))]
86    /// Simple key-value embedded database based on [`sled`]
87    Sled(sled::Tree),
88    #[cfg(feature = "sqlite")]
89    #[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
90    /// Sqlite embedded database using [`rusqlite`]
91    Sqlite(sqlite::SqliteDatabase),
92}
93
94impl_from!(memory::MemoryDatabase, AnyDatabase, Memory,);
95impl_from!(sled::Tree, AnyDatabase, Sled, #[cfg(feature = "key-value-db")]);
96impl_from!(sqlite::SqliteDatabase, AnyDatabase, Sqlite, #[cfg(feature = "sqlite")]);
97
98/// Type that contains any of the [`BatchDatabase::Batch`] types defined by the library
99pub enum AnyBatch {
100    /// In-memory ephemeral database
101    Memory(<memory::MemoryDatabase as BatchDatabase>::Batch),
102    #[cfg(feature = "key-value-db")]
103    #[cfg_attr(docsrs, doc(cfg(feature = "key-value-db")))]
104    /// Simple key-value embedded database based on [`sled`]
105    Sled(<sled::Tree as BatchDatabase>::Batch),
106    #[cfg(feature = "sqlite")]
107    #[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
108    /// Sqlite embedded database using [`rusqlite`]
109    Sqlite(<sqlite::SqliteDatabase as BatchDatabase>::Batch),
110}
111
112impl_from!(
113    <memory::MemoryDatabase as BatchDatabase>::Batch,
114    AnyBatch,
115    Memory,
116);
117impl_from!(<sled::Tree as BatchDatabase>::Batch, AnyBatch, Sled, #[cfg(feature = "key-value-db")]);
118impl_from!(<sqlite::SqliteDatabase as BatchDatabase>::Batch, AnyBatch, Sqlite, #[cfg(feature = "sqlite")]);
119
120impl BatchOperations for AnyDatabase {
121    fn set_script_pubkey(
122        &mut self,
123        script: &Script,
124        keychain: KeychainKind,
125        child: u32,
126    ) -> Result<(), Error> {
127        impl_inner_method!(
128            AnyDatabase,
129            self,
130            set_script_pubkey,
131            script,
132            keychain,
133            child
134        )
135    }
136    fn set_utxo(&mut self, utxo: &LocalUtxo) -> Result<(), Error> {
137        impl_inner_method!(AnyDatabase, self, set_utxo, utxo)
138    }
139    fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error> {
140        impl_inner_method!(AnyDatabase, self, set_raw_tx, transaction)
141    }
142    fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error> {
143        impl_inner_method!(AnyDatabase, self, set_tx, transaction)
144    }
145    fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> {
146        impl_inner_method!(AnyDatabase, self, set_last_index, keychain, value)
147    }
148    fn set_sync_time(&mut self, sync_time: SyncTime) -> Result<(), Error> {
149        impl_inner_method!(AnyDatabase, self, set_sync_time, sync_time)
150    }
151
152    fn del_script_pubkey_from_path(
153        &mut self,
154        keychain: KeychainKind,
155        child: u32,
156    ) -> Result<Option<Script>, Error> {
157        impl_inner_method!(
158            AnyDatabase,
159            self,
160            del_script_pubkey_from_path,
161            keychain,
162            child
163        )
164    }
165    fn del_path_from_script_pubkey(
166        &mut self,
167        script: &Script,
168    ) -> Result<Option<(KeychainKind, u32)>, Error> {
169        impl_inner_method!(AnyDatabase, self, del_path_from_script_pubkey, script)
170    }
171    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> {
172        impl_inner_method!(AnyDatabase, self, del_utxo, outpoint)
173    }
174    fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error> {
175        impl_inner_method!(AnyDatabase, self, del_raw_tx, txid)
176    }
177    fn del_tx(
178        &mut self,
179        txid: &Txid,
180        include_raw: bool,
181    ) -> Result<Option<TransactionDetails>, Error> {
182        impl_inner_method!(AnyDatabase, self, del_tx, txid, include_raw)
183    }
184    fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
185        impl_inner_method!(AnyDatabase, self, del_last_index, keychain)
186    }
187    fn del_sync_time(&mut self) -> Result<Option<SyncTime>, Error> {
188        impl_inner_method!(AnyDatabase, self, del_sync_time)
189    }
190}
191
192impl Database for AnyDatabase {
193    fn check_descriptor_checksum<B: AsRef<[u8]>>(
194        &mut self,
195        keychain: KeychainKind,
196        bytes: B,
197    ) -> Result<(), Error> {
198        impl_inner_method!(
199            AnyDatabase,
200            self,
201            check_descriptor_checksum,
202            keychain,
203            bytes
204        )
205    }
206
207    fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<Script>, Error> {
208        impl_inner_method!(AnyDatabase, self, iter_script_pubkeys, keychain)
209    }
210    fn iter_utxos(&self) -> Result<Vec<LocalUtxo>, Error> {
211        impl_inner_method!(AnyDatabase, self, iter_utxos)
212    }
213    fn iter_raw_txs(&self) -> Result<Vec<Transaction>, Error> {
214        impl_inner_method!(AnyDatabase, self, iter_raw_txs)
215    }
216    fn iter_txs(&self, include_raw: bool) -> Result<Vec<TransactionDetails>, Error> {
217        impl_inner_method!(AnyDatabase, self, iter_txs, include_raw)
218    }
219
220    fn get_script_pubkey_from_path(
221        &self,
222        keychain: KeychainKind,
223        child: u32,
224    ) -> Result<Option<Script>, Error> {
225        impl_inner_method!(
226            AnyDatabase,
227            self,
228            get_script_pubkey_from_path,
229            keychain,
230            child
231        )
232    }
233    fn get_path_from_script_pubkey(
234        &self,
235        script: &Script,
236    ) -> Result<Option<(KeychainKind, u32)>, Error> {
237        impl_inner_method!(AnyDatabase, self, get_path_from_script_pubkey, script)
238    }
239    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> {
240        impl_inner_method!(AnyDatabase, self, get_utxo, outpoint)
241    }
242    fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
243        impl_inner_method!(AnyDatabase, self, get_raw_tx, txid)
244    }
245    fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error> {
246        impl_inner_method!(AnyDatabase, self, get_tx, txid, include_raw)
247    }
248    fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
249        impl_inner_method!(AnyDatabase, self, get_last_index, keychain)
250    }
251    fn get_sync_time(&self) -> Result<Option<SyncTime>, Error> {
252        impl_inner_method!(AnyDatabase, self, get_sync_time)
253    }
254
255    fn increment_last_index(&mut self, keychain: KeychainKind) -> Result<u32, Error> {
256        impl_inner_method!(AnyDatabase, self, increment_last_index, keychain)
257    }
258}
259
260impl BatchOperations for AnyBatch {
261    fn set_script_pubkey(
262        &mut self,
263        script: &Script,
264        keychain: KeychainKind,
265        child: u32,
266    ) -> Result<(), Error> {
267        impl_inner_method!(AnyBatch, self, set_script_pubkey, script, keychain, child)
268    }
269    fn set_utxo(&mut self, utxo: &LocalUtxo) -> Result<(), Error> {
270        impl_inner_method!(AnyBatch, self, set_utxo, utxo)
271    }
272    fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error> {
273        impl_inner_method!(AnyBatch, self, set_raw_tx, transaction)
274    }
275    fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error> {
276        impl_inner_method!(AnyBatch, self, set_tx, transaction)
277    }
278    fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error> {
279        impl_inner_method!(AnyBatch, self, set_last_index, keychain, value)
280    }
281    fn set_sync_time(&mut self, sync_time: SyncTime) -> Result<(), Error> {
282        impl_inner_method!(AnyBatch, self, set_sync_time, sync_time)
283    }
284
285    fn del_script_pubkey_from_path(
286        &mut self,
287        keychain: KeychainKind,
288        child: u32,
289    ) -> Result<Option<Script>, Error> {
290        impl_inner_method!(AnyBatch, self, del_script_pubkey_from_path, keychain, child)
291    }
292    fn del_path_from_script_pubkey(
293        &mut self,
294        script: &Script,
295    ) -> Result<Option<(KeychainKind, u32)>, Error> {
296        impl_inner_method!(AnyBatch, self, del_path_from_script_pubkey, script)
297    }
298    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> {
299        impl_inner_method!(AnyBatch, self, del_utxo, outpoint)
300    }
301    fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error> {
302        impl_inner_method!(AnyBatch, self, del_raw_tx, txid)
303    }
304    fn del_tx(
305        &mut self,
306        txid: &Txid,
307        include_raw: bool,
308    ) -> Result<Option<TransactionDetails>, Error> {
309        impl_inner_method!(AnyBatch, self, del_tx, txid, include_raw)
310    }
311    fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error> {
312        impl_inner_method!(AnyBatch, self, del_last_index, keychain)
313    }
314    fn del_sync_time(&mut self) -> Result<Option<SyncTime>, Error> {
315        impl_inner_method!(AnyBatch, self, del_sync_time)
316    }
317}
318
319impl BatchDatabase for AnyDatabase {
320    type Batch = AnyBatch;
321
322    fn begin_batch(&self) -> Self::Batch {
323        match self {
324            AnyDatabase::Memory(inner) => inner.begin_batch().into(),
325            #[cfg(feature = "key-value-db")]
326            AnyDatabase::Sled(inner) => inner.begin_batch().into(),
327            #[cfg(feature = "sqlite")]
328            AnyDatabase::Sqlite(inner) => inner.begin_batch().into(),
329        }
330    }
331    fn commit_batch(&mut self, batch: Self::Batch) -> Result<(), Error> {
332        match self {
333            AnyDatabase::Memory(db) => match batch {
334                AnyBatch::Memory(batch) => db.commit_batch(batch),
335                #[cfg(any(feature = "key-value-db", feature = "sqlite"))]
336                _ => unimplemented!("Other batch shouldn't be used with Memory db."),
337            },
338            #[cfg(feature = "key-value-db")]
339            AnyDatabase::Sled(db) => match batch {
340                AnyBatch::Sled(batch) => db.commit_batch(batch),
341                _ => unimplemented!("Other batch shouldn't be used with Sled db."),
342            },
343            #[cfg(feature = "sqlite")]
344            AnyDatabase::Sqlite(db) => match batch {
345                AnyBatch::Sqlite(batch) => db.commit_batch(batch),
346                _ => unimplemented!("Other batch shouldn't be used with Sqlite db."),
347            },
348        }
349    }
350}
351
352/// Configuration type for a [`sled::Tree`] database
353#[cfg(feature = "key-value-db")]
354#[derive(Debug, serde::Serialize, serde::Deserialize)]
355pub struct SledDbConfiguration {
356    /// Main directory of the db
357    pub path: String,
358    /// Name of the database tree, a separated namespace for the data
359    pub tree_name: String,
360}
361
362#[cfg(feature = "key-value-db")]
363impl ConfigurableDatabase for sled::Tree {
364    type Config = SledDbConfiguration;
365
366    fn from_config(config: &Self::Config) -> Result<Self, Error> {
367        Ok(sled::open(&config.path)?.open_tree(&config.tree_name)?)
368    }
369}
370
371/// Configuration type for a [`sqlite::SqliteDatabase`] database
372#[cfg(feature = "sqlite")]
373#[derive(Debug, serde::Serialize, serde::Deserialize)]
374pub struct SqliteDbConfiguration {
375    /// Main directory of the db
376    pub path: String,
377}
378
379#[cfg(feature = "sqlite")]
380impl ConfigurableDatabase for sqlite::SqliteDatabase {
381    type Config = SqliteDbConfiguration;
382
383    fn from_config(config: &Self::Config) -> Result<Self, Error> {
384        Ok(sqlite::SqliteDatabase::new(config.path.clone()))
385    }
386}
387
388/// Type that can contain any of the database configurations defined by the library
389///
390/// This allows storing a single configuration that can be loaded into an [`AnyDatabase`]
391/// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime
392/// will find this particularly useful.
393#[derive(Debug, serde::Serialize, serde::Deserialize)]
394pub enum AnyDatabaseConfig {
395    /// Memory database has no config
396    Memory(()),
397    #[cfg(feature = "key-value-db")]
398    #[cfg_attr(docsrs, doc(cfg(feature = "key-value-db")))]
399    /// Simple key-value embedded database based on [`sled`]
400    Sled(SledDbConfiguration),
401    #[cfg(feature = "sqlite")]
402    #[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))]
403    /// Sqlite embedded database using [`rusqlite`]
404    Sqlite(SqliteDbConfiguration),
405}
406
407impl ConfigurableDatabase for AnyDatabase {
408    type Config = AnyDatabaseConfig;
409
410    fn from_config(config: &Self::Config) -> Result<Self, Error> {
411        Ok(match config {
412            AnyDatabaseConfig::Memory(inner) => {
413                AnyDatabase::Memory(memory::MemoryDatabase::from_config(inner)?)
414            }
415            #[cfg(feature = "key-value-db")]
416            AnyDatabaseConfig::Sled(inner) => AnyDatabase::Sled(sled::Tree::from_config(inner)?),
417            #[cfg(feature = "sqlite")]
418            AnyDatabaseConfig::Sqlite(inner) => {
419                AnyDatabase::Sqlite(sqlite::SqliteDatabase::from_config(inner)?)
420            }
421        })
422    }
423}
424
425impl_from!((), AnyDatabaseConfig, Memory,);
426impl_from!(SledDbConfiguration, AnyDatabaseConfig, Sled, #[cfg(feature = "key-value-db")]);
427impl_from!(SqliteDbConfiguration, AnyDatabaseConfig, Sqlite, #[cfg(feature = "sqlite")]);