Skip to content

Commit 8bf61de

Browse files
committed
add get_batch_unused_addresses
Signed-off-by: nickfarrow <[email protected]>
1 parent cd80af0 commit 8bf61de

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
@@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
1212
- added `ensure_addresses_cached` to `Wallet` to let offline wallets load and cache addresses in their database
1313
- Add `is_spent` field to `LocalUtxo`; when we notice that a utxo has been spent we set `is_spent` field to true instead of deleting it from the db.
14+
- Changed `AddressIndex::LastUnused` to look back further than `current_index`, and only return a new address if all have been used.
15+
- Add `AddressIndex::FirstUnused` to get unused addresses from the beginning of the keychain.
16+
- Add `wallet.get_batch_unused_addresses` to return vector of N unused addresses, populating any remaining with new addresses.
1417

1518
### Sync API change
1619

src/wallet/mod.rs

+77
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,35 @@ where
431431
Ok(new_addresses_cached)
432432
}
433433

434+
/// Return vector of n unused addresses from the [`KeychainKind`].
435+
/// If less than n unused addresses are returned, the rest will be populated by new addresses.
436+
/// The unused addresses returned are in order of oldest in keychain first, with increasing index.
437+
pub fn get_batch_unused_addresses(
438+
&self,
439+
n: usize,
440+
keychain: KeychainKind,
441+
) -> Result<Vec<AddressInfo>, Error> {
442+
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
443+
let mut addresses = unused_key_indexes
444+
.iter()
445+
.map(|i| {
446+
let derived_key = self
447+
.get_descriptor_for_keychain(keychain)
448+
.as_derived(*i, &self.secp);
449+
450+
derived_key
451+
.address(self.network)
452+
.map(|address| AddressInfo { address, index: *i })
453+
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
454+
})
455+
.take(n)
456+
.collect::<Result<Vec<_>, _>>()?;
457+
for _ in 0..(n - addresses.len()) {
458+
addresses.push(self.get_new_address(keychain)?)
459+
}
460+
Ok(addresses)
461+
}
462+
434463
/// Return whether or not a `script` is part of this wallet (either internal or external)
435464
pub fn is_mine(&self, script: &Script) -> Result<bool, Error> {
436465
self.database.borrow().is_mine(script)
@@ -3904,6 +3933,54 @@ pub(crate) mod test {
39043933
);
39053934
}
39063935

3936+
#[test]
3937+
fn test_batch_unused_addresses() {
3938+
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
3939+
let descriptors = testutils!(@descriptors (descriptor));
3940+
let wallet = Wallet::new(
3941+
&descriptors.0,
3942+
None,
3943+
Network::Testnet,
3944+
MemoryDatabase::new(),
3945+
)
3946+
.unwrap();
3947+
3948+
// get first two addresses, moving index
3949+
for _ in 0..2 {
3950+
let _ = wallet.get_address(New);
3951+
}
3952+
3953+
// use the second address
3954+
crate::populate_test_db!(
3955+
wallet.database.borrow_mut(),
3956+
testutils! (@tx ( (@external descriptors, 1) => 25_000 ) (@confirmations 1)),
3957+
Some(100),
3958+
);
3959+
3960+
assert_eq!(
3961+
wallet
3962+
.get_batch_unused_addresses(3, KeychainKind::External)
3963+
.unwrap(),
3964+
vec![
3965+
AddressInfo {
3966+
index: 0,
3967+
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
3968+
.unwrap(),
3969+
},
3970+
AddressInfo {
3971+
index: 2,
3972+
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
3973+
.unwrap(),
3974+
},
3975+
AddressInfo {
3976+
index: 3,
3977+
address: Address::from_str("tb1q32a23q6u3yy89l8svrt80a54h06qvn7gnuvsen")
3978+
.unwrap(),
3979+
}
3980+
]
3981+
);
3982+
}
3983+
39073984
#[test]
39083985
fn test_peek_address_at_index() {
39093986
let db = MemoryDatabase::new();

0 commit comments

Comments
 (0)