Skip to content

Commit f429d4e

Browse files
committed
add AddressIndex::FirstUnused
Signed-off-by: nickfarrow <[email protected]>
1 parent 11ab617 commit f429d4e

File tree

1 file changed

+61
-3
lines changed

1 file changed

+61
-3
lines changed

src/wallet/mod.rs

+61-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
///
@@ -287,7 +294,7 @@ where
287294

288295
// Return the the last previously derived address for `keychain` if it has not been used in a
289296
// received transaction. Otherwise return a new address using [`Wallet::get_new_address`].
290-
fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
297+
fn get_last_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
291298
let mut unused_key_indexes = self.get_unused_key_indexes(keychain)?;
292299
match unused_key_indexes.pop() {
293300
None => self.get_new_address(keychain),
@@ -307,6 +314,26 @@ where
307314
}
308315
}
309316

317+
// Return the the first address in the keychain which has not been a recipient of a transaction
318+
fn get_first_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
319+
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
320+
if unused_key_indexes.is_empty() {
321+
self.get_new_address(keychain)
322+
} else {
323+
let derived_key = self
324+
.get_descriptor_for_keychain(keychain)
325+
.as_derived(unused_key_indexes[0], &self.secp);
326+
327+
derived_key
328+
.address(self.network)
329+
.map(|address| AddressInfo {
330+
address,
331+
index: unused_key_indexes[0],
332+
})
333+
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
334+
}
335+
}
336+
310337
// Return derived address for the descriptor of given [`KeychainKind`] at a specific index
311338
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
312339
self.get_descriptor_for_keychain(keychain)
@@ -353,7 +380,8 @@ where
353380
) -> Result<AddressInfo, Error> {
354381
match address_index {
355382
AddressIndex::New => self.get_new_address(keychain),
356-
AddressIndex::LastUnused => self.get_unused_address(keychain),
383+
AddressIndex::LastUnused => self.get_last_unused_address(keychain),
384+
AddressIndex::FirstUnused => self.get_first_unused_address(keychain),
357385
AddressIndex::Peek(index) => self.peek_address(index, keychain),
358386
AddressIndex::Reset(index) => self.reset_address(index, keychain),
359387
}
@@ -1638,7 +1666,7 @@ pub(crate) mod test {
16381666

16391667
use super::*;
16401668
use crate::signer::{SignOptions, SignerError};
1641-
use crate::wallet::AddressIndex::{LastUnused, New, Peek, Reset};
1669+
use crate::wallet::AddressIndex::{FirstUnused, LastUnused, New, Peek, Reset};
16421670

16431671
#[test]
16441672
fn test_cache_addresses_fixed() {
@@ -3846,6 +3874,36 @@ pub(crate) mod test {
38463874
);
38473875
}
38483876

3877+
#[test]
3878+
fn test_firstunused_address() {
3879+
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3880+
let descriptors = testutils!(@descriptors (descriptor));
3881+
let wallet = Wallet::new_offline(
3882+
&descriptors.0,
3883+
None,
3884+
Network::Testnet,
3885+
MemoryDatabase::new(),
3886+
)
3887+
.unwrap();
3888+
3889+
assert_eq!(
3890+
wallet.get_address(FirstUnused).unwrap().to_string(),
3891+
"tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
3892+
);
3893+
3894+
// use the first address
3895+
crate::populate_test_db!(
3896+
wallet.database.borrow_mut(),
3897+
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3898+
Some(100),
3899+
);
3900+
3901+
assert_eq!(
3902+
wallet.get_address(FirstUnused).unwrap().to_string(),
3903+
"tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3904+
);
3905+
}
3906+
38493907
#[test]
38503908
fn test_peek_address_at_index() {
38513909
let db = MemoryDatabase::new();

0 commit comments

Comments
 (0)