diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b43faf3..cb089741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `sqlite-bundled` feature for deployments that need a bundled version of sqlite, ie. for mobile platforms. - Added `Wallet::get_signers()`, `Wallet::descriptor_checksum()` and `Wallet::get_address_validators()`, exposed the `AsDerived` trait. - Deprecate `database::Database::flush()`, the function is only needed for the sled database on mobile, instead for mobile use the sqlite database. +- Improve key generation traits ## [v0.17.0] - [v0.16.1] @@ -441,4 +442,4 @@ final transaction is created by calling `finish` on the builder. [v0.16.0]: https://github.com/bitcoindevkit/bdk/compare/v0.15.0...v0.16.0 [v0.16.1]: https://github.com/bitcoindevkit/bdk/compare/v0.16.0...v0.16.1 [v0.17.0]: https://github.com/bitcoindevkit/bdk/compare/v0.16.1...v0.17.0 -[unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.17.0...HEAD \ No newline at end of file +[unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.17.0...HEAD diff --git a/examples/rpcwallet.rs b/examples/rpcwallet.rs index 0263ef78..3178af6b 100644 --- a/examples/rpcwallet.rs +++ b/examples/rpcwallet.rs @@ -7,7 +7,6 @@ // licenses. use bdk::bitcoin::secp256k1::Secp256k1; -use bdk::bitcoin::util::bip32::ExtendedPrivKey; use bdk::bitcoin::Amount; use bdk::bitcoin::Network; use bdk::bitcoincore_rpc::RpcApi; @@ -16,7 +15,7 @@ use bdk::blockchain::rpc::{Auth, RpcBlockchain, RpcConfig}; use bdk::blockchain::ConfigurableBlockchain; use bdk::keys::bip39::{Language, Mnemonic, WordCount}; -use bdk::keys::{DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey}; +use bdk::keys::{DerivableKey, GeneratableKey, GeneratedKey}; use bdk::miniscript::miniscript::Segwitv0; @@ -85,8 +84,8 @@ fn main() -> Result<(), Box> { // create unique wallet name. // This is a special utility function exposed via `bdk::wallet_name_from_descriptor()` let wallet_name = wallet_name_from_descriptor( - Bip84(xprv, KeychainKind::External), - Some(Bip84(xprv, KeychainKind::Internal)), + Bip84(xprv.clone(), KeychainKind::External), + Some(Bip84(xprv.clone(), KeychainKind::Internal)), Network::Regtest, &Secp256k1::new(), )?; @@ -112,8 +111,8 @@ fn main() -> Result<(), Box> { // Combine Database + Descriptor to create the final wallet let wallet = Wallet::new( - Bip84(xprv, KeychainKind::External), - Some(Bip84(xprv, KeychainKind::Internal)), + Bip84(xprv.clone(), KeychainKind::External), + Some(Bip84(xprv.clone(), KeychainKind::Internal)), Network::Regtest, database, )?; @@ -216,18 +215,15 @@ fn main() -> Result<(), Box> { // Helper function demonstrating privatekey extraction using bip39 mnemonic // The mnemonic can be shown to user to safekeeping and the same wallet // private descriptors can be recreated from it. -fn generate_random_ext_privkey() -> Result> { +fn generate_random_ext_privkey() -> Result + Clone, Box> { // a Bip39 passphrase can be set optionally let password = Some("random password".to_string()); - // Generate a random mnemonic, and use that to create an Extended PrivateKey - let mnemonic: GeneratedKey<_, Segwitv0> = - Mnemonic::generate((WordCount::Words12, Language::English)) - .map_err(|e| e.expect("Unknown Error"))?; - let mnemonic = mnemonic.into_key(); - let xkey: ExtendedKey = (mnemonic, password).into_extended_key()?; - let xprv = xkey - .into_xprv(Network::Regtest) - .expect("Expected Private Key"); - Ok(xprv) + // Generate a random mnemonic, and use that to create a "DerivableKey" + let mnemonic: GeneratedKey<_, _> = Mnemonic::generate((WordCount::Words12, Language::English)) + .map_err(|e| e.expect("Unknown Error"))?; + + // `Ok(mnemonic)` would also work if there's no passphrase and it would + // yield the same result as this construct with `password` = `None`. + Ok((mnemonic, password)) } diff --git a/src/keys/bip39.rs b/src/keys/bip39.rs index c6cf24fd..a8902a9f 100644 --- a/src/keys/bip39.rs +++ b/src/keys/bip39.rs @@ -94,6 +94,23 @@ impl DerivableKey for MnemonicWithPassphrase { } } +#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))] +impl DerivableKey for (GeneratedKey, Option) { + fn into_extended_key(self) -> Result, KeyError> { + let (mnemonic, passphrase) = self; + (mnemonic.into_key(), passphrase).into_extended_key() + } + + fn into_descriptor_key( + self, + source: Option, + derivation_path: bip32::DerivationPath, + ) -> Result, KeyError> { + let (mnemonic, passphrase) = self; + (mnemonic.into_key(), passphrase).into_descriptor_key(source, derivation_path) + } +} + #[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))] impl DerivableKey for Mnemonic { fn into_extended_key(self) -> Result, KeyError> { diff --git a/src/keys/mod.rs b/src/keys/mod.rs index a21a0c97..14e16e7b 100644 --- a/src/keys/mod.rs +++ b/src/keys/mod.rs @@ -548,6 +548,16 @@ impl Deref for GeneratedKey { } } +impl Clone for GeneratedKey { + fn clone(&self) -> GeneratedKey { + GeneratedKey { + key: self.key.clone(), + valid_networks: self.valid_networks.clone(), + phantom: self.phantom, + } + } +} + // Make generated "derivable" keys themselves "derivable". Also make sure they are assigned the // right `valid_networks`. impl DerivableKey for GeneratedKey