Skip to content

Commit 15d75b5

Browse files
committed
feat: expose derived_address helper on Descriptor
1 parent 1ff5631 commit 15d75b5

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

bdk-ffi/src/descriptor.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::bitcoin::Address;
12
use crate::bitcoin::DescriptorId;
23
use crate::bitcoin::DescriptorType;
34
use crate::error::DescriptorError;
@@ -12,6 +13,7 @@ use bdk_wallet::chain::DescriptorExt;
1213
use bdk_wallet::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
1314
use bdk_wallet::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
1415
use bdk_wallet::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
16+
use bdk_wallet::miniscript::descriptor::ConversionError;
1517
use bdk_wallet::template::{
1618
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
1719
DescriptorTemplate,
@@ -355,6 +357,33 @@ impl Descriptor {
355357
pub fn desc_type(&self) -> DescriptorType {
356358
self.extended_descriptor.desc_type()
357359
}
360+
361+
pub fn derived_address(
362+
&self,
363+
index: u32,
364+
network: Network,
365+
) -> Result<Arc<Address>, DescriptorError> {
366+
if self.extended_descriptor.is_multipath() {
367+
return Err(DescriptorError::MultiPath);
368+
}
369+
370+
let derived_descriptor = self
371+
.extended_descriptor
372+
.at_derivation_index(index)
373+
.map_err(|error| match error {
374+
ConversionError::HardenedChild => DescriptorError::HardenedDerivationXpub,
375+
ConversionError::MultiKey => DescriptorError::MultiPath,
376+
})?;
377+
378+
let address = derived_descriptor
379+
.address(network)
380+
.map_err(|error| DescriptorError::Miniscript {
381+
error_message: error.to_string(),
382+
})?
383+
.into();
384+
385+
Ok(Arc::new(address))
386+
}
358387
}
359388

360389
impl Display for Descriptor {

bdk-ffi/src/tests/descriptor.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,39 @@ fn test_max_weight_to_satisfy() {
161161
// Verify the method works and returns a positive weight
162162
assert!(weight > 0, "Weight must be positive");
163163
}
164+
165+
#[test]
166+
fn test_descriptor_derived_address() {
167+
let descriptor = Descriptor::new_bip84(
168+
&get_descriptor_secret_key(),
169+
KeychainKind::External,
170+
Network::Testnet,
171+
);
172+
173+
let derived = descriptor
174+
.derived_address(0, Network::Testnet)
175+
.expect("derive address");
176+
177+
let expected_descriptor = descriptor
178+
.extended_descriptor
179+
.at_derivation_index(0)
180+
.expect("derive descriptor");
181+
let expected_address = expected_descriptor
182+
.address(Network::Testnet)
183+
.expect("address from descriptor");
184+
185+
assert_eq!(derived.to_string(), expected_address.to_string());
186+
}
187+
188+
#[test]
189+
fn test_descriptor_derived_address_multipath_error() {
190+
let descriptor = Descriptor::new(
191+
"wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)".to_string(),
192+
Network::Testnet,
193+
)
194+
.expect("multipath descriptor parses");
195+
196+
let error = descriptor.derived_address(0, Network::Testnet).unwrap_err();
197+
198+
assert_matches!(error, DescriptorError::MultiPath);
199+
}

0 commit comments

Comments
 (0)