Skip to content

Commit df0e410

Browse files
committed
add AddressIndex::FirstUnused
Signed-off-by: nickfarrow <[email protected]>
1 parent 3b029eb commit df0e410

File tree

1 file changed

+62
-3
lines changed

1 file changed

+62
-3
lines changed

src/wallet/mod.rs

+62-3
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ pub enum AddressIndex {
116116
/// caller is untrusted; for example when deriving donation addresses on-demand for a public
117117
/// web page.
118118
LastUnused,
119+
/// Return the address for the first address in the keychain that has not been used in a received
120+
/// transaction. Otherwise return a new address as with [`AddressIndex::New`].
121+
///
122+
/// Use with caution, if the wallet has not yet detected an address has been used it could
123+
/// return an already used address. This function is primarily meant for making use of addresses earlier
124+
/// in the keychain that were infact never used.
125+
FirstUnused,
119126
/// Return the address for a specific descriptor index. Does not change the current descriptor
120127
/// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
121128
///
@@ -290,7 +297,7 @@ where
290297

291298
// Return the the last previously derived address for `keychain` if it has not been used in a
292299
// received transaction. Otherwise return a new address using [`Wallet::get_new_address`].
293-
fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
300+
fn get_last_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
294301
let mut unused_key_indexes = self.get_unused_key_indexes(keychain)?;
295302
match unused_key_indexes.pop() {
296303
None => self.get_new_address(keychain),
@@ -324,6 +331,27 @@ where
324331
}
325332
}
326333

334+
// Return the the first address in the keychain which has not been a recipient of a transaction
335+
fn get_first_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
336+
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
337+
if unused_key_indexes.is_empty() {
338+
self.get_new_address(keychain)
339+
} else {
340+
let derived_key = self
341+
.get_descriptor_for_keychain(keychain)
342+
.as_derived(unused_key_indexes[0], &self.secp);
343+
344+
derived_key
345+
.address(self.network)
346+
.map(|address| AddressInfo {
347+
address,
348+
index: unused_key_indexes[0],
349+
keychain,
350+
})
351+
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
352+
}
353+
}
354+
327355
// Return derived address for the descriptor of given [`KeychainKind`] at a specific index
328356
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
329357
self.get_descriptor_for_keychain(keychain)
@@ -378,7 +406,8 @@ where
378406
) -> Result<AddressInfo, Error> {
379407
match address_index {
380408
AddressIndex::New => self.get_new_address(keychain),
381-
AddressIndex::LastUnused => self.get_unused_address(keychain),
409+
AddressIndex::LastUnused => self.get_last_unused_address(keychain),
410+
AddressIndex::FirstUnused => self.get_first_unused_address(keychain),
382411
AddressIndex::Peek(index) => self.peek_address(index, keychain),
383412
AddressIndex::Reset(index) => self.reset_address(index, keychain),
384413
}
@@ -1703,7 +1732,7 @@ pub(crate) mod test {
17031732

17041733
use super::*;
17051734
use crate::signer::{SignOptions, SignerError};
1706-
use crate::wallet::AddressIndex::{LastUnused, New, Peek, Reset};
1735+
use crate::wallet::AddressIndex::{FirstUnused, LastUnused, New, Peek, Reset};
17071736

17081737
#[test]
17091738
fn test_cache_addresses_fixed() {
@@ -3911,6 +3940,36 @@ pub(crate) mod test {
39113940
);
39123941
}
39133942

3943+
#[test]
3944+
fn test_firstunused_address() {
3945+
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3946+
let descriptors = testutils!(@descriptors (descriptor));
3947+
let wallet = Wallet::new(
3948+
&descriptors.0,
3949+
None,
3950+
Network::Testnet,
3951+
MemoryDatabase::new(),
3952+
)
3953+
.unwrap();
3954+
3955+
assert_eq!(
3956+
wallet.get_address(FirstUnused).unwrap().to_string(),
3957+
"tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
3958+
);
3959+
3960+
// use the first address
3961+
crate::populate_test_db!(
3962+
wallet.database.borrow_mut(),
3963+
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3964+
Some(100),
3965+
);
3966+
3967+
assert_eq!(
3968+
wallet.get_address(FirstUnused).unwrap().to_string(),
3969+
"tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3970+
);
3971+
}
3972+
39143973
#[test]
39153974
fn test_peek_address_at_index() {
39163975
let db = MemoryDatabase::new();

0 commit comments

Comments
 (0)