Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddressIndex improvements: LastUnused, FirstUnused, and get_batch_unused_addresses() #546

Closed
wants to merge 5 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add get_batch_unused_addresses
Signed-off-by: nickfarrow <nick@nickfarrow.com>
  • Loading branch information
nickfarrow committed May 5, 2022
commit 0a8e36863569549ba07684c7a96b03d9464321d1
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -27,6 +27,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

84 changes: 84 additions & 0 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not as per impl right now.. if from_front is set false, the addresses are returned in reverse order..

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)
@@ -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();