Skip to content

Commit 461255c

Browse files
committed
add AddressIndex::FirstUnused
Signed-off-by: nickfarrow <[email protected]>
1 parent 6a08f81 commit 461255c

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
@@ -192,6 +192,13 @@ pub enum AddressIndex {
192192
/// caller is untrusted; for example when deriving donation addresses on-demand for a public
193193
/// web page.
194194
LastUnused,
195+
/// Return the address for the first address in the keychain that has not been used in a received
196+
/// transaction. Otherwise return a new address as with [`AddressIndex::New`].
197+
///
198+
/// Use with caution, if the wallet has not yet detected an address has been used it could
199+
/// return an already used address. This function is primarily meant for making use of addresses earlier
200+
/// in the keychain that were infact never used.
201+
FirstUnused,
195202
/// Return the address for a specific descriptor index. Does not change the current descriptor
196203
/// index used by `AddressIndex::New` and `AddressIndex::LastUsed`.
197204
///
@@ -291,7 +298,7 @@ where
291298

292299
// Return the the last previously derived address for `keychain` if it has not been used in a
293300
// received transaction. Otherwise return a new address using [`Wallet::get_new_address`].
294-
fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
301+
fn get_last_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
295302
let mut unused_key_indexes = self.get_unused_key_indexes(keychain)?;
296303
match unused_key_indexes.pop() {
297304
None => self.get_new_address(keychain),
@@ -311,6 +318,26 @@ where
311318
}
312319
}
313320

321+
// Return the the first address in the keychain which has not been a recipient of a transaction
322+
fn get_first_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
323+
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
324+
if unused_key_indexes.is_empty() {
325+
self.get_new_address(keychain)
326+
} else {
327+
let derived_key = self
328+
.get_descriptor_for_keychain(keychain)
329+
.as_derived(unused_key_indexes[0], &self.secp);
330+
331+
derived_key
332+
.address(self.network)
333+
.map(|address| AddressInfo {
334+
address,
335+
index: unused_key_indexes[0],
336+
})
337+
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
338+
}
339+
}
340+
314341
// Return derived address for the descriptor of given [`KeychainKind`] at a specific index
315342
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
316343
self.get_descriptor_for_keychain(keychain)
@@ -357,7 +384,8 @@ where
357384
) -> Result<AddressInfo, Error> {
358385
match address_index {
359386
AddressIndex::New => self.get_new_address(keychain),
360-
AddressIndex::LastUnused => self.get_unused_address(keychain),
387+
AddressIndex::LastUnused => self.get_last_unused_address(keychain),
388+
AddressIndex::FirstUnused => self.get_first_unused_address(keychain),
361389
AddressIndex::Peek(index) => self.peek_address(index, keychain),
362390
AddressIndex::Reset(index) => self.reset_address(index, keychain),
363391
}
@@ -1659,7 +1687,7 @@ pub(crate) mod test {
16591687

16601688
use super::*;
16611689
use crate::signer::{SignOptions, SignerError};
1662-
use crate::wallet::AddressIndex::{LastUnused, New, Peek, Reset};
1690+
use crate::wallet::AddressIndex::{FirstUnused, LastUnused, New, Peek, Reset};
16631691

16641692
#[test]
16651693
fn test_cache_addresses_fixed() {
@@ -3867,6 +3895,36 @@ pub(crate) mod test {
38673895
);
38683896
}
38693897

3898+
#[test]
3899+
fn test_firstunused_address() {
3900+
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3901+
let descriptors = testutils!(@descriptors (descriptor));
3902+
let wallet = Wallet::new_offline(
3903+
&descriptors.0,
3904+
None,
3905+
Network::Testnet,
3906+
MemoryDatabase::new(),
3907+
)
3908+
.unwrap();
3909+
3910+
assert_eq!(
3911+
wallet.get_address(FirstUnused).unwrap().to_string(),
3912+
"tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
3913+
);
3914+
3915+
// use the first address
3916+
crate::populate_test_db!(
3917+
wallet.database.borrow_mut(),
3918+
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
3919+
Some(100),
3920+
);
3921+
3922+
assert_eq!(
3923+
wallet.get_address(FirstUnused).unwrap().to_string(),
3924+
"tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
3925+
);
3926+
}
3927+
38703928
#[test]
38713929
fn test_peek_address_at_index() {
38723930
let db = MemoryDatabase::new();

0 commit comments

Comments
 (0)