From 5944756b780968588e97886d888bb854aa2053ad Mon Sep 17 00:00:00 2001 From: dhruvbaliyan Date: Tue, 9 Aug 2022 14:43:06 +0530 Subject: [PATCH] Added tests for DescriptorSecretKey and DescriptorPublicKey --- CHANGELOG.md | 23 +++++ src/lib.rs | 233 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 190 insertions(+), 66 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf7d727..f319934 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- APIs Added [#154] + - `generate_mnemonic()`, returns string mnemonic + - `interface DescriptorSecretKey` + - `new(Network, string_mnenoinc, password)`, contructs DescriptorSecretKey + - `derive(DerivationPath)`, derives and returns child DescriptorSecretKey + - `extend(DerivationPath)`, extends and returns DescriptorSecretKey + - `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey + - `as_string()`, returns DescriptorSecretKey as String + - `interface DescriptorPublicKey` + - `derive(DerivationPath)` derives and returns child DescriptorPublicKey + - `extend(DerivationPath)` extends and returns DescriptorPublicKey + - `as_string()` returns DescriptorPublicKey as String +- Interfaces Added [#154] + - `DescriptorSecretKey` + - `DescriptorPublicKey` + - `DerivationPath` +- Dictionary Removed [#154] + - `ExtendedKeyInfo {mnenonic, xprv, fingerprint}` +- APIs Removed [#154] + - `generate_extended_key`, returned ExtendedKeyInfo + - `restore_extended_key`, returned ExtendedKeyInfo + +[#154]: https://github.com/bitcoindevkit/bdk-ffi/pull/154 ## [v0.8.0] - Update BDK to version 0.20.0 [#169] diff --git a/src/lib.rs b/src/lib.rs index df468a6..d7ba6be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -676,45 +676,49 @@ impl DescriptorSecretKey { let secp = Secp256k1::new(); let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap(); let path = path.derivation_path_mutex.lock().unwrap().deref().clone(); - let descriptor_x_key = match descriptor_secret_key.deref() { - BdkDescriptorSecretKey::XPrv(descriptor_x_key) => Some(descriptor_x_key), - _ => None, + match descriptor_secret_key.deref() { + BdkDescriptorSecretKey::XPrv(descriptor_x_key) => { + let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?; + let key_source = match descriptor_x_key.origin.clone() { + Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)), + None => (descriptor_x_key.xkey.fingerprint(&secp), path), + }; + let derived_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey { + origin: Some(key_source), + xkey: derived_xprv, + derivation_path: BdkDerivationPath::default(), + wildcard: descriptor_x_key.wildcard, + }); + Ok(Arc::new(Self { + descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key), + })) + } + BdkDescriptorSecretKey::SinglePriv(_) => { + unreachable!() + } } - .unwrap(); - let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?; - let key_source = match descriptor_x_key.origin.clone() { - Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)), - None => (descriptor_x_key.xkey.fingerprint(&secp), path), - }; - let derived_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey { - origin: Some(key_source), - xkey: derived_xprv, - derivation_path: BdkDerivationPath::default(), - wildcard: descriptor_x_key.wildcard, - }); - Ok(Arc::new(Self { - descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key), - })) } fn extend(&self, path: Arc) -> Arc { let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap(); let path = path.derivation_path_mutex.lock().unwrap().deref().clone(); - let descriptor_x_key = match descriptor_secret_key.deref() { - BdkDescriptorSecretKey::XPrv(descriptor_x_key) => Some(descriptor_x_key), - _ => None, + match descriptor_secret_key.deref() { + BdkDescriptorSecretKey::XPrv(descriptor_x_key) => { + let extended_path = descriptor_x_key.derivation_path.extend(path); + let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey { + origin: descriptor_x_key.origin.clone(), + xkey: descriptor_x_key.xkey, + derivation_path: extended_path, + wildcard: descriptor_x_key.wildcard, + }); + Arc::new(Self { + descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key), + }) + } + BdkDescriptorSecretKey::SinglePriv(_) => { + unreachable!() + } } - .unwrap(); - let extended_path = descriptor_x_key.derivation_path.extend(path); - let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey { - origin: descriptor_x_key.origin.clone(), - xkey: descriptor_x_key.xkey, - derivation_path: extended_path, - wildcard: descriptor_x_key.wildcard, - }); - Arc::new(Self { - descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key), - }) } fn as_public(&self) -> Arc { @@ -744,45 +748,50 @@ impl DescriptorPublicKey { let secp = Secp256k1::new(); let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap(); let path = path.derivation_path_mutex.lock().unwrap().deref().clone(); - let descriptor_x_key = match descriptor_public_key.deref() { - BdkDescriptorPublicKey::XPub(descriptor_x_key) => Some(descriptor_x_key), - _ => None, + + match descriptor_public_key.deref() { + BdkDescriptorPublicKey::XPub(descriptor_x_key) => { + let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?; + let key_source = match descriptor_x_key.origin.clone() { + Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)), + None => (descriptor_x_key.xkey.fingerprint(), path), + }; + let derived_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey { + origin: Some(key_source), + xkey: derived_xpub, + derivation_path: BdkDerivationPath::default(), + wildcard: descriptor_x_key.wildcard, + }); + Ok(Arc::new(Self { + descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key), + })) + } + BdkDescriptorPublicKey::SinglePub(_) => { + unreachable!() + } } - .unwrap(); - let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?; - let key_source = match descriptor_x_key.origin.clone() { - Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)), - None => (descriptor_x_key.xkey.fingerprint(), path), - }; - let derived_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey { - origin: Some(key_source), - xkey: derived_xpub, - derivation_path: BdkDerivationPath::default(), - wildcard: descriptor_x_key.wildcard, - }); - Ok(Arc::new(Self { - descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key), - })) } fn extend(&self, path: Arc) -> Arc { - let descriptor_secret_key = self.descriptor_public_key_mutex.lock().unwrap(); + let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap(); let path = path.derivation_path_mutex.lock().unwrap().deref().clone(); - let descriptor_x_key = match descriptor_secret_key.deref() { - BdkDescriptorPublicKey::XPub(descriptor_x_key) => Some(descriptor_x_key), - _ => None, + match descriptor_public_key.deref() { + BdkDescriptorPublicKey::XPub(descriptor_x_key) => { + let extended_path = descriptor_x_key.derivation_path.extend(path); + let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey { + origin: descriptor_x_key.origin.clone(), + xkey: descriptor_x_key.xkey, + derivation_path: extended_path, + wildcard: descriptor_x_key.wildcard, + }); + Arc::new(Self { + descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key), + }) + } + BdkDescriptorPublicKey::SinglePub(_) => { + unreachable!() + } } - .unwrap(); - let extended_path = descriptor_x_key.derivation_path.extend(path); - let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey { - origin: descriptor_x_key.origin.clone(), - xkey: descriptor_x_key.xkey, - derivation_path: extended_path, - wildcard: descriptor_x_key.wildcard, - }); - Arc::new(Self { - descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key), - }) } fn as_string(&self) -> String { @@ -797,7 +806,7 @@ uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send); // crate. #[cfg(test)] mod test { - use crate::{TxBuilder, Wallet}; + use crate::*; use bdk::bitcoin::Address; use bdk::bitcoin::Network::Testnet; use bdk::wallet::get_funded_wallet; @@ -857,4 +866,96 @@ mod test { let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value; assert_eq!(output_value, 49_890_u64); // input - fee } + + fn get_descriptor_secret_key() -> DescriptorSecretKey { + let mnemonic = + "chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string(); + DescriptorSecretKey::new(Network::Testnet, mnemonic, None).unwrap() + } + + fn derive_dsk( + key: &DescriptorSecretKey, + path: &str, + ) -> Result, BdkError> { + let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); + key.derive(path) + } + + fn extend_dsk(key: &DescriptorSecretKey, path: &str) -> Arc { + let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); + key.extend(path) + } + + fn derive_dpk( + key: &DescriptorPublicKey, + path: &str, + ) -> Result, BdkError> { + let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); + key.derive(path) + } + + fn extend_dpk(key: &DescriptorPublicKey, path: &str) -> Arc { + let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); + key.extend(path) + } + + #[test] + fn test_generate_descriptor_secret_key() { + let master_dsk = get_descriptor_secret_key(); + assert_eq!(master_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*"); + assert_eq!(master_dsk.as_public().as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*"); + } + + #[test] + fn test_derive_self() { + let master_dsk = get_descriptor_secret_key(); + let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m").unwrap(); + assert_eq!(derived_dsk.as_string(), "[d1d04177]tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*"); + + let master_dpk: &DescriptorPublicKey = &master_dsk.as_public(); + let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m").unwrap(); + assert_eq!(derived_dpk.as_string(), "[d1d04177]tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*"); + } + + #[test] + fn test_derive_descriptors_keys() { + let master_dsk = get_descriptor_secret_key(); + let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap(); + assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*"); + + let master_dpk: &DescriptorPublicKey = &master_dsk.as_public(); + let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m/0").unwrap(); + assert_eq!(derived_dpk.as_string(), "[d1d04177/0]tpubD9oaCiP1MPmQdndm7DCD3D3QU34pWd6BbKSRedoZF1UJcNhEk3PJwkALNYkhxeTKL29oGNR7psqvT1KZydCGqUDEKXN6dVQJY2R8ooLPy8m/*"); + } + + #[test] + fn test_extend_descriptor_keys() { + let master_dsk = get_descriptor_secret_key(); + let extended_dsk: &DescriptorSecretKey = &extend_dsk(&master_dsk, "m/0"); + assert_eq!(extended_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/0/*"); + + let master_dpk: &DescriptorPublicKey = &master_dsk.as_public(); + let extended_dpk: &DescriptorPublicKey = &extend_dpk(master_dpk, "m/0"); + assert_eq!(extended_dpk.as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/0/*"); + } + + #[test] + fn test_derive_and_extend_descriptor_secret_key() { + let master_dsk = get_descriptor_secret_key(); + + // derive DescriptorSecretKey with path "m/0" from master + let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap(); + assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*"); + + // extend derived_dsk with path "m/0" + let extended_dsk: &DescriptorSecretKey = &extend_dsk(derived_dsk, "m/0"); + assert_eq!(extended_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/0/*"); + } + + #[test] + fn test_derive_hardened_path_using_public() { + let master_dpk = get_descriptor_secret_key().as_public(); + let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h"); + assert!(derived_dpk.is_err()); + } }