|
27 | 27 | use serde::{Deserialize, Serialize}; |
28 | 28 |
|
29 | 29 | use bitcoin::hash_types::Txid; |
30 | | -use bitcoin::{OutPoint, Script, Transaction, TxOut}; |
| 30 | +use bitcoin::{Network, OutPoint, Script, Transaction, TxOut}; |
31 | 31 |
|
| 32 | +use crate::descriptor::ExtendedDescriptor; |
32 | 33 | use crate::error::Error; |
33 | 34 | use crate::types::*; |
| 35 | +use crate::wallet::utils::SecpCtx; |
34 | 36 |
|
35 | 37 | pub mod any; |
36 | 38 | pub use any::{AnyDatabase, AnyDatabaseConfig}; |
@@ -212,12 +214,39 @@ pub(crate) trait DatabaseUtils: Database { |
212 | 214 |
|
213 | 215 | impl<T: Database> DatabaseUtils for T {} |
214 | 216 |
|
| 217 | +/// A factory trait that builds databases which share underlying configurations and/or storage |
| 218 | +/// paths. |
| 219 | +pub trait DatabaseFactory: Sized { |
| 220 | + /// Inner type to build |
| 221 | + type Inner: BatchDatabase; |
| 222 | + |
| 223 | + /// Builds the defined [`DatabaseFactory::Inner`] type. |
| 224 | + fn build( |
| 225 | + &self, |
| 226 | + descriptor: ExtendedDescriptor, |
| 227 | + network: Network, |
| 228 | + secp: &SecpCtx, |
| 229 | + ) -> Result<Self::Inner, Error> { |
| 230 | + self.build_with_change(descriptor, None, network, secp) |
| 231 | + } |
| 232 | + |
| 233 | + /// Builds the defined [`DatabaseFactory::Inner`] type with the addition of a change descriptor. |
| 234 | + fn build_with_change( |
| 235 | + &self, |
| 236 | + descriptor: ExtendedDescriptor, |
| 237 | + change_descriptor: Option<ExtendedDescriptor>, |
| 238 | + network: Network, |
| 239 | + secp: &SecpCtx, |
| 240 | + ) -> Result<Self::Inner, Error>; |
| 241 | +} |
| 242 | + |
215 | 243 | #[cfg(test)] |
216 | 244 | pub mod test { |
217 | 245 | use std::str::FromStr; |
218 | 246 |
|
219 | 247 | use bitcoin::consensus::encode::deserialize; |
220 | 248 | use bitcoin::hashes::hex::*; |
| 249 | + use bitcoin::util::bip32::{self, DerivationPath, ExtendedPubKey}; |
221 | 250 | use bitcoin::*; |
222 | 251 |
|
223 | 252 | use super::*; |
@@ -441,5 +470,42 @@ pub mod test { |
441 | 470 | assert!(tree.get_sync_time().unwrap().is_none()); |
442 | 471 | } |
443 | 472 |
|
| 473 | + pub fn test_factory<F: DatabaseFactory>(fac: &F) { |
| 474 | + let secp = SecpCtx::new(); |
| 475 | + let network = Network::Regtest; |
| 476 | + let master_privkey = bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPdowxEXJxXqPYd7i7WN3jG8NTVsq9MYVaR7qnLgi5xo1KZq4z1T89GfGs7BwQTVrtVKWozxwuQLgFNcd3snADMeivux1Y5u5").unwrap(); |
| 477 | + |
| 478 | + let descriptor = |acc: usize| -> ExtendedDescriptor { |
| 479 | + let path = DerivationPath::from_str(&format!("m/84h/1h/{}h", acc)).unwrap(); |
| 480 | + let sk = master_privkey.derive_priv(&secp, &path).unwrap(); |
| 481 | + let pk = ExtendedPubKey::from_priv(&secp, &sk); |
| 482 | + ExtendedDescriptor::from_str(&format!( |
| 483 | + "wpkh([{}/84h/1h/{}h]{}/0/*)", |
| 484 | + master_privkey.fingerprint(&secp), |
| 485 | + acc, |
| 486 | + pk |
| 487 | + )) |
| 488 | + .unwrap() |
| 489 | + }; |
| 490 | + |
| 491 | + let mut acc_index = 0_usize; |
| 492 | + let mut database = || { |
| 493 | + let db = fac.build(descriptor(acc_index), network, &secp).unwrap(); |
| 494 | + acc_index += 1; |
| 495 | + db |
| 496 | + }; |
| 497 | + |
| 498 | + test_script_pubkey(database()); |
| 499 | + test_batch_script_pubkey(database()); |
| 500 | + test_iter_script_pubkey(database()); |
| 501 | + test_del_script_pubkey(database()); |
| 502 | + test_utxo(database()); |
| 503 | + test_raw_tx(database()); |
| 504 | + test_tx(database()); |
| 505 | + test_list_transaction(database()); |
| 506 | + test_last_index(database()); |
| 507 | + test_sync_time(database()); |
| 508 | + } |
| 509 | + |
444 | 510 | // TODO: more tests... |
445 | 511 | } |
0 commit comments