Skip to content

Commit

Permalink
add get_batch_unused_addresses
Browse files Browse the repository at this point in the history
Signed-off-by: nickfarrow <[email protected]>
  • Loading branch information
nickfarrow committed Apr 10, 2022
1 parent df0e410 commit 05aeb23
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
- added `ensure_addresses_cached` to `Wallet` to let offline wallets load and cache addresses in their database
- 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.
- Changed `AddressIndex::LastUnused` to look back further than `current_index`, and only return a new address if all have been used.
- Add `AddressIndex::FirstUnused` to get unused addresses from the beginning of the keychain.
- Add `wallet.get_batch_unused_addresses` to return vector of N unused addresses, populating any remaining with new addresses.

### Sync API change

Expand Down
84 changes: 84 additions & 0 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,39 @@ where
Ok(new_addresses_cached)
}

/// Return vector of n unused addresses from the [`KeychainKind`].
/// If less than n unused addresses are returned, the rest will be populated by new addresses.
/// The unused addresses returned are in order of oldest in keychain first, with increasing index.
pub fn get_batch_unused_addresses(
&self,
n: usize,
keychain: KeychainKind,
) -> Result<Vec<AddressInfo>, Error> {
let unused_key_indexes = self.get_unused_key_indexes(keychain)?;
let mut addresses = unused_key_indexes
.iter()
.map(|i| {
let derived_key = self
.get_descriptor_for_keychain(keychain)
.as_derived(*i, &self.secp);

derived_key
.address(self.network)
.map(|address| AddressInfo {
address,
index: *i,
keychain,
})
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
})
.take(n)
.collect::<Result<Vec<_>, _>>()?;
for _ in 0..(n - addresses.len()) {
addresses.push(self.get_new_address(keychain)?)
}
Ok(addresses)
}

/// Return whether or not a `script` is part of this wallet (either internal or external)
pub fn is_mine(&self, script: &Script) -> Result<bool, Error> {
self.database.borrow().is_mine(script)
Expand Down Expand Up @@ -3970,6 +4003,57 @@ pub(crate) mod test {
);
}

#[test]
fn test_batch_unused_addresses() {
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
let descriptors = testutils!(@descriptors (descriptor));
let wallet = Wallet::new(
&descriptors.0,
None,
Network::Testnet,
MemoryDatabase::new(),
)
.unwrap();

// get first two addresses, moving index
for _ in 0..2 {
let _ = wallet.get_address(New);
}

// use the second address
crate::populate_test_db!(
wallet.database.borrow_mut(),
testutils! (@tx ( (@external descriptors, 1) => 25_000 ) (@confirmations 1)),
Some(100),
);

assert_eq!(
wallet
.get_batch_unused_addresses(3, KeychainKind::External)
.unwrap(),
vec![
AddressInfo {
index: 0,
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
.unwrap(),
keychain: KeychainKind::External,
},
AddressInfo {
index: 2,
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
.unwrap(),
keychain: KeychainKind::External,
},
AddressInfo {
index: 3,
address: Address::from_str("tb1q32a23q6u3yy89l8svrt80a54h06qvn7gnuvsen")
.unwrap(),
keychain: KeychainKind::External,
}
]
);
}

#[test]
fn test_peek_address_at_index() {
let db = MemoryDatabase::new();
Expand Down

0 comments on commit 05aeb23

Please sign in to comment.