Merge bitcoindevkit/bdk-ffi#154: Add child key pair generation api
5944756b780968588e97886d888bb854aa2053ad Added tests for DescriptorSecretKey and DescriptorPublicKey (dhruvbaliyan) 58fea6b2050d9f10b1c5429cce4f4969c155e9e3 Added interfaces DescriptorSecretKey and DescriptorPublicKey (dhruvbaliyan) 4977cb6d68830accf036b192dba3c524253e53f2 Added interface DerivationPath (dhruvbaliyan) 930a1f1eb4a64bf447dec2493edd9948be67f119 Added generate_mnemonic method (dhruvbaliyan) 973013cbdfb0093c9d0bc8d2f5176c981160e060 Removed ExtendedKeyInfo & related methods (dhruvbaliyan) Pull request description: Would like to know if anything can be improved. Completes #87 Top commit has no ACKs. Tree-SHA512: a480535c8965015d860336c717ec3c394778ac08194b0336eeba4209f3e3eff2072873a190dd8c9e4fac1e2f712c7040c838dc1c1a757d53c28866f118c99c17
This commit is contained in:
		
						commit
						7da28658a5
					
				
							
								
								
									
										23
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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). | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||||
| 
 | 
 | ||||||
| ## [Unreleased] | ## [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] | ## [v0.8.0] | ||||||
| - Update BDK to version 0.20.0 [#169] | - Update BDK to version 0.20.0 [#169] | ||||||
|  | |||||||
							
								
								
									
										39
									
								
								src/bdk.udl
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/bdk.udl
									
									
									
									
									
								
							| @ -1,9 +1,6 @@ | |||||||
| namespace bdk { | namespace bdk { | ||||||
|   [Throws=BdkError] |   [Throws=BdkError] | ||||||
|   ExtendedKeyInfo generate_extended_key(Network network, WordCount word_count, string? password); |   string generate_mnemonic(WordCount word_count); | ||||||
| 
 |  | ||||||
|   [Throws=BdkError] |  | ||||||
|   ExtendedKeyInfo restore_extended_key(Network network, string mnemonic, string? password); |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| [Error] | [Error] | ||||||
| @ -101,12 +98,6 @@ interface Transaction { | |||||||
|   Confirmed(TransactionDetails details, BlockTime confirmation); |   Confirmed(TransactionDetails details, BlockTime confirmation); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| dictionary ExtendedKeyInfo { |  | ||||||
|   string mnemonic; |  | ||||||
|   string xprv; |  | ||||||
|   string fingerprint; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| enum WordCount { | enum WordCount { | ||||||
|   "Words12", |   "Words12", | ||||||
|   "Words15", |   "Words15", | ||||||
| @ -254,3 +245,31 @@ interface BumpFeeTxBuilder { | |||||||
|   [Throws=BdkError] |   [Throws=BdkError] | ||||||
|   PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet); |   PartiallySignedBitcoinTransaction finish([ByRef] Wallet wallet); | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | interface DerivationPath { | ||||||
|  |   [Throws=BdkError] | ||||||
|  |   constructor(string path); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | interface DescriptorSecretKey { | ||||||
|  |   [Throws=BdkError] | ||||||
|  |   constructor(Network network, string mnemonic, string? password); | ||||||
|  | 
 | ||||||
|  |   [Throws=BdkError] | ||||||
|  |   DescriptorSecretKey derive(DerivationPath path); | ||||||
|  | 
 | ||||||
|  |   DescriptorSecretKey extend(DerivationPath path); | ||||||
|  | 
 | ||||||
|  |   DescriptorPublicKey as_public(); | ||||||
|  | 
 | ||||||
|  |   string as_string(); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | interface DescriptorPublicKey { | ||||||
|  |   [Throws=BdkError] | ||||||
|  |   DescriptorPublicKey derive(DerivationPath path); | ||||||
|  | 
 | ||||||
|  |   DescriptorPublicKey extend(DerivationPath path); | ||||||
|  | 
 | ||||||
|  |   string as_string(); | ||||||
|  | }; | ||||||
							
								
								
									
										308
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										308
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -1,5 +1,6 @@ | |||||||
| use bdk::bitcoin::hashes::hex::ToHex; | use bdk::bitcoin::hashes::hex::ToHex; | ||||||
| use bdk::bitcoin::secp256k1::Secp256k1; | use bdk::bitcoin::secp256k1::Secp256k1; | ||||||
|  | use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath; | ||||||
| use bdk::bitcoin::util::psbt::PartiallySignedTransaction; | use bdk::bitcoin::util::psbt::PartiallySignedTransaction; | ||||||
| use bdk::bitcoin::{Address, Network, OutPoint as BdkOutPoint, Script, Txid}; | use bdk::bitcoin::{Address, Network, OutPoint as BdkOutPoint, Script, Txid}; | ||||||
| use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; | use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; | ||||||
| @ -9,8 +10,12 @@ use bdk::blockchain::{ | |||||||
| use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress}; | use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress}; | ||||||
| use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration}; | use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration}; | ||||||
| use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase}; | use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase}; | ||||||
|  | use bdk::descriptor::DescriptorXKey; | ||||||
| use bdk::keys::bip39::{Language, Mnemonic, WordCount}; | use bdk::keys::bip39::{Language, Mnemonic, WordCount}; | ||||||
| use bdk::keys::{DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey}; | use bdk::keys::{ | ||||||
|  |     DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey, | ||||||
|  |     DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey, | ||||||
|  | }; | ||||||
| use bdk::miniscript::BareCtx; | use bdk::miniscript::BareCtx; | ||||||
| use bdk::wallet::tx_builder::ChangeSpendPolicy; | use bdk::wallet::tx_builder::ChangeSpendPolicy; | ||||||
| use bdk::wallet::AddressIndex as BdkAddressIndex; | use bdk::wallet::AddressIndex as BdkAddressIndex; | ||||||
| @ -351,46 +356,6 @@ impl Wallet { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct ExtendedKeyInfo { |  | ||||||
|     mnemonic: String, |  | ||||||
|     xprv: String, |  | ||||||
|     fingerprint: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn generate_extended_key( |  | ||||||
|     network: Network, |  | ||||||
|     word_count: WordCount, |  | ||||||
|     password: Option<String>, |  | ||||||
| ) -> Result<ExtendedKeyInfo, Error> { |  | ||||||
|     let mnemonic: GeneratedKey<_, BareCtx> = |  | ||||||
|         Mnemonic::generate((word_count, Language::English)).unwrap(); |  | ||||||
|     let mnemonic = mnemonic.into_key(); |  | ||||||
|     let xkey: ExtendedKey = (mnemonic.clone(), password).into_extended_key()?; |  | ||||||
|     let xprv = xkey.into_xprv(network).unwrap(); |  | ||||||
|     let fingerprint = xprv.fingerprint(&Secp256k1::new()); |  | ||||||
|     Ok(ExtendedKeyInfo { |  | ||||||
|         mnemonic: mnemonic.to_string(), |  | ||||||
|         xprv: xprv.to_string(), |  | ||||||
|         fingerprint: fingerprint.to_string(), |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn restore_extended_key( |  | ||||||
|     network: Network, |  | ||||||
|     mnemonic: String, |  | ||||||
|     password: Option<String>, |  | ||||||
| ) -> Result<ExtendedKeyInfo, Error> { |  | ||||||
|     let mnemonic = Mnemonic::parse_in(Language::English, mnemonic).unwrap(); |  | ||||||
|     let xkey: ExtendedKey = (mnemonic.clone(), password).into_extended_key()?; |  | ||||||
|     let xprv = xkey.into_xprv(network).unwrap(); |  | ||||||
|     let fingerprint = xprv.fingerprint(&Secp256k1::new()); |  | ||||||
|     Ok(ExtendedKeyInfo { |  | ||||||
|         mnemonic: mnemonic.to_string(), |  | ||||||
|         xprv: xprv.to_string(), |  | ||||||
|         fingerprint: fingerprint.to_string(), |  | ||||||
|     }) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn to_script_pubkey(address: &str) -> Result<Script, BdkError> { | fn to_script_pubkey(address: &str) -> Result<Script, BdkError> { | ||||||
|     Address::from_str(address) |     Address::from_str(address) | ||||||
|         .map(|x| x.script_pubkey()) |         .map(|x| x.script_pubkey()) | ||||||
| @ -667,6 +632,173 @@ impl BumpFeeTxBuilder { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | fn generate_mnemonic(word_count: WordCount) -> Result<String, BdkError> { | ||||||
|  |     let mnemonic: GeneratedKey<_, BareCtx> = | ||||||
|  |         Mnemonic::generate((word_count, Language::English)).unwrap(); | ||||||
|  |     Ok(mnemonic.to_string()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct DerivationPath { | ||||||
|  |     derivation_path_mutex: Mutex<BdkDerivationPath>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DerivationPath { | ||||||
|  |     fn new(path: String) -> Result<Self, BdkError> { | ||||||
|  |         BdkDerivationPath::from_str(&path) | ||||||
|  |             .map(|x| DerivationPath { | ||||||
|  |                 derivation_path_mutex: Mutex::new(x), | ||||||
|  |             }) | ||||||
|  |             .map_err(|e| BdkError::Generic(e.to_string())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct DescriptorSecretKey { | ||||||
|  |     descriptor_secret_key_mutex: Mutex<BdkDescriptorSecretKey>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DescriptorSecretKey { | ||||||
|  |     fn new(network: Network, mnemonic: String, password: Option<String>) -> Result<Self, BdkError> { | ||||||
|  |         let mnemonic = Mnemonic::parse_in(Language::English, mnemonic) | ||||||
|  |             .map_err(|e| BdkError::Generic(e.to_string()))?; | ||||||
|  |         let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?; | ||||||
|  |         let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey { | ||||||
|  |             origin: None, | ||||||
|  |             xkey: xkey.into_xprv(network).unwrap(), | ||||||
|  |             derivation_path: BdkDerivationPath::master(), | ||||||
|  |             wildcard: bdk::descriptor::Wildcard::Unhardened, | ||||||
|  |         }); | ||||||
|  |         Ok(Self { | ||||||
|  |             descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> { | ||||||
|  |         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(); | ||||||
|  |         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!() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn extend(&self, path: Arc<DerivationPath>) -> Arc<Self> { | ||||||
|  |         let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap(); | ||||||
|  |         let path = path.derivation_path_mutex.lock().unwrap().deref().clone(); | ||||||
|  |         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!() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn as_public(&self) -> Arc<DescriptorPublicKey> { | ||||||
|  |         let secp = Secp256k1::new(); | ||||||
|  |         let descriptor_public_key = self | ||||||
|  |             .descriptor_secret_key_mutex | ||||||
|  |             .lock() | ||||||
|  |             .unwrap() | ||||||
|  |             .as_public(&secp) | ||||||
|  |             .unwrap(); | ||||||
|  |         Arc::new(DescriptorPublicKey { | ||||||
|  |             descriptor_public_key_mutex: Mutex::new(descriptor_public_key), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn as_string(&self) -> String { | ||||||
|  |         self.descriptor_secret_key_mutex.lock().unwrap().to_string() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct DescriptorPublicKey { | ||||||
|  |     descriptor_public_key_mutex: Mutex<BdkDescriptorPublicKey>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DescriptorPublicKey { | ||||||
|  |     fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> { | ||||||
|  |         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(); | ||||||
|  | 
 | ||||||
|  |         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!() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn extend(&self, path: Arc<DerivationPath>) -> Arc<Self> { | ||||||
|  |         let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap(); | ||||||
|  |         let path = path.derivation_path_mutex.lock().unwrap().deref().clone(); | ||||||
|  |         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!() | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn as_string(&self) -> String { | ||||||
|  |         self.descriptor_public_key_mutex.lock().unwrap().to_string() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send); | uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send); | ||||||
| 
 | 
 | ||||||
| // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
 | // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
 | ||||||
| @ -674,7 +806,7 @@ uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send); | |||||||
| // crate.
 | // crate.
 | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod test { | mod test { | ||||||
|     use crate::{TxBuilder, Wallet}; |     use crate::*; | ||||||
|     use bdk::bitcoin::Address; |     use bdk::bitcoin::Address; | ||||||
|     use bdk::bitcoin::Network::Testnet; |     use bdk::bitcoin::Network::Testnet; | ||||||
|     use bdk::wallet::get_funded_wallet; |     use bdk::wallet::get_funded_wallet; | ||||||
| @ -734,4 +866,96 @@ mod test { | |||||||
|         let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value; |         let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value; | ||||||
|         assert_eq!(output_value, 49_890_u64); // input - fee
 |         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<Arc<DescriptorSecretKey>, BdkError> { | ||||||
|  |         let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); | ||||||
|  |         key.derive(path) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn extend_dsk(key: &DescriptorSecretKey, path: &str) -> Arc<DescriptorSecretKey> { | ||||||
|  |         let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); | ||||||
|  |         key.extend(path) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn derive_dpk( | ||||||
|  |         key: &DescriptorPublicKey, | ||||||
|  |         path: &str, | ||||||
|  |     ) -> Result<Arc<DescriptorPublicKey>, BdkError> { | ||||||
|  |         let path = Arc::new(DerivationPath::new(path.to_string()).unwrap()); | ||||||
|  |         key.derive(path) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn extend_dpk(key: &DescriptorPublicKey, path: &str) -> Arc<DescriptorPublicKey> { | ||||||
|  |         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()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user