@@ -2,15 +2,13 @@ use std::{error, fmt, str::FromStr};
22
33use bitcoin:: {
44 self ,
5- hashes:: { hash160 , hex:: FromHex } ,
5+ hashes:: hex:: FromHex ,
66 secp256k1,
77 secp256k1:: { Secp256k1 , Signing } ,
88 util:: bip32,
99} ;
1010
1111use MiniscriptKey ;
12- use NullCtx ;
13- use ToPublicKey ;
1412
1513/// The MiniscriptKey corresponding to Descriptors. This can
1614/// either be Single public key or a Xpub
@@ -319,10 +317,35 @@ impl FromStr for DescriptorPublicKey {
319317 }
320318}
321319
320+ /// Descriptor key conversion error
321+ #[ derive( Debug , PartialEq , Clone , Copy ) ]
322+ pub enum ConversionError {
323+ /// Attempted to convert a key with a wildcard to a bitcoin public key
324+ Wildcard ,
325+ /// Attempted to convert a key with hardened derivations to a bitcoin public key
326+ HardenedChild ,
327+ /// Attempted to convert a key with a hardened wildcard to a bitcoin public key
328+ HardenedWildcard ,
329+ }
330+
331+ impl fmt:: Display for ConversionError {
332+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
333+ f. write_str ( match * self {
334+ ConversionError :: Wildcard => "uninstantiated wildcard in bip32 path" ,
335+ ConversionError :: HardenedChild => "hardened child step in bip32 path" ,
336+ ConversionError :: HardenedWildcard => {
337+ "hardened and uninstantiated wildcard in bip32 path"
338+ }
339+ } )
340+ }
341+ }
342+
343+ impl error:: Error for ConversionError { }
344+
322345impl DescriptorPublicKey {
323- /// Derives the specified child key if self is a wildcard xpub. Otherwise returns self.
346+ /// If this public key has a wildcard, replace it by the given index
324347 ///
325- /// Panics if given a child number ≥ 2^31
348+ /// Panics if given an index ≥ 2^31
326349 pub fn derive ( mut self , index : u32 ) -> DescriptorPublicKey {
327350 if let DescriptorPublicKey :: XPub ( mut xpub) = self {
328351 match xpub. is_wildcard {
@@ -343,6 +366,35 @@ impl DescriptorPublicKey {
343366 }
344367 self
345368 }
369+
370+ /// Computes the public key corresponding to this descriptor key
371+ ///
372+ /// Will return an error if the descriptor key has any hardened
373+ /// derivation steps in its path, or if the key has any wildcards.
374+ ///
375+ /// To ensure there are no wildcards, call `.derive(0)` or similar;
376+ /// to avoid hardened derivation steps, start from a `DescriptorSecretKey`
377+ /// and call `as_public`, or call `TranslatePk2::translate_pk2` with
378+ /// some function which has access to secret key data.
379+ pub fn derive_public_key < C : secp256k1:: Verification > (
380+ & self ,
381+ secp : & Secp256k1 < C > ,
382+ ) -> Result < bitcoin:: PublicKey , ConversionError > {
383+ match * self {
384+ DescriptorPublicKey :: SinglePub ( ref pk) => Ok ( pk. key ) ,
385+ DescriptorPublicKey :: XPub ( ref xpk) => match xpk. is_wildcard {
386+ Wildcard :: Unhardened => Err ( ConversionError :: Wildcard ) ,
387+ Wildcard :: Hardened => Err ( ConversionError :: HardenedWildcard ) ,
388+ Wildcard :: None => match xpk. xkey . derive_pub ( secp, & xpk. derivation_path . as_ref ( ) ) {
389+ Ok ( xpub) => Ok ( xpub. public_key ) ,
390+ Err ( bip32:: Error :: CannotDeriveFromHardenedKey ) => {
391+ Err ( ConversionError :: HardenedChild )
392+ }
393+ Err ( e) => unreachable ! ( "cryptographically unreachable: {}" , e) ,
394+ } ,
395+ } ,
396+ }
397+ }
346398}
347399
348400impl FromStr for DescriptorSecretKey {
@@ -584,32 +636,6 @@ impl<'secp, C: secp256k1::Verification> DescriptorPublicKeyCtx<'secp, C> {
584636 }
585637}
586638
587- impl < ' secp , C : secp256k1:: Verification > ToPublicKey < DescriptorPublicKeyCtx < ' secp , C > >
588- for DescriptorPublicKey
589- {
590- fn to_public_key ( & self , to_pk_ctx : DescriptorPublicKeyCtx < ' secp , C > ) -> bitcoin:: PublicKey {
591- let xpub = self . clone ( ) . derive ( to_pk_ctx. index ) ;
592- match xpub {
593- DescriptorPublicKey :: SinglePub ( ref spub) => spub. key . to_public_key ( NullCtx ) ,
594- DescriptorPublicKey :: XPub ( ref xpub) => {
595- // derives if wildcard, otherwise returns self
596- debug_assert ! ( xpub. is_wildcard == Wildcard :: None ) ;
597- xpub. xkey
598- . derive_pub ( to_pk_ctx. secp_ctx , & xpub. derivation_path )
599- . expect ( "Shouldn't fail, only normal derivations" )
600- . public_key
601- }
602- }
603- }
604-
605- fn hash_to_hash160 (
606- hash : & Self :: Hash ,
607- to_pk_ctx : DescriptorPublicKeyCtx < ' secp , C > ,
608- ) -> hash160:: Hash {
609- hash. to_public_key ( to_pk_ctx) . to_pubkeyhash ( )
610- }
611- }
612-
613639#[ cfg( test) ]
614640mod test {
615641 use super :: { DescriptorKeyParseError , DescriptorPublicKey , DescriptorSecretKey } ;
0 commit comments