bdk/database/
mod.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//! Database types
13//!
14//! This module provides the implementation of some defaults database types, along with traits that
15//! can be implemented externally to let [`Wallet`]s use customized databases.
16//!
17//! It's important to note that the databases defined here only contains "blockchain-related" data.
18//! They can be seen more as a cache than a critical piece of storage that contains secrets and
19//! keys.
20//!
21//! The currently recommended database is [`sled`], which is a pretty simple key-value embedded
22//! database written in Rust. If the `key-value-db` feature is enabled (which by default is),
23//! this library automatically implements all the required traits for [`sled::Tree`].
24//!
25//! [`Wallet`]: crate::wallet::Wallet
26
27use serde::{Deserialize, Serialize};
28
29use bitcoin::hash_types::Txid;
30use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, TxOut};
31
32use crate::error::Error;
33use crate::types::*;
34
35pub mod any;
36pub use any::{AnyDatabase, AnyDatabaseConfig};
37
38#[cfg(feature = "key-value-db")]
39pub(crate) mod keyvalue;
40
41#[cfg(feature = "sqlite")]
42pub(crate) mod sqlite;
43#[cfg(feature = "sqlite")]
44pub use sqlite::SqliteDatabase;
45
46pub mod memory;
47pub use memory::MemoryDatabase;
48
49/// Blockchain state at the time of syncing
50///
51/// Contains only the block time and height at the moment
52#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
53pub struct SyncTime {
54    /// Block timestamp and height at the time of sync
55    pub block_time: BlockTime,
56}
57
58/// Trait for operations that can be batched
59///
60/// This trait defines the list of operations that must be implemented on the [`Database`] type and
61/// the [`BatchDatabase::Batch`] type.
62pub trait BatchOperations {
63    /// Store a script_pubkey along with its keychain and child number.
64    fn set_script_pubkey(
65        &mut self,
66        script: &Script,
67        keychain: KeychainKind,
68        child: u32,
69    ) -> Result<(), Error>;
70    /// Store a [`LocalUtxo`]
71    fn set_utxo(&mut self, utxo: &LocalUtxo) -> Result<(), Error>;
72    /// Store a raw transaction
73    fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error>;
74    /// Store the metadata of a transaction
75    fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error>;
76    /// Store the last derivation index for a given keychain.
77    fn set_last_index(&mut self, keychain: KeychainKind, value: u32) -> Result<(), Error>;
78    /// Store the sync time
79    fn set_sync_time(&mut self, sync_time: SyncTime) -> Result<(), Error>;
80
81    /// Delete a script_pubkey given the keychain and its child number.
82    fn del_script_pubkey_from_path(
83        &mut self,
84        keychain: KeychainKind,
85        child: u32,
86    ) -> Result<Option<ScriptBuf>, Error>;
87    /// Delete the data related to a specific script_pubkey, meaning the keychain and the child
88    /// number.
89    fn del_path_from_script_pubkey(
90        &mut self,
91        script: &Script,
92    ) -> Result<Option<(KeychainKind, u32)>, Error>;
93    /// Delete a [`LocalUtxo`] given its [`OutPoint`]
94    fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error>;
95    /// Delete a raw transaction given its [`Txid`]
96    fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error>;
97    /// Delete the metadata of a transaction and optionally the raw transaction itself
98    fn del_tx(
99        &mut self,
100        txid: &Txid,
101        include_raw: bool,
102    ) -> Result<Option<TransactionDetails>, Error>;
103    /// Delete the last derivation index for a keychain.
104    fn del_last_index(&mut self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
105    /// Reset the sync time to `None`
106    ///
107    /// Returns the removed value
108    fn del_sync_time(&mut self) -> Result<Option<SyncTime>, Error>;
109}
110
111/// Trait for reading data from a database
112///
113/// This traits defines the operations that can be used to read data out of a database
114pub trait Database: BatchOperations {
115    /// Read and checks the descriptor checksum for a given keychain.
116    ///
117    /// Should return [`Error::ChecksumMismatch`] if the
118    /// checksum doesn't match. If there's no checksum in the database, simply store it for the
119    /// next time.
120    fn check_descriptor_checksum<B: AsRef<[u8]>>(
121        &mut self,
122        keychain: KeychainKind,
123        bytes: B,
124    ) -> Result<(), Error>;
125
126    /// Return the list of script_pubkeys
127    fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<ScriptBuf>, Error>;
128    /// Return the list of [`LocalUtxo`]s
129    fn iter_utxos(&self) -> Result<Vec<LocalUtxo>, Error>;
130    /// Return the list of raw transactions
131    fn iter_raw_txs(&self) -> Result<Vec<Transaction>, Error>;
132    /// Return the list of transactions metadata
133    fn iter_txs(&self, include_raw: bool) -> Result<Vec<TransactionDetails>, Error>;
134
135    /// Fetch a script_pubkey given the child number of a keychain.
136    fn get_script_pubkey_from_path(
137        &self,
138        keychain: KeychainKind,
139        child: u32,
140    ) -> Result<Option<ScriptBuf>, Error>;
141    /// Fetch the keychain and child number of a given script_pubkey
142    fn get_path_from_script_pubkey(
143        &self,
144        script: &Script,
145    ) -> Result<Option<(KeychainKind, u32)>, Error>;
146    /// Fetch a [`LocalUtxo`] given its [`OutPoint`]
147    fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error>;
148    /// Fetch a raw transaction given its [`Txid`]
149    fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error>;
150    /// Fetch the transaction metadata and optionally also the raw transaction
151    fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error>;
152    /// Return the last derivation index for a keychain.
153    fn get_last_index(&self, keychain: KeychainKind) -> Result<Option<u32>, Error>;
154    /// Return the sync time, if present
155    fn get_sync_time(&self) -> Result<Option<SyncTime>, Error>;
156
157    /// Increment the last derivation index for a keychain and return it
158    ///
159    /// It should insert and return `0` if not present in the database
160    fn increment_last_index(&mut self, keychain: KeychainKind) -> Result<u32, Error>;
161}
162
163/// Trait for a database that supports batch operations
164///
165/// This trait defines the methods to start and apply a batch of operations.
166pub trait BatchDatabase: Database {
167    /// Container for the operations
168    type Batch: BatchOperations;
169
170    /// Create a new batch container
171    fn begin_batch(&self) -> Self::Batch;
172    /// Consume and apply a batch of operations
173    fn commit_batch(&mut self, batch: Self::Batch) -> Result<(), Error>;
174}
175
176/// Trait for [`Database`] types that can be created given a configuration
177pub trait ConfigurableDatabase: Database + Sized {
178    /// Type that contains the configuration
179    type Config: std::fmt::Debug;
180
181    /// Create a new instance given a configuration
182    fn from_config(config: &Self::Config) -> Result<Self, Error>;
183}
184
185pub(crate) trait DatabaseUtils: Database {
186    fn is_mine(&self, script: &Script) -> Result<bool, Error> {
187        self.get_path_from_script_pubkey(script)
188            .map(|o| o.is_some())
189    }
190
191    #[allow(unused)]
192    fn get_raw_tx_or<D>(&self, txid: &Txid, default: D) -> Result<Option<Transaction>, Error>
193    where
194        D: FnOnce() -> Result<Option<Transaction>, Error>,
195    {
196        self.get_tx(txid, true)?
197            .and_then(|t| t.transaction)
198            .map_or_else(default, |t| Ok(Some(t)))
199    }
200
201    fn get_previous_output(&self, outpoint: &OutPoint) -> Result<Option<TxOut>, Error> {
202        self.get_raw_tx(&outpoint.txid)?
203            .map(|previous_tx| {
204                if outpoint.vout as usize >= previous_tx.output.len() {
205                    Err(Error::InvalidOutpoint(*outpoint))
206                } else {
207                    Ok(previous_tx.output[outpoint.vout as usize].clone())
208                }
209            })
210            .transpose()
211    }
212}
213
214impl<T: Database> DatabaseUtils for T {}
215
216#[cfg(test)]
217#[allow(missing_docs)]
218pub mod test {
219    use bitcoin::consensus::encode::deserialize;
220    use bitcoin::consensus::serialize;
221    use bitcoin::hashes::hex::*;
222    use bitcoin::Witness;
223    use bitcoin::*;
224    use std::str::FromStr;
225
226    use super::*;
227
228    pub fn test_script_pubkey<D: Database>(mut db: D) {
229        let script = ScriptBuf::from(
230            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
231        );
232        let path = 42;
233        let keychain = KeychainKind::External;
234
235        db.set_script_pubkey(&script, keychain, path).unwrap();
236
237        assert_eq!(
238            db.get_script_pubkey_from_path(keychain, path).unwrap(),
239            Some(script.clone())
240        );
241        assert_eq!(
242            db.get_path_from_script_pubkey(&script).unwrap(),
243            Some((keychain, path))
244        );
245    }
246
247    pub fn test_batch_script_pubkey<D: BatchDatabase>(mut db: D) {
248        let mut batch = db.begin_batch();
249
250        let script = ScriptBuf::from(
251            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
252        );
253        let path = 42;
254        let keychain = KeychainKind::External;
255
256        batch.set_script_pubkey(&script, keychain, path).unwrap();
257
258        assert_eq!(
259            db.get_script_pubkey_from_path(keychain, path).unwrap(),
260            None
261        );
262        assert_eq!(db.get_path_from_script_pubkey(&script).unwrap(), None);
263
264        db.commit_batch(batch).unwrap();
265
266        assert_eq!(
267            db.get_script_pubkey_from_path(keychain, path).unwrap(),
268            Some(script.clone())
269        );
270        assert_eq!(
271            db.get_path_from_script_pubkey(&script).unwrap(),
272            Some((keychain, path))
273        );
274    }
275
276    pub fn test_iter_script_pubkey<D: Database>(mut db: D) {
277        let script = ScriptBuf::from(
278            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
279        );
280        let path = 42;
281        let keychain = KeychainKind::External;
282
283        db.set_script_pubkey(&script, keychain, path).unwrap();
284
285        assert_eq!(db.iter_script_pubkeys(None).unwrap().len(), 1);
286    }
287
288    pub fn test_del_script_pubkey<D: Database>(mut db: D) {
289        let script = ScriptBuf::from(
290            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
291        );
292        let path = 42;
293        let keychain = KeychainKind::External;
294
295        db.set_script_pubkey(&script, keychain, path).unwrap();
296        assert_eq!(db.iter_script_pubkeys(None).unwrap().len(), 1);
297
298        db.del_script_pubkey_from_path(keychain, path).unwrap();
299        assert_eq!(db.iter_script_pubkeys(None).unwrap().len(), 0);
300    }
301
302    pub fn test_utxo<D: Database>(mut db: D) {
303        let outpoint = OutPoint::from_str(
304            "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0",
305        )
306        .unwrap();
307        let script = ScriptBuf::from(
308            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
309        );
310        let txout = TxOut {
311            value: 133742,
312            script_pubkey: script,
313        };
314        let utxo = LocalUtxo {
315            txout,
316            outpoint,
317            keychain: KeychainKind::External,
318            is_spent: true,
319        };
320
321        db.set_utxo(&utxo).unwrap();
322        db.set_utxo(&utxo).unwrap();
323        assert_eq!(db.iter_utxos().unwrap().len(), 1);
324        assert_eq!(db.get_utxo(&outpoint).unwrap(), Some(utxo));
325    }
326
327    pub fn test_raw_tx<D: Database>(mut db: D) {
328        let hex_tx = Vec::<u8>::from_hex("02000000000101f58c18a90d7a76b30c7e47d4e817adfdd79a6a589a615ef36e360f913adce2cd0000000000feffffff0210270000000000001600145c9a1816d38db5cbdd4b067b689dc19eb7d930e2cf70aa2b080000001600140f48b63160043047f4f60f7f8f551f80458f693f024730440220413f42b7bc979945489a38f5221e5527d4b8e3aa63eae2099e01945896ad6c10022024ceec492d685c31d8adb64e935a06933877c5ae0e21f32efe029850914c5bad012102361caae96f0e9f3a453d354bb37a5c3244422fb22819bf0166c0647a38de39f21fca2300").unwrap();
329        let mut tx: Transaction = deserialize(&hex_tx).unwrap();
330
331        db.set_raw_tx(&tx).unwrap();
332
333        let txid = tx.txid();
334
335        assert_eq!(db.get_raw_tx(&txid).unwrap(), Some(tx.clone()));
336
337        // mutate transaction's witnesses
338        for tx_in in tx.input.iter_mut() {
339            tx_in.witness = Witness::new();
340        }
341
342        let updated_hex_tx = serialize(&tx);
343
344        // verify that mutation was successful
345        assert_ne!(hex_tx, updated_hex_tx);
346
347        db.set_raw_tx(&tx).unwrap();
348
349        let txid = tx.txid();
350
351        assert_eq!(db.get_raw_tx(&txid).unwrap(), Some(tx));
352    }
353
354    pub fn test_tx<D: Database>(mut db: D) {
355        let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
356        let tx: Transaction = deserialize(&hex_tx).unwrap();
357        let txid = tx.txid();
358        let mut tx_details = TransactionDetails {
359            transaction: Some(tx),
360            txid,
361            received: 1337,
362            sent: 420420,
363            fee: Some(140),
364            confirmation_time: Some(BlockTime {
365                timestamp: 123456,
366                height: 1000,
367            }),
368        };
369
370        db.set_tx(&tx_details).unwrap();
371
372        // get with raw tx too
373        assert_eq!(
374            db.get_tx(&tx_details.txid, true).unwrap(),
375            Some(tx_details.clone())
376        );
377        // get only raw_tx
378        assert_eq!(
379            db.get_raw_tx(&tx_details.txid).unwrap(),
380            tx_details.transaction
381        );
382
383        // now get without raw_tx
384        tx_details.transaction = None;
385        assert_eq!(
386            db.get_tx(&tx_details.txid, false).unwrap(),
387            Some(tx_details)
388        );
389    }
390
391    pub fn test_list_transaction<D: Database>(mut db: D) {
392        let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
393        let tx: Transaction = deserialize(&hex_tx).unwrap();
394        let txid = tx.txid();
395        let mut tx_details = TransactionDetails {
396            transaction: Some(tx),
397            txid,
398            received: 1337,
399            sent: 420420,
400            fee: Some(140),
401            confirmation_time: Some(BlockTime {
402                timestamp: 123456,
403                height: 1000,
404            }),
405        };
406
407        db.set_tx(&tx_details).unwrap();
408
409        // get raw tx
410        assert_eq!(db.iter_txs(true).unwrap(), vec![tx_details.clone()]);
411
412        // now get without raw tx
413        tx_details.transaction = None;
414
415        // get not raw tx
416        assert_eq!(db.iter_txs(false).unwrap(), vec![tx_details.clone()]);
417    }
418
419    pub fn test_last_index<D: Database>(mut db: D) {
420        db.set_last_index(KeychainKind::External, 1337).unwrap();
421
422        assert_eq!(
423            db.get_last_index(KeychainKind::External).unwrap(),
424            Some(1337)
425        );
426        assert_eq!(db.get_last_index(KeychainKind::Internal).unwrap(), None);
427
428        let res = db.increment_last_index(KeychainKind::External).unwrap();
429        assert_eq!(res, 1338);
430        let res = db.increment_last_index(KeychainKind::Internal).unwrap();
431        assert_eq!(res, 0);
432
433        assert_eq!(
434            db.get_last_index(KeychainKind::External).unwrap(),
435            Some(1338)
436        );
437        assert_eq!(db.get_last_index(KeychainKind::Internal).unwrap(), Some(0));
438    }
439
440    pub fn test_sync_time<D: Database>(mut db: D) {
441        assert!(db.get_sync_time().unwrap().is_none());
442
443        db.set_sync_time(SyncTime {
444            block_time: BlockTime {
445                height: 100,
446                timestamp: 1000,
447            },
448        })
449        .unwrap();
450
451        let extracted = db.get_sync_time().unwrap();
452        assert!(extracted.is_some());
453        assert_eq!(extracted.as_ref().unwrap().block_time.height, 100);
454        assert_eq!(extracted.as_ref().unwrap().block_time.timestamp, 1000);
455
456        db.del_sync_time().unwrap();
457        assert!(db.get_sync_time().unwrap().is_none());
458    }
459
460    pub fn test_iter_raw_txs<D: Database>(mut db: D) {
461        let txs = db.iter_raw_txs().unwrap();
462        assert!(txs.is_empty());
463
464        let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
465        let first_tx: Transaction = deserialize(&hex_tx).unwrap();
466
467        let hex_tx = Vec::<u8>::from_hex("02000000000101f58c18a90d7a76b30c7e47d4e817adfdd79a6a589a615ef36e360f913adce2cd0000000000feffffff0210270000000000001600145c9a1816d38db5cbdd4b067b689dc19eb7d930e2cf70aa2b080000001600140f48b63160043047f4f60f7f8f551f80458f693f024730440220413f42b7bc979945489a38f5221e5527d4b8e3aa63eae2099e01945896ad6c10022024ceec492d685c31d8adb64e935a06933877c5ae0e21f32efe029850914c5bad012102361caae96f0e9f3a453d354bb37a5c3244422fb22819bf0166c0647a38de39f21fca2300").unwrap();
468        let second_tx: Transaction = deserialize(&hex_tx).unwrap();
469
470        db.set_raw_tx(&first_tx).unwrap();
471        db.set_raw_tx(&second_tx).unwrap();
472
473        let txs = db.iter_raw_txs().unwrap();
474
475        assert!(txs.contains(&first_tx));
476        assert!(txs.contains(&second_tx));
477        assert_eq!(txs.len(), 2);
478    }
479
480    pub fn test_del_path_from_script_pubkey<D: Database>(mut db: D) {
481        let keychain = KeychainKind::External;
482
483        let script = ScriptBuf::from(
484            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
485        );
486        let path = 42;
487
488        let res = db.del_path_from_script_pubkey(&script).unwrap();
489
490        assert!(res.is_none());
491
492        let _res = db.set_script_pubkey(&script, keychain, path);
493        let (chain, child) = db.del_path_from_script_pubkey(&script).unwrap().unwrap();
494
495        assert_eq!(chain, keychain);
496        assert_eq!(child, path);
497
498        let res = db.get_path_from_script_pubkey(&script).unwrap();
499        assert!(res.is_none());
500    }
501
502    pub fn test_iter_script_pubkeys<D: Database>(mut db: D) {
503        let keychain = KeychainKind::External;
504        let scripts = db.iter_script_pubkeys(Some(keychain)).unwrap();
505        assert!(scripts.is_empty());
506
507        let first_script = ScriptBuf::from(
508            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
509        );
510        let path = 42;
511
512        db.set_script_pubkey(&first_script, keychain, path).unwrap();
513
514        let second_script = ScriptBuf::from(
515            Vec::<u8>::from_hex("00145c9a1816d38db5cbdd4b067b689dc19eb7d930e2").unwrap(),
516        );
517        let path = 57;
518
519        db.set_script_pubkey(&second_script, keychain, path)
520            .unwrap();
521        let scripts = db.iter_script_pubkeys(Some(keychain)).unwrap();
522
523        assert!(scripts.contains(&first_script));
524        assert!(scripts.contains(&second_script));
525        assert_eq!(scripts.len(), 2);
526    }
527
528    pub fn test_del_utxo<D: Database>(mut db: D) {
529        let outpoint = OutPoint::from_str(
530            "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0",
531        )
532        .unwrap();
533        let script = ScriptBuf::from(
534            Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
535        );
536        let txout = TxOut {
537            value: 133742,
538            script_pubkey: script,
539        };
540        let utxo = LocalUtxo {
541            txout,
542            outpoint,
543            keychain: KeychainKind::External,
544            is_spent: true,
545        };
546
547        let res = db.del_utxo(&outpoint).unwrap();
548        assert!(res.is_none());
549
550        db.set_utxo(&utxo).unwrap();
551
552        let res = db.del_utxo(&outpoint).unwrap();
553
554        assert_eq!(res.unwrap(), utxo);
555
556        let res = db.get_utxo(&outpoint).unwrap();
557        assert!(res.is_none());
558    }
559
560    pub fn test_del_raw_tx<D: Database>(mut db: D) {
561        let hex_tx = Vec::<u8>::from_hex("02000000000101f58c18a90d7a76b30c7e47d4e817adfdd79a6a589a615ef36e360f913adce2cd0000000000feffffff0210270000000000001600145c9a1816d38db5cbdd4b067b689dc19eb7d930e2cf70aa2b080000001600140f48b63160043047f4f60f7f8f551f80458f693f024730440220413f42b7bc979945489a38f5221e5527d4b8e3aa63eae2099e01945896ad6c10022024ceec492d685c31d8adb64e935a06933877c5ae0e21f32efe029850914c5bad012102361caae96f0e9f3a453d354bb37a5c3244422fb22819bf0166c0647a38de39f21fca2300").unwrap();
562        let tx: Transaction = deserialize(&hex_tx).unwrap();
563
564        let res = db.del_raw_tx(&tx.txid()).unwrap();
565
566        assert!(res.is_none());
567
568        db.set_raw_tx(&tx).unwrap();
569
570        let res = db.del_raw_tx(&tx.txid()).unwrap();
571
572        assert_eq!(res.unwrap(), tx);
573
574        let res = db.get_raw_tx(&tx.txid()).unwrap();
575        assert!(res.is_none());
576    }
577
578    pub fn test_del_tx<D: Database>(mut db: D) {
579        let hex_tx = Vec::<u8>::from_hex("0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000").unwrap();
580        let tx: Transaction = deserialize(&hex_tx).unwrap();
581        let txid = tx.txid();
582        let mut tx_details = TransactionDetails {
583            transaction: Some(tx.clone()),
584            txid,
585            received: 1337,
586            sent: 420420,
587            fee: Some(140),
588            confirmation_time: Some(BlockTime {
589                timestamp: 123456,
590                height: 1000,
591            }),
592        };
593
594        let res = db.del_tx(&tx.txid(), true).unwrap();
595
596        assert!(res.is_none());
597
598        db.set_tx(&tx_details).unwrap();
599
600        let res = db.del_tx(&tx.txid(), false).unwrap();
601        tx_details.transaction = None;
602        assert_eq!(res.unwrap(), tx_details);
603
604        let res = db.get_tx(&tx.txid(), true).unwrap();
605        assert!(res.is_none());
606
607        let res = db.get_raw_tx(&tx.txid()).unwrap();
608        assert_eq!(res.unwrap(), tx);
609
610        db.set_tx(&tx_details).unwrap();
611        let res = db.del_tx(&tx.txid(), true).unwrap();
612        tx_details.transaction = Some(tx.clone());
613        assert_eq!(res.unwrap(), tx_details);
614
615        let res = db.get_tx(&tx.txid(), true).unwrap();
616        assert!(res.is_none());
617
618        let res = db.get_raw_tx(&tx.txid()).unwrap();
619        assert!(res.is_none());
620    }
621
622    pub fn test_del_last_index<D: Database>(mut db: D) {
623        let keychain = KeychainKind::External;
624
625        let _res = db.increment_last_index(keychain);
626
627        let res = db.get_last_index(keychain).unwrap().unwrap();
628
629        assert_eq!(res, 0);
630
631        let _res = db.increment_last_index(keychain);
632
633        let res = db.del_last_index(keychain).unwrap().unwrap();
634
635        assert_eq!(res, 1);
636
637        let res = db.get_last_index(keychain).unwrap();
638        assert!(res.is_none());
639    }
640
641    pub fn test_check_descriptor_checksum<D: Database>(mut db: D) {
642        // insert checksum associated to keychain
643        let checksum = "1cead456".as_bytes();
644        let keychain = KeychainKind::External;
645        let _res = db.check_descriptor_checksum(keychain, checksum);
646
647        // check if `check_descriptor_checksum` throws
648        // `Error::ChecksumMismatch` error if the
649        // function is passed a checksum that does
650        // not match the one initially inserted
651        let checksum = "1cead454".as_bytes();
652        let keychain = KeychainKind::External;
653        let res = db.check_descriptor_checksum(keychain, checksum);
654
655        assert!(res.is_err());
656    }
657
658    // TODO: more tests...
659}