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.
This commit is contained in:
parent
19f028714b
commit
00f0901bac
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Pin tokio dependency version to ~1.14 to prevent errors due to their new MSRV 1.49.0
|
||||
- Removed default verification from `wallet::sync`. sync-time verification is added in `script_sync` and is activated by `verify` feature flag.
|
||||
- `verify` flag removed from `TransactionDetails`.
|
||||
- Add `get_internal_address` to allow you to get internal addresses just as you get external addresses.
|
||||
|
||||
## [v0.16.0] - [v0.15.0]
|
||||
|
||||
|
@ -815,7 +815,7 @@ macro_rules! bdk_blockchain_tests {
|
||||
|
||||
let mut builder = wallet.build_fee_bump(details.txid).unwrap();
|
||||
builder.fee_rate(FeeRate::from_sat_per_vb(2.1));
|
||||
let (mut new_psbt, new_details) = builder.finish().unwrap();
|
||||
let (mut new_psbt, new_details) = builder.finish().expect("fee bump tx");
|
||||
let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap();
|
||||
assert!(finalized, "Cannot finalize transaction");
|
||||
wallet.broadcast(&new_psbt.extract_tx()).unwrap();
|
||||
|
@ -238,11 +238,11 @@ where
|
||||
D: BatchDatabase,
|
||||
{
|
||||
// Return a newly derived address using the external descriptor
|
||||
fn get_new_address(&self) -> Result<AddressInfo, Error> {
|
||||
let incremented_index = self.fetch_and_increment_index(KeychainKind::External)?;
|
||||
fn get_new_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
|
||||
let incremented_index = self.fetch_and_increment_index(keychain)?;
|
||||
|
||||
let address_result = self
|
||||
.descriptor
|
||||
.get_descriptor_for_keychain(keychain)
|
||||
.as_derived(incremented_index, &self.secp)
|
||||
.address(self.network);
|
||||
|
||||
@ -256,10 +256,12 @@ where
|
||||
|
||||
// Return the the last previously derived address if it has not been used in a received
|
||||
// transaction. Otherwise return a new address using [`Wallet::get_new_address`].
|
||||
fn get_unused_address(&self) -> Result<AddressInfo, Error> {
|
||||
let current_index = self.fetch_index(KeychainKind::External)?;
|
||||
fn get_unused_address(&self, keychain: KeychainKind) -> Result<AddressInfo, Error> {
|
||||
let current_index = self.fetch_index(keychain)?;
|
||||
|
||||
let derived_key = self.descriptor.as_derived(current_index, &self.secp);
|
||||
let derived_key = self
|
||||
.get_descriptor_for_keychain(keychain)
|
||||
.as_derived(current_index, &self.secp);
|
||||
|
||||
let script_pubkey = derived_key.script_pubkey();
|
||||
|
||||
@ -271,7 +273,7 @@ where
|
||||
.any(|o| o.script_pubkey == script_pubkey);
|
||||
|
||||
if found_used {
|
||||
self.get_new_address()
|
||||
self.get_new_address(keychain)
|
||||
} else {
|
||||
derived_key
|
||||
.address(self.network)
|
||||
@ -284,8 +286,8 @@ where
|
||||
}
|
||||
|
||||
// Return derived address for the external descriptor at a specific index
|
||||
fn peek_address(&self, index: u32) -> Result<AddressInfo, Error> {
|
||||
self.descriptor
|
||||
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
|
||||
self.get_descriptor_for_keychain(keychain)
|
||||
.as_derived(index, &self.secp)
|
||||
.address(self.network)
|
||||
.map(|address| AddressInfo { index, address })
|
||||
@ -294,10 +296,10 @@ where
|
||||
|
||||
// Return derived address for the external descriptor at a specific index and reset current
|
||||
// address index
|
||||
fn reset_address(&self, index: u32) -> Result<AddressInfo, Error> {
|
||||
self.set_index(KeychainKind::External, index)?;
|
||||
fn reset_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
|
||||
self.set_index(keychain, index)?;
|
||||
|
||||
self.descriptor
|
||||
self.get_descriptor_for_keychain(keychain)
|
||||
.as_derived(index, &self.secp)
|
||||
.address(self.network)
|
||||
.map(|address| AddressInfo { index, address })
|
||||
@ -308,11 +310,30 @@ where
|
||||
/// available address index selection strategies. If none of the keys in the descriptor are derivable
|
||||
/// (ie. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
|
||||
pub fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
|
||||
self._get_address(address_index, KeychainKind::External)
|
||||
}
|
||||
|
||||
/// Return a derived address using the internal (change) descriptor.
|
||||
///
|
||||
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
|
||||
///
|
||||
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
|
||||
/// in the descriptor are derivable (ie. does not end with /*) then the same address will always
|
||||
/// be returned for any [`AddressIndex`].
|
||||
pub fn get_internal_address(&self, address_index: AddressIndex) -> Result<AddressInfo, Error> {
|
||||
self._get_address(address_index, KeychainKind::Internal)
|
||||
}
|
||||
|
||||
fn _get_address(
|
||||
&self,
|
||||
address_index: AddressIndex,
|
||||
keychain: KeychainKind,
|
||||
) -> Result<AddressInfo, Error> {
|
||||
match address_index {
|
||||
AddressIndex::New => self.get_new_address(),
|
||||
AddressIndex::LastUnused => self.get_unused_address(),
|
||||
AddressIndex::Peek(index) => self.peek_address(index),
|
||||
AddressIndex::Reset(index) => self.reset_address(index),
|
||||
AddressIndex::New => self.get_new_address(keychain),
|
||||
AddressIndex::LastUnused => self.get_unused_address(keychain),
|
||||
AddressIndex::Peek(index) => self.peek_address(index, keychain),
|
||||
AddressIndex::Reset(index) => self.reset_address(index, keychain),
|
||||
}
|
||||
}
|
||||
|
||||
@ -662,7 +683,10 @@ where
|
||||
let mut drain_output = {
|
||||
let script_pubkey = match params.drain_to {
|
||||
Some(ref drain_recipient) => drain_recipient.clone(),
|
||||
None => self.get_change_address()?,
|
||||
None => self
|
||||
.get_internal_address(AddressIndex::New)?
|
||||
.address
|
||||
.script_pubkey(),
|
||||
};
|
||||
|
||||
TxOut {
|
||||
@ -1091,13 +1115,6 @@ where
|
||||
.map(|(desc, child)| desc.as_derived(child, &self.secp)))
|
||||
}
|
||||
|
||||
fn get_change_address(&self) -> Result<Script, Error> {
|
||||
let (desc, keychain) = self._get_descriptor_for_keychain(KeychainKind::Internal);
|
||||
let index = self.fetch_and_increment_index(keychain)?;
|
||||
|
||||
Ok(desc.as_derived(index, &self.secp).script_pubkey())
|
||||
}
|
||||
|
||||
fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
|
||||
let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
|
||||
let index = match descriptor.is_deriveable() {
|
||||
@ -3987,6 +4004,48 @@ pub(crate) mod test {
|
||||
builder.add_recipient(addr.script_pubkey(), 45_000);
|
||||
builder.finish().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_address() {
|
||||
use crate::descriptor::template::Bip84;
|
||||
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
|
||||
let wallet = Wallet::new_offline(
|
||||
Bip84(key, KeychainKind::External),
|
||||
Some(Bip84(key, KeychainKind::Internal)),
|
||||
Network::Regtest,
|
||||
MemoryDatabase::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wallet.get_address(AddressIndex::New).unwrap().address,
|
||||
Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
wallet
|
||||
.get_internal_address(AddressIndex::New)
|
||||
.unwrap()
|
||||
.address,
|
||||
Address::from_str("bcrt1qtrwtz00wxl69e5xex7amy4xzlxkaefg3gfdkxa").unwrap()
|
||||
);
|
||||
|
||||
let wallet = Wallet::new_offline(
|
||||
Bip84(key, KeychainKind::External),
|
||||
None,
|
||||
Network::Regtest,
|
||||
MemoryDatabase::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
wallet
|
||||
.get_internal_address(AddressIndex::New)
|
||||
.unwrap()
|
||||
.address,
|
||||
Address::from_str("bcrt1qkmvk2nadgplmd57ztld8nf8v2yxkzmdvwtjf8s").unwrap(),
|
||||
"when there's no internal descriptor it should just use external"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Deterministically generate a unique name given the descriptors defining the wallet
|
||||
|
Loading…
x
Reference in New Issue
Block a user