Skip to content

Commit 5daf23a

Browse files
committed
add get_batch_unused_addresses
Signed-off-by: nickfarrow <[email protected]>
1 parent 461255c commit 5daf23a

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag.
1111
- `verify` flag removed from `TransactionDetails`.
1212
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
13+
- Changed `AddressIndex::LastUnused` to look back further than `current_index`, and only return a new address if all have been used.
14+
- Add `AddressIndex::FirstUnused` to get unused addresses from the beginning of the keychain.
15+
- Add `wallet.get_batch_unused_addresses` to return vector of N unused addresses, populating any remaining with new addresses.
1316

1417
## [v0.16.0] - [v0.15.0]
1518

src/wallet/mod.rs

+77
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)
@@ -3925,6 +3954,54 @@ pub(crate) mod test {
39253954
);
39263955
}
39273956

3957+
#[test]
3958+
fn test_batch_unused_addresses() {
3959+
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3960+
let descriptors = testutils!(@descriptors (descriptor));
3961+
let wallet = Wallet::new_offline(
3962+
&descriptors.0,
3963+
None,
3964+
Network::Testnet,
3965+
MemoryDatabase::new(),
3966+
)
3967+
.unwrap();
3968+
3969+
// get first two addresses, moving index
3970+
for _ in 0..2 {
3971+
let _ = wallet.get_address(New);
3972+
}
3973+
3974+
// use the second address
3975+
crate::populate_test_db!(
3976+
wallet.database.borrow_mut(),
3977+
testutils! (@tx ( (@external descriptors, 1) => 25_000 ) (@confirmations 1)),
3978+
Some(100),
3979+
);
3980+
3981+
assert_eq!(
3982+
wallet
3983+
.get_batch_unused_addresses(3, KeychainKind::External)
3984+
.unwrap(),
3985+
vec![
3986+
AddressInfo {
3987+
index: 0,
3988+
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
3989+
.unwrap(),
3990+
},
3991+
AddressInfo {
3992+
index: 2,
3993+
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
3994+
.unwrap(),
3995+
},
3996+
AddressInfo {
3997+
index: 3,
3998+
address: Address::from_str("tb1q32a23q6u3yy89l8svrt80a54h06qvn7gnuvsen")
3999+
.unwrap(),
4000+
}
4001+
]
4002+
);
4003+
}
4004+
39284005
#[test]
39294006
fn test_peek_address_at_index() {
39304007
let db = MemoryDatabase::new();

0 commit comments

Comments
 (0)