]> Untitled Git - bdk/commitdiff
Add API for internal addresses
authorLLFourn <lloyd.fourn@gmail.com>
Thu, 19 Aug 2021 09:57:35 +0000 (19:57 +1000)
committerLLFourn <lloyd.fourn@gmail.com>
Tue, 22 Feb 2022 22:28:08 +0000 (09:28 +1100)
There are good reasons for applications to need to get internal
addresses too. For example creating a transactions that splits an output
into several smaller ones.

CHANGELOG.md
src/testutils/blockchain_tests.rs
src/wallet/mod.rs

index 75801e01a950accf5b8b08185530e0f1031bc637..ff48d6f56e898b3186e1d855a53e4132d2f87317 100644 (file)
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0
 - Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag.
 - `verify` flag removed from `TransactionDetails`.
+- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
 
 ## [v0.16.0] - [v0.15.0]
 
index 02047d678fdef0151cc8dd53b736f83879ac3b77..30d926cee4d83398efc9f2330fee6499c59c8ae1 100644 (file)
@@ -815,7 +815,7 @@ macro_rules! bdk_blockchain_tests {
 
                 let mut builder = wallet.build_fee_bump(details.txid).unwrap();
                 builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
-                let (mut new_psbt, new_details) = builder.finish().unwrap();
+                let (mut new_psbt, new_details) = builder.finish().expect("fee bump tx");
                 let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
                 assert!(finalized, "Cannot finalize transaction");
                 wallet.broadcast(&new_psbt.extract_tx()).unwrap();
index a24f922dcddebe13b6cb8a32760b68b94052ca51..28c2129e5fa4874fca58f57a8dd02cca6b257532 100644 (file)
@@ -238,11 +238,11 @@ where
     D: BatchDatabase,
 {
     // Return a newly derived address using the external descriptor
-    fn get_new_address(&self) -> Result<AddressInfo, Error> {
-        let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
+    fn get_new_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
+        let incremented_index = self.fetch_and_increment_index(keychain)?;
 
         let address_result = self
-            .descriptor
+            .get_descriptor_for_keychain(keychain)
             .as_derived(incremented_index, &self.secp)
             .address(self.network);
 
@@ -256,10 +256,12 @@ where
 
     // Return the the last previously derived address if it has not been used in a received
     // transaction. Otherwise return a new address using [`Wallet::get_new_address`].
-    fn get_unused_address(&self) -> Result<AddressInfo, Error> {
-        let current_index = self.fetch_index(KeychainKind::External)?;
+    fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
+        let current_index = self.fetch_index(keychain)?;
 
-        let derived_key = self.descriptor.as_derived(current_index, &self.secp);
+        let derived_key = self
+            .get_descriptor_for_keychain(keychain)
+            .as_derived(current_index, &self.secp);
 
         let script_pubkey = derived_key.script_pubkey();
 
@@ -271,7 +273,7 @@ where
             .any(|o| o.script_pubkey == script_pubkey);
 
         if found_used {
-            self.get_new_address()
+            self.get_new_address(keychain)
         } else {
             derived_key
                 .address(self.network)
@@ -284,8 +286,8 @@ where
     }
 
     // Return derived address for the external descriptor at a specific index
-    fn peek_address(&self, index: u32) -> Result<AddressInfo, Error> {
-        self.descriptor
+    fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
+        self.get_descriptor_for_keychain(keychain)
             .as_derived(index, &self.secp)
             .address(self.network)
             .map(|address| AddressInfo { index, address })
@@ -294,10 +296,10 @@ where
 
     // Return derived address for the external descriptor at a specific index and reset current
     // address index
-    fn reset_address(&self, index: u32) -> Result<AddressInfo, Error> {
-        self.set_index(KeychainKind::External, index)?;
+    fn reset_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
+        self.set_index(keychain, index)?;
 
-        self.descriptor
+        self.get_descriptor_for_keychain(keychain)
             .as_derived(index, &self.secp)
             .address(self.network)
             .map(|address| AddressInfo { index, address })
@@ -308,11 +310,30 @@ where
     /// available address index selection strategies. If none of the keys in the descriptor are derivable
     /// (ie. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
     pub fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
+        self._get_address(address_index, KeychainKind::External)
+    }
+
+    /// Return a derived address using the internal (change) descriptor.
+    ///
+    /// If the wallet doesn't have an internal descriptor it will use the external descriptor.
+    ///
+    /// see [`AddressIndex`] for available address index selection strategies. If none of the keys
+    /// in the descriptor are derivable (ie. does not end with /*) then the same address will always
+    /// be returned for any [`AddressIndex`].
+    pub fn get_internal_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
+        self._get_address(address_index, KeychainKind::Internal)
+    }
+
+    fn _get_address(
+        &self,
+        address_index: AddressIndex,
+        keychain: KeychainKind,
+    ) -> Result<AddressInfo, Error> {
         match address_index {
-            AddressIndex::New => self.get_new_address(),
-            AddressIndex::LastUnused => self.get_unused_address(),
-            AddressIndex::Peek(index) => self.peek_address(index),
-            AddressIndex::Reset(index) => self.reset_address(index),
+            AddressIndex::New => self.get_new_address(keychain),
+            AddressIndex::LastUnused => self.get_unused_address(keychain),
+            AddressIndex::Peek(index) => self.peek_address(index, keychain),
+            AddressIndex::Reset(index) => self.reset_address(index, keychain),
         }
     }
 
@@ -662,7 +683,10 @@ where
         let mut drain_output = {
             let script_pubkey = match params.drain_to {
                 Some(ref drain_recipient) => drain_recipient.clone(),
-                None => self.get_change_address()?,
+                None => self
+                    .get_internal_address(AddressIndex::New)?
+                    .address
+                    .script_pubkey(),
             };
 
             TxOut {
@@ -1091,13 +1115,6 @@ where
             .map(|(desc, child)| desc.as_derived(child, &self.secp)))
     }
 
-    fn get_change_address(&self) -> Result<Script, Error> {
-        let (desc, keychain) = self._get_descriptor_for_keychain(KeychainKind::Internal);
-        let index = self.fetch_and_increment_index(keychain)?;
-
-        Ok(desc.as_derived(index, &self.secp).script_pubkey())
-    }
-
     fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
         let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
         let index = match descriptor.is_deriveable() {
@@ -3987,6 +4004,48 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 45_000);
         builder.finish().unwrap();
     }
+
+    #[test]
+    fn test_get_address() {
+        use crate::descriptor::template::Bip84;
+        let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
+        let wallet = Wallet::new_offline(
+            Bip84(key, KeychainKind::External),
+            Some(Bip84(key, KeychainKind::Internal)),
+            Network::Regtest,
+            MemoryDatabase::default(),
+        )
+        .unwrap();
+
+        assert_eq!(
+            wallet.get_address(AddressIndex::New).unwrap().address,
+            Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap()
+        );
+        assert_eq!(
+            wallet
+                .get_internal_address(AddressIndex::New)
+                .unwrap()
+                .address,
+            Address::from_str("bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa").unwrap()
+        );
+
+        let wallet = Wallet::new_offline(
+            Bip84(key, KeychainKind::External),
+            None,
+            Network::Regtest,
+            MemoryDatabase::default(),
+        )
+        .unwrap();
+
+        assert_eq!(
+            wallet
+                .get_internal_address(AddressIndex::New)
+                .unwrap()
+                .address,
+            Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap(),
+            "when there's no internal descriptor it should just use external"
+        );
+    }
 }
 
 /// Deterministically generate a unique name given the descriptors defining the wallet