Skip to content

Commit 7b36e47

Browse files
committed
add get_batch_unused_addresses
Signed-off-by: nickfarrow <[email protected]>
1 parent 2835b09 commit 7b36e47

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77
## [Unreleased]
88

99
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
10+
- Changed `AddressIndex::LastUnused` to look back further than `current_index`, and only return a new address if all have been used.
11+
- Add `AddressIndex::FirstUnused` to get unused addresses from the beginning of the keychain.
12+
- Add `wallet.get_batch_unused_addresses` to return vector of N unused addresses, populating any remaining with new addresses.
1013

1114
## [v0.16.0] - [v0.15.0]
1215

src/wallet/mod.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,35 @@ where
391391
}
392392
}
393393

394+
/// Return vector of n unused addresses from the [`KeychainKind`].
395+
/// If less than n unused addresses are returned, the rest will be populated by new addresses.
396+
/// The unused addresses returned are in order of oldest in keychain first, with increasing index.
397+
pub fn get_batch_unused_addresses(
398+
&self,
399+
n: usize,
400+
keychain: KeychainKind,
401+
) -> Result<Vec<AddressInfo>, Error> {
402+
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
403+
let mut addresses = unused_key_indexes
404+
.iter()
405+
.map(|i| {
406+
let derived_key = self
407+
.get_descriptor_for_keychain(keychain)
408+
.as_derived(*i, &self.secp);
409+
410+
derived_key
411+
.address(self.network)
412+
.map(|address| AddressInfo { address, index: *i })
413+
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
414+
})
415+
.take(n)
416+
.collect::<Result<Vec<_>, _>>()?;
417+
for _ in 0..(n - addresses.len()) {
418+
addresses.push(self.get_new_address(keychain)?)
419+
}
420+
Ok(addresses)
421+
}
422+
394423
/// Return whether or not a `script` is part of this wallet (either internal or external)
395424
pub fn is_mine(&self, script: &Script) -> Result<bool, Error> {
396425
self.database.borrow().is_mine(script)
@@ -3943,6 +3972,54 @@ pub(crate) mod test {
39433972
);
39443973
}
39453974

3975+
#[test]
3976+
fn test_batch_unused_addresses() {
3977+
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3978+
let descriptors = testutils!(@descriptors (descriptor));
3979+
let wallet = Wallet::new_offline(
3980+
&descriptors.0,
3981+
None,
3982+
Network::Testnet,
3983+
MemoryDatabase::new(),
3984+
)
3985+
.unwrap();
3986+
3987+
// get first two addresses, moving index
3988+
for _ in 0..2 {
3989+
let _ = wallet.get_address(New);
3990+
}
3991+
3992+
// use the second address
3993+
crate::populate_test_db!(
3994+
wallet.database.borrow_mut(),
3995+
testutils! (@tx ( (@external descriptors, 1) => 25_000 ) (@confirmations 1)),
3996+
Some(100),
3997+
);
3998+
3999+
assert_eq!(
4000+
wallet
4001+
.get_batch_unused_addresses(3, KeychainKind::External)
4002+
.unwrap(),
4003+
vec![
4004+
AddressInfo {
4005+
index: 0,
4006+
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
4007+
.unwrap(),
4008+
},
4009+
AddressInfo {
4010+
index: 2,
4011+
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
4012+
.unwrap(),
4013+
},
4014+
AddressInfo {
4015+
index: 3,
4016+
address: Address::from_str("tb1q32a23q6u3yy89l8svrt80a54h06qvn7gnuvsen")
4017+
.unwrap(),
4018+
}
4019+
]
4020+
);
4021+
}
4022+
39464023
#[test]
39474024
fn test_peek_address_at_index() {
39484025
let db = MemoryDatabase::new();

0 commit comments

Comments
 (0)