[wallet] Add get_unused_address() function, update CHANGELOG

This commit is contained in:
Steve Myers 2021-03-07 21:57:19 -08:00
parent fef6176275
commit 41ae202d02
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
2 changed files with 82 additions and 0 deletions

View File

@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
#### Changed
- `FeeRate` constructors `from_sat_per_vb` and `default_min_relay_fee` are now `const` functions
#### Added
- Added `get_unused_address()` which returns the last generated address if it has not been used or if used in a received transaction returns a new address
## [v0.4.0] - [v0.3.0]
### Keys

View File

@ -177,6 +177,39 @@ where
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
}
/// Return the the last previously generated address if it has not been used in a received
/// transaction. Otherwise return a new address using [`Wallet::get_new_address`].
///
/// Use with caution, if the wallet has not yet detected an address has been used it could
/// return an already used address. This function is primarily meant for situations where the
/// caller is untrusted; for example when generating donation addresses on-demand for a public
/// web page.
///
pub fn get_unused_address(&self) -> Result<Address, Error> {
let index = self.fetch_index(KeychainKind::External)?;
let script = self
.descriptor
.as_derived(index, &self.secp)
.script_pubkey();
let found_used = self
.list_transactions(true)?
.iter()
.flat_map(|tx_details| tx_details.transaction.as_ref())
.flat_map(|tx| tx.output.iter())
.any(|o| o.script_pubkey == script);
if found_used {
self.get_new_address()
} else {
self.descriptor
.as_derived(index, &self.secp)
.address(self.network)
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
}
}
/// 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)
@ -3471,4 +3504,50 @@ mod test {
"should finalized input it signed"
)
}
#[test]
fn test_unused_address() {
let db = MemoryDatabase::new();
let wallet = Wallet::new_offline("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)",
None, Network::Testnet, db).unwrap();
assert_eq!(
wallet.get_unused_address().unwrap().to_string(),
"tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
);
assert_eq!(
wallet.get_unused_address().unwrap().to_string(),
"tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
);
}
#[test]
fn test_next_unused_address() {
let descriptor = "wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)";
let descriptors = testutils!(@descriptors (descriptor));
let wallet = Wallet::new_offline(
&descriptors.0,
None,
Network::Testnet,
MemoryDatabase::new(),
)
.unwrap();
assert_eq!(
wallet.get_unused_address().unwrap().to_string(),
"tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a"
);
// use the above address
crate::populate_test_db!(
wallet.database.borrow_mut(),
testutils! (@tx ( (@external descriptors, 0) => 25_000 ) (@confirmations 1)),
Some(100),
);
assert_eq!(
wallet.get_unused_address().unwrap().to_string(),
"tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7"
);
}
}