Skip to content

Commit 1546c42

Browse files
committed
Add API for internal addresses
There are good reasons for applications to need to get internal addresses too. For example creating a transactions that splits an output into several smaller ones.
1 parent fdb272e commit 1546c42

File tree

3 files changed

+82
-25
lines changed

3 files changed

+82
-25
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
- Unify ureq and reqwest esplora backends to have the same configuration parameters. This means reqwest now has a timeout parameter and ureq has a concurrency parameter.
1111
- Fixed esplora fee estimation.
1212
- Fixed generating WIF in the correct network format.
13+
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
1314

1415
## [v0.14.0] - [v0.13.0]
1516

src/testutils/blockchain_tests.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ macro_rules! bdk_blockchain_tests {
809809

810810
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
811811
builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
812-
let (mut new_psbt, new_details) = builder.finish().unwrap();
812+
let (mut new_psbt, new_details) = builder.finish().expect("fee bump tx");
813813
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
814814
assert!(finalized, "Cannot finalize transaction");
815815
wallet.broadcast(&new_psbt.extract_tx()).unwrap();

src/wallet/mod.rs

+80-24
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,11 @@ where
238238
D: BatchDatabase,
239239
{
240240
// Return a newly derived address using the external descriptor
241-
fn get_new_address(&self) -> Result<AddressInfo, Error> {
242-
let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
241+
fn get_new_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
242+
let incremented_index = self.fetch_and_increment_index(keychain)?;
243243

244244
let address_result = self
245-
.descriptor
245+
.get_descriptor_for_keychain(keychain)
246246
.as_derived(incremented_index, &self.secp)
247247
.address(self.network);
248248

@@ -256,10 +256,12 @@ where
256256

257257
// Return the the last previously derived address if it has not been used in a received
258258
// transaction. Otherwise return a new address using [`Wallet::get_new_address`].
259-
fn get_unused_address(&self) -> Result<AddressInfo, Error> {
260-
let current_index = self.fetch_index(KeychainKind::External)?;
259+
fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
260+
let current_index = self.fetch_index(keychain)?;
261261

262-
let derived_key = self.descriptor.as_derived(current_index, &self.secp);
262+
let derived_key = self
263+
.get_descriptor_for_keychain(keychain)
264+
.as_derived(current_index, &self.secp);
263265

264266
let script_pubkey = derived_key.script_pubkey();
265267

@@ -271,7 +273,7 @@ where
271273
.any(|o| o.script_pubkey == script_pubkey);
272274

273275
if found_used {
274-
self.get_new_address()
276+
self.get_new_address(keychain)
275277
} else {
276278
derived_key
277279
.address(self.network)
@@ -284,8 +286,8 @@ where
284286
}
285287

286288
// Return derived address for the external descriptor at a specific index
287-
fn peek_address(&self, index: u32) -> Result<AddressInfo, Error> {
288-
self.descriptor
289+
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
290+
self.get_descriptor_for_keychain(keychain)
289291
.as_derived(index, &self.secp)
290292
.address(self.network)
291293
.map(|address| AddressInfo { index, address })
@@ -294,10 +296,10 @@ where
294296

295297
// Return derived address for the external descriptor at a specific index and reset current
296298
// address index
297-
fn reset_address(&self, index: u32) -> Result<AddressInfo, Error> {
298-
self.set_index(KeychainKind::External, index)?;
299+
fn reset_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
300+
self.set_index(keychain, index)?;
299301

300-
self.descriptor
302+
self.get_descriptor_for_keychain(keychain)
301303
.as_derived(index, &self.secp)
302304
.address(self.network)
303305
.map(|address| AddressInfo { index, address })
@@ -308,11 +310,30 @@ where
308310
/// available address index selection strategies. If none of the keys in the descriptor are derivable
309311
/// (ie. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
310312
pub fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
313+
self._get_address(address_index, KeychainKind::External)
314+
}
315+
316+
/// Return a derived address using the internal (change) descriptor.
317+
///
318+
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
319+
///
320+
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
321+
/// in the descriptor are derivable (ie. does not end with /*) then the same address will always
322+
/// be returned for any [`AddressIndex`].
323+
pub fn get_internal_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
324+
self._get_address(address_index, KeychainKind::Internal)
325+
}
326+
327+
fn _get_address(
328+
&self,
329+
address_index: AddressIndex,
330+
keychain: KeychainKind,
331+
) -> Result<AddressInfo, Error> {
311332
match address_index {
312-
AddressIndex::New => self.get_new_address(),
313-
AddressIndex::LastUnused => self.get_unused_address(),
314-
AddressIndex::Peek(index) => self.peek_address(index),
315-
AddressIndex::Reset(index) => self.reset_address(index),
333+
AddressIndex::New => self.get_new_address(keychain),
334+
AddressIndex::LastUnused => self.get_unused_address(keychain),
335+
AddressIndex::Peek(index) => self.peek_address(index, keychain),
336+
AddressIndex::Reset(index) => self.reset_address(index, keychain),
316337
}
317338
}
318339

@@ -662,7 +683,10 @@ where
662683
let mut drain_output = {
663684
let script_pubkey = match params.drain_to {
664685
Some(ref drain_recipient) => drain_recipient.clone(),
665-
None => self.get_change_address()?,
686+
None => self
687+
.get_internal_address(AddressIndex::New)?
688+
.address
689+
.script_pubkey(),
666690
};
667691

668692
TxOut {
@@ -1092,13 +1116,6 @@ where
10921116
.map(|(desc, child)| desc.as_derived(child, &self.secp)))
10931117
}
10941118

1095-
fn get_change_address(&self) -> Result<Script, Error> {
1096-
let (desc, keychain) = self._get_descriptor_for_keychain(KeychainKind::Internal);
1097-
let index = self.fetch_and_increment_index(keychain)?;
1098-
1099-
Ok(desc.as_derived(index, &self.secp).script_pubkey())
1100-
}
1101-
11021119
fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
11031120
let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
11041121
let index = match descriptor.is_deriveable() {
@@ -4005,6 +4022,45 @@ pub(crate) mod test {
40054022
builder.add_recipient(addr.script_pubkey(), 45_000);
40064023
builder.finish().unwrap();
40074024
}
4025+
4026+
#[test]
4027+
fn test_get_address() {
4028+
use crate::descriptor::template::Bip84;
4029+
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
4030+
let wallet = Wallet::new_offline(
4031+
Bip84(key.clone(), KeychainKind::External),
4032+
Some(Bip84(key, KeychainKind::Internal)),
4033+
Network::Regtest,
4034+
MemoryDatabase::default(),
4035+
)
4036+
.unwrap();
4037+
4038+
assert_eq!(
4039+
wallet.get_address(AddressIndex::New).unwrap().address,
4040+
Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap()
4041+
);
4042+
assert_eq!(
4043+
wallet
4044+
.get_internal_address(AddressIndex::New)
4045+
.unwrap()
4046+
.address,
4047+
Address::from_str("bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa").unwrap()
4048+
);
4049+
4050+
let wallet = Wallet::new_offline(
4051+
Bip84(key.clone(), KeychainKind::External),
4052+
None,
4053+
Network::Regtest,
4054+
MemoryDatabase::default(),
4055+
)
4056+
.unwrap();
4057+
4058+
assert_eq!(
4059+
wallet.get_address(AddressIndex::New).unwrap().address,
4060+
Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap(),
4061+
"when there's no internal descriptor it should just use external"
4062+
);
4063+
}
40084064
}
40094065

40104066
/// Deterministically generate a unique name given the descriptors defining the wallet

0 commit comments

Comments
 (0)