diff --git a/CHANGELOG.md b/CHANGELOG.md index 05fa59f9..d9e0693f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - Expose miniscript `has_wildcard` and `sanity_check` methods on `Descriptor` type #945 - Expose `new_wsh_sortedmulti` and `new_pk` methods on `Descriptor` type #949 +- Expose `new_sh_sortedmulti`, `new_sh_wsh_sortedmulti`, `new_pkh`, `new_wpkh`, `new_sh_wpkh` methods on `Descriptor` type #973 ## [v2.3.0] diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/DescriptorTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/DescriptorTest.kt index 351076a0..f5901efb 100644 --- a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/DescriptorTest.kt +++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/DescriptorTest.kt @@ -49,13 +49,17 @@ class DescriptorTest { "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB" ) - val wshDescriptor = Descriptor.newWshSortedmulti(2u, pkList); + val wshSortedMultiDescriptor = Descriptor.newWshSortedmulti(2u, pkList); + val shWshSortedMultiDescriptor = Descriptor.newShWshSortedmulti(2u, pkList); + val shSortedMultiDescriptor = Descriptor.newShSortedmulti(2u, pkList); - assertEquals(wshDescriptor.descType(), DescriptorType.WSH_SORTED_MULTI) + assertEquals(wshSortedMultiDescriptor.descType(), DescriptorType.WSH_SORTED_MULTI) + assertEquals(shWshSortedMultiDescriptor.descType(), DescriptorType.SH_WSH_SORTED_MULTI) + assertEquals(shSortedMultiDescriptor.descType(), DescriptorType.SH_SORTED_MULTI) } @Test - fun createInvalidMutlisigDescriptor() { + fun createInvalidMultisigDescriptor() { val invalidPkList = listOf( "invalid_xpub", "1111111111111", @@ -69,7 +73,14 @@ class DescriptorTest { @Test fun createSingleKeyDescriptor() { val newPkDescriptor = Descriptor.newPk("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") + val newPkhDescriptor = Descriptor.newPkh("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") + val newWpkhDescriptor = Descriptor.newWpkh("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") + val newShWpkhDescriptor = Descriptor.newShWpkh("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB") + assertEquals(newPkDescriptor.descType(), DescriptorType.BARE) + assertEquals(newPkhDescriptor.descType(), DescriptorType.PKH) + assertEquals(newWpkhDescriptor.descType(), DescriptorType.WPKH) + assertEquals(newShWpkhDescriptor.descType(), DescriptorType.SH_WPKH) } // Cannot create addr() descriptor. diff --git a/bdk-ffi/src/descriptor.rs b/bdk-ffi/src/descriptor.rs index e008f178..25760903 100644 --- a/bdk-ffi/src/descriptor.rs +++ b/bdk-ffi/src/descriptor.rs @@ -334,6 +334,64 @@ impl Descriptor { }) } + /// Create a new sh sortedmulti descriptor with threshold k and Vec of pks. + /// Errors when miniscript exceeds resource limits under p2sh context + #[uniffi::constructor] + pub fn new_sh_sortedmulti(k: u32, pks: Vec) -> Result { + let bdk_pks: Vec = pks + .iter() + .map(|pk| { + BdkDescriptorPublicKey::from_str(pk).map_err(|e| DescriptorError::Key { + error_message: e.to_string(), + }) + }) + .collect::, DescriptorError>>()?; + let miniscript_descriptor = + match bdk_wallet::miniscript::Descriptor::new_sh_sortedmulti(k as usize, bdk_pks) { + Ok(descriptor) => descriptor, + Err(e) => { + return Err(DescriptorError::Miniscript { + error_message: e.to_string(), + }) + } + }; + let extended_descriptor = ExtendedDescriptor::from(miniscript_descriptor); + + Ok(Self { + extended_descriptor, + key_map: KeyMap::new(), + }) + } + + /// Create a new sh wrapped wsh sortedmulti descriptor from threshold k and Vec of pks + /// Errors when miniscript exceeds resource limits under segwit context + #[uniffi::constructor] + pub fn new_sh_wsh_sortedmulti(k: u32, pks: Vec) -> Result { + let bdk_pks: Vec = pks + .iter() + .map(|pk| { + BdkDescriptorPublicKey::from_str(pk).map_err(|e| DescriptorError::Key { + error_message: e.to_string(), + }) + }) + .collect::, DescriptorError>>()?; + let miniscript_descriptor = + match bdk_wallet::miniscript::Descriptor::new_sh_wsh_sortedmulti(k as usize, bdk_pks) { + Ok(descriptor) => descriptor, + Err(e) => { + return Err(DescriptorError::Miniscript { + error_message: e.to_string(), + }) + } + }; + let extended_descriptor = ExtendedDescriptor::from(miniscript_descriptor); + + Ok(Self { + extended_descriptor, + key_map: KeyMap::new(), + }) + } + /// Create a new pay-to-pubkey descriptor from a public key string. #[uniffi::constructor] pub fn new_pk(pk: String) -> Result { @@ -350,6 +408,75 @@ impl Descriptor { }) } + /// Create a new PkH descriptor + #[uniffi::constructor] + pub fn new_pkh(pk: String) -> Result { + let key = BdkDescriptorPublicKey::from_str(&pk).map_err(|e| DescriptorError::Key { + error_message: e.to_string(), + })?; + + let miniscript_descriptor = match bdk_wallet::miniscript::Descriptor::new_pkh(key) { + Ok(descriptor) => descriptor, + Err(e) => { + return Err(DescriptorError::Miniscript { + error_message: e.to_string(), + }) + } + }; + let extended_descriptor = ExtendedDescriptor::from(miniscript_descriptor); + + Ok(Self { + extended_descriptor, + key_map: KeyMap::new(), + }) + } + + /// Create a new Wpkh descriptor Will return Err if uncompressed key is used + #[uniffi::constructor] + pub fn new_wpkh(pk: String) -> Result { + let key = BdkDescriptorPublicKey::from_str(&pk).map_err(|e| DescriptorError::Key { + error_message: e.to_string(), + })?; + + let miniscript_descriptor = match bdk_wallet::miniscript::Descriptor::new_wpkh(key) { + Ok(descriptor) => descriptor, + Err(e) => { + return Err(DescriptorError::Miniscript { + error_message: e.to_string(), + }) + } + }; + let extended_descriptor = ExtendedDescriptor::from(miniscript_descriptor); + + Ok(Self { + extended_descriptor, + key_map: KeyMap::new(), + }) + } + + /// Create a new sh wrapped wpkh from Pk. Errors when uncompressed keys are supplied + #[uniffi::constructor] + pub fn new_sh_wpkh(pk: String) -> Result { + let key = BdkDescriptorPublicKey::from_str(&pk).map_err(|e| DescriptorError::Key { + error_message: e.to_string(), + })?; + + let miniscript_descriptor = match bdk_wallet::miniscript::Descriptor::new_sh_wpkh(key) { + Ok(descriptor) => descriptor, + Err(e) => { + return Err(DescriptorError::Miniscript { + error_message: e.to_string(), + }) + } + }; + let extended_descriptor = ExtendedDescriptor::from(miniscript_descriptor); + + Ok(Self { + extended_descriptor, + key_map: KeyMap::new(), + }) + } + /// Dangerously convert the descriptor to a string. pub fn to_string_with_secret(&self) -> String { let descriptor = &self.extended_descriptor;