From 542328e85758839f4673c7c5b693b57786f0e80f Mon Sep 17 00:00:00 2001 From: GideonBature Date: Tue, 28 Oct 2025 12:27:06 +0100 Subject: [PATCH 1/2] feat(bdk_wallet): add get_keymap() helper method to get the KeyMap for Psbt.sign --- src/wallet/mod.rs | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 137aecc3..bbf711c0 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -1305,6 +1305,43 @@ impl Wallet { } } + /// Get the combined KeyMap for all signers in the wallet. + /// + /// This includes keys from both external and internal (change) signers. + /// The returned KeyMap can be used with `PsbtExt::sign` to sign PSBTs directly. + /// + /// # Example + /// + /// ```rust,no_run + /// use bdk_wallet::{KeychainKind, Wallet}; + /// use miniscript::psbt::PsbtExt; + /// use bdk_wallet::bitcoin::{Amount, Network}; + /// + /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)"; + /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)"; + /// let wallet = Wallet::create(descriptor, change_descriptor) + ///    .network(Network::Testnet) + ///  .create_wallet_no_persist()?; + /// + /// let address = wallet.peek_address(KeychainKind::External, 0); + /// + /// // Build a PSBT using the wallet + /// let mut tx_builder = wallet.build_tx(); + /// tx_builder.add_recipient(address.script_pubkey(), Amount::from_sat(50_000)); + /// let mut psbt = tx_builder.finish().unwrap(); + /// + /// // Sign the PSBT directly using PsbtExt::sign with the wallet's keymap + /// let keymap = wallet.get_keymap(); + /// psbt.sign(&keymap, &wallet.secp_ctx()).expect("Failed to sign PSBT"); + /// + /// Ok::<(), Box>(()) + /// ``` + pub fn get_keymap(&self) -> KeyMap { + let mut keymap = self.signers.as_key_map(&self.secp); + keymap.extend(self.change_signers.as_key_map(&self.secp)); + keymap + } + /// Start building a transaction. /// /// This returns a blank [`TxBuilder`] from which you can specify the parameters for the @@ -2925,4 +2962,43 @@ mod test { let wallet = params.network(Network::Testnet).create_wallet_no_persist(); assert!(wallet.is_err()); } + + #[test] + fn test_get_keymap() { + use crate::test_utils::get_test_wpkh_and_change_desc; + let (external_desc, internal_desc) = get_test_wpkh_and_change_desc(); + + // Create wallet with private key descriptors + let wallet = Wallet::create(external_desc, internal_desc) + .network(Network::Testnet) + .create_wallet_no_persist() + .unwrap(); + + // Get the combined keymap + let keymap = wallet.get_keymap(); + + // Verify keymap is not empty + assert!(!keymap.is_empty()); + + // Get individual keymaps for comparison + let external_keymap = wallet + .get_signers(KeychainKind::External) + .as_key_map(wallet.secp_ctx()); + let internal_keymap = wallet + .get_signers(KeychainKind::Internal) + .as_key_map(wallet.secp_ctx()); + + // Verify combined keymap contains all keys from both signers + assert!(keymap.len() >= external_keymap.len() + internal_keymap.len()); + + // Verify all external keys are present + for (pubkey, _) in external_keymap.iter() { + assert!(keymap.contains_key(pubkey)); + } + + // Verify all internal keys are present + for (pubkey, _) in internal_keymap.iter() { + assert!(keymap.contains_key(pubkey)); + } + } } From 4e432aad919d5aa46f7c3dfd3c46e148353f593e Mon Sep 17 00:00:00 2001 From: GideonBature Date: Tue, 28 Oct 2025 12:56:53 +0100 Subject: [PATCH 2/2] feat(bdk_wallet): add get_keymap() helper method to get the KeyMap for Psbt.sign --- src/wallet/mod.rs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index bbf711c0..8f6e5d26 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -1305,34 +1305,22 @@ impl Wallet { } } - /// Get the combined KeyMap for all signers in the wallet. + /// Get KeyMap /// - /// This includes keys from both external and internal (change) signers. - /// The returned KeyMap can be used with `PsbtExt::sign` to sign PSBTs directly. - /// - /// # Example + /// ## Example /// /// ```rust,no_run - /// use bdk_wallet::{KeychainKind, Wallet}; - /// use miniscript::psbt::PsbtExt; - /// use bdk_wallet::bitcoin::{Amount, Network}; + /// # use bdk_wallet::{KeychainKind, Wallet}; + /// # use bdk_wallet::bitcoin::Network; /// /// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)"; /// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)"; /// let wallet = Wallet::create(descriptor, change_descriptor) - ///    .network(Network::Testnet) - ///  .create_wallet_no_persist()?; - /// - /// let address = wallet.peek_address(KeychainKind::External, 0); - /// - /// // Build a PSBT using the wallet - /// let mut tx_builder = wallet.build_tx(); - /// tx_builder.add_recipient(address.script_pubkey(), Amount::from_sat(50_000)); - /// let mut psbt = tx_builder.finish().unwrap(); + /// .network(Network::Testnet) + /// .create_wallet_no_persist()?; /// - /// // Sign the PSBT directly using PsbtExt::sign with the wallet's keymap /// let keymap = wallet.get_keymap(); - /// psbt.sign(&keymap, &wallet.secp_ctx()).expect("Failed to sign PSBT"); + /// // Use the keymap with miniscript's PSBT signing or other cryptographic operations /// /// Ok::<(), Box>(()) /// ```