]> Untitled Git - bdk/commitdiff
[wallet] Add get_address(AddressIndex::Reset(u32)), update CHANGELOG
authorSteve Myers <steve@notmandatory.org>
Wed, 10 Mar 2021 23:58:58 +0000 (15:58 -0800)
committerSteve Myers <steve@notmandatory.org>
Mon, 15 Mar 2021 16:13:23 +0000 (09:13 -0700)
CHANGELOG.md
src/wallet/mod.rs

index 2fb53a4eebfb3fe74e24a1df1cc53ce2c2d71d1f..a5646fb1ab5be560ae2906b45fdb98875a4f4991 100644 (file)
@@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 #### Added
 - Added `get_address(AddressIndex::LastUnused)` which returns the last derived address if it has not been used or if used in a received transaction returns a new address
-- Added `get_address(AddressIndex::Peek(u32))` which returns a derived address for a specified descriptor index
+- Added `get_address(AddressIndex::Peek(u32))` which returns a derived address for a specified descriptor index but does not change the current index
+- Added `get_address(AddressIndex::Reset(u32))` which returns a derived address for a specified descriptor index and resets current index to the given value
 
 ## [v0.4.0] - [v0.3.0]
 
index 406ca3c7f2fe202a98379397edf2483fa0cb782a..7594033bc6f27bab156ee02dcd142ea32e6f8065 100644 (file)
@@ -163,15 +163,9 @@ where
 }
 
 /// The address index selection strategy to use to derived an address from the wallet's external
-/// descriptor. See [`Wallet::get_address`].
+/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
 #[derive(Debug)]
 pub enum AddressIndex {
-    /// Return the address for a specific descriptor index. Does not change the current descriptor
-    /// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
-    ///
-    /// Use with caution, if an index is given that is less than the current descriptor index
-    /// then the returned address may have already been used.
-    Peek(u32),
     /// Return a new address after incrementing the current descriptor index.
     New,
     /// Return the address for the current descriptor index if it has not been used in a received
@@ -182,6 +176,21 @@ pub enum AddressIndex {
     /// caller is untrusted; for example when deriving donation addresses on-demand for a public
     /// web page.
     LastUnused,
+    /// Return the address for a specific descriptor index. Does not change the current descriptor
+    /// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
+    ///
+    /// Use with caution, if an index is given that is less than the current descriptor index
+    /// then the returned address may have already been used.
+    Peek(u32),
+    /// Return the address for a specific descriptor index and reset the current descriptor index
+    /// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value.
+    ///
+    /// Use with caution, if an index is given that is less than the current descriptor index
+    /// then the returned address and subsequent addresses returned by calls to `AddressIndex::New`
+    /// and `AddressIndex::LastUsed` may have already been used. Also if the index is reset to a
+    /// value earlier than the [`crate::blockchain::Blockchain`] stop_gap (default is 20) then a
+    /// larger stop_gap should be used to monitor for all possibly used addresses.  
+    Reset(u32),
 }
 
 // offline actions, always available
@@ -189,14 +198,6 @@ impl<B, D> Wallet<B, D>
 where
     D: BatchDatabase,
 {
-    // Return derived address for the external descriptor at a specific index
-    fn peek_address(&self, index: u32) -> Result<Address, Error> {
-        self.descriptor
-            .as_derived(index, &self.secp)
-            .address(self.network)
-            .map_err(|_| Error::ScriptDoesntHaveAddressForm)
-    }
-
     // Return a newly derived address using the external descriptor
     fn get_new_address(&self) -> Result<Address, Error> {
         let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
@@ -232,14 +233,34 @@ where
         }
     }
 
+    // Return derived address for the external descriptor at a specific index
+    fn peek_address(&self, index: u32) -> Result<Address, Error> {
+        self.descriptor
+            .as_derived(index, &self.secp)
+            .address(self.network)
+            .map_err(|_| Error::ScriptDoesntHaveAddressForm)
+    }
+
+    // Return derived address for the external descriptor at a specific index and reset current
+    // address index
+    fn reset_address(&self, index: u32) -> Result<Address, Error> {
+        self.set_index(KeychainKind::External, index)?;
+
+        self.descriptor
+            .as_derived(index, &self.secp)
+            .address(self.network)
+            .map_err(|_| Error::ScriptDoesntHaveAddressForm)
+    }
+
     /// Return a derived address using the external descriptor, see [`AddressIndex`] for
-    /// available address index selection strategies. If the wallet descriptor is not derivable
+    /// 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<Address, 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),
         }
     }
 
@@ -1047,6 +1068,11 @@ where
         }
     }
 
+    fn set_index(&self, keychain: KeychainKind, index: u32) -> Result<(), Error> {
+        self.database.borrow_mut().set_last_index(keychain, index)?;
+        Ok(())
+    }
+
     fn cache_addresses(
         &self,
         keychain: KeychainKind,
@@ -1450,7 +1476,7 @@ mod test {
     use crate::types::KeychainKind;
 
     use super::*;
-    use crate::wallet::AddressIndex::{LastUnused, New, Peek};
+    use crate::wallet::AddressIndex::{LastUnused, New, Peek, Reset};
 
     #[test]
     fn test_cache_addresses_fixed() {
@@ -3639,4 +3665,41 @@ mod test {
             "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
         );
     }
+
+    #[test]
+    fn test_reset_address_index() {
+        let db = MemoryDatabase::new();
+        let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
+                                         None, Network::Testnet, db).unwrap();
+
+        // new index 0
+        assert_eq!(
+            wallet.get_address(New).unwrap().to_string(),
+            "tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
+        );
+
+        // new index 1
+        assert_eq!(
+            wallet.get_address(New).unwrap().to_string(),
+            "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
+        );
+
+        // new index 2
+        assert_eq!(
+            wallet.get_address(New).unwrap().to_string(),
+            "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
+        );
+
+        //  reset index 1 again
+        assert_eq!(
+            wallet.get_address(Reset(1)).unwrap().to_string(),
+            "tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
+        );
+
+        // new index 2 again
+        assert_eq!(
+            wallet.get_address(New).unwrap().to_string(),
+            "tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2"
+        );
+    }
 }