Skip to content

Commit b560ae4

Browse files
author
Scott Robinson
committed
Extract specialised DescriptorPublicKey functionality into DescriptorExtendedPublicKey
1 parent 0fab60b commit b560ae4

File tree

1 file changed

+126
-71
lines changed

1 file changed

+126
-71
lines changed

src/descriptor/key.rs

+126-71
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ use crate::prelude::*;
1818
use crate::serde::{Deserialize, Deserializer, Serialize, Serializer};
1919
use crate::{hash256, MiniscriptKey, ToPublicKey};
2020

21+
type DescriptorExtendedPublicKey = DescriptorXKey<bip32::ExtendedPubKey>;
22+
2123
/// The descriptor pubkey, either a single pubkey or an xpub.
2224
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
2325
pub enum DescriptorPublicKey {
2426
/// Single public key.
2527
Single(SinglePub),
2628
/// Extended public key (xpub).
27-
XPub(DescriptorXKey<bip32::ExtendedPubKey>),
29+
XPub(DescriptorExtendedPublicKey),
2830
/// Multiple extended public keys.
2931
MultiXPub(DescriptorMultiXKey<bip32::ExtendedPubKey>),
3032
}
@@ -283,6 +285,20 @@ impl error::Error for DescriptorKeyParseError {
283285
}
284286
}
285287

288+
impl fmt::Display for DescriptorExtendedPublicKey {
289+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290+
maybe_fmt_master_id(f, &self.origin)?;
291+
self.xkey.fmt(f)?;
292+
fmt_derivation_path(f, &self.derivation_path)?;
293+
match self.wildcard {
294+
Wildcard::None => {}
295+
Wildcard::Unhardened => write!(f, "/*")?,
296+
Wildcard::Hardened => write!(f, "/*h")?,
297+
}
298+
Ok(())
299+
}
300+
}
301+
286302
impl fmt::Display for DescriptorPublicKey {
287303
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
288304
match *self {
@@ -294,17 +310,7 @@ impl fmt::Display for DescriptorPublicKey {
294310
}?;
295311
Ok(())
296312
}
297-
DescriptorPublicKey::XPub(ref xpub) => {
298-
maybe_fmt_master_id(f, &xpub.origin)?;
299-
xpub.xkey.fmt(f)?;
300-
fmt_derivation_path(f, &xpub.derivation_path)?;
301-
match xpub.wildcard {
302-
Wildcard::None => {}
303-
Wildcard::Unhardened => write!(f, "/*")?,
304-
Wildcard::Hardened => write!(f, "/*h")?,
305-
}
306-
Ok(())
307-
}
313+
DescriptorPublicKey::XPub(ref xpub) => xpub.fmt(f),
308314
DescriptorPublicKey::MultiXPub(ref xpub) => {
309315
maybe_fmt_master_id(f, &xpub.origin)?;
310316
xpub.xkey.fmt(f)?;
@@ -503,13 +509,16 @@ pub enum ConversionError {
503509
HardenedChild,
504510
/// Attempted to convert a key with multiple derivation paths to a bitcoin public key
505511
MultiKey,
512+
/// Attempted to convert a key with a wildcard to a bitcoin public key
513+
Wildcard,
506514
}
507515

508516
impl fmt::Display for ConversionError {
509517
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510518
f.write_str(match *self {
511519
ConversionError::HardenedChild => "hardened child step in bip32 path",
512520
ConversionError::MultiKey => "multiple existing keys",
521+
ConversionError::Wildcard => "wildcard in bip32 path",
513522
})
514523
}
515524
}
@@ -520,7 +529,7 @@ impl error::Error for ConversionError {
520529
use self::ConversionError::*;
521530

522531
match self {
523-
HardenedChild | MultiKey => None,
532+
HardenedChild | MultiKey | Wildcard => None,
524533
}
525534
}
526535
}
@@ -572,17 +581,102 @@ pub trait DescriptorKey : Clone + fmt::Debug + fmt::Display + Eq + FromStr + std
572581
secp: &Secp256k1<C>,
573582
) -> Result<bitcoin::PublicKey, ConversionError>;
574583
}
584+
585+
impl DescriptorInnerKey for DescriptorExtendedPublicKey {
586+
/// The fingerprint of the master key associated with this key, `0x00000000` if none.
587+
fn master_fingerprint(&self) -> bip32::Fingerprint {
588+
if let Some((fingerprint, _)) = self.origin {
589+
fingerprint
590+
} else {
591+
self.xkey.fingerprint()
592+
}
593+
}
594+
595+
/// Full path, from the master key
596+
///
597+
/// For wildcard keys this will return the path up to the wildcard, so you
598+
/// can get full paths by appending one additional derivation step, according
599+
/// to the wildcard type (hardened or normal).
600+
///
601+
/// For multipath extended keys, this returns `None`.
602+
fn full_derivation_path(&self) -> Option<bip32::DerivationPath> {
603+
let origin_path = if let Some((_, ref path)) = self.origin {
604+
path.clone()
605+
} else {
606+
bip32::DerivationPath::from(vec![])
607+
};
608+
Some(origin_path.extend(&self.derivation_path))
609+
}
610+
611+
/// Whether or not the key has a wildcard
612+
fn has_wildcard(&self) -> bool {
613+
self.wildcard != Wildcard::None
614+
}
615+
616+
/// Replaces any wildcard (i.e. `/*`) in the key with a particular derivation index, turning it into a
617+
/// *definite* key (i.e. one where all the derivation paths are set).
618+
///
619+
/// # Returns
620+
///
621+
/// - If this key is not an xpub, returns `self`.
622+
/// - If this key is an xpub but does not have a wildcard, returns `self`.
623+
/// - Otherwise, returns the xpub at derivation `index` (removing the wildcard).
624+
///
625+
/// # Errors
626+
///
627+
/// - If `index` is hardened.
628+
fn at_derivation_index(self, index: u32) -> Result<DefiniteDescriptorKey, ConversionError> {
629+
let derivation_path = match self.wildcard {
630+
Wildcard::None => self.derivation_path,
631+
Wildcard::Unhardened => self.derivation_path.into_child(
632+
bip32::ChildNumber::from_normal_idx(index)
633+
.ok()
634+
.ok_or(ConversionError::HardenedChild)?,
635+
),
636+
Wildcard::Hardened => self.derivation_path.into_child(
637+
bip32::ChildNumber::from_hardened_idx(index)
638+
.ok()
639+
.ok_or(ConversionError::HardenedChild)?,
640+
),
641+
};
642+
let definite = DescriptorPublicKey::XPub(DescriptorXKey {
643+
origin: self.origin,
644+
xkey: self.xkey,
645+
derivation_path,
646+
wildcard: Wildcard::None,
647+
});
648+
649+
Ok(DefiniteDescriptorKey::new(definite)
650+
.expect("The key should not contain any wildcards at this point"))
651+
}
652+
653+
/// Whether or not this key has multiple derivation paths.
654+
fn is_multipath(&self) -> bool {
655+
false
656+
}
657+
658+
fn derive_public_key<C: Verification>(
659+
&self,
660+
secp: &Secp256k1<C>,
661+
) -> Result<bitcoin::PublicKey, ConversionError> {
662+
match self.wildcard {
663+
Wildcard::Unhardened | Wildcard::Hardened => Err(ConversionError::Wildcard),
664+
Wildcard::None => match self.xkey.derive_pub(secp, &self.derivation_path.as_ref()) {
665+
Ok(xpub) => Ok(bitcoin::PublicKey::new(xpub.public_key)),
666+
Err(bip32::Error::CannotDeriveFromHardenedKey) => {
667+
Err(ConversionError::HardenedChild)
668+
}
669+
Err(e) => unreachable!("cryptographically unreachable: {}", e),
670+
},
671+
}
672+
}
673+
}
674+
575675
impl DescriptorPublicKey {
576676
/// The fingerprint of the master key associated with this key, `0x00000000` if none.
577677
pub fn master_fingerprint(&self) -> bip32::Fingerprint {
578678
match *self {
579-
DescriptorPublicKey::XPub(ref xpub) => {
580-
if let Some((fingerprint, _)) = xpub.origin {
581-
fingerprint
582-
} else {
583-
xpub.xkey.fingerprint()
584-
}
585-
}
679+
DescriptorPublicKey::XPub(ref xpub) => xpub.master_fingerprint(),
586680
DescriptorPublicKey::MultiXPub(ref xpub) => {
587681
if let Some((fingerprint, _)) = xpub.origin {
588682
fingerprint
@@ -620,14 +714,7 @@ impl DescriptorPublicKey {
620714
/// For multipath extended keys, this returns `None`.
621715
pub fn full_derivation_path(&self) -> Option<bip32::DerivationPath> {
622716
match *self {
623-
DescriptorPublicKey::XPub(ref xpub) => {
624-
let origin_path = if let Some((_, ref path)) = xpub.origin {
625-
path.clone()
626-
} else {
627-
bip32::DerivationPath::from(vec![])
628-
};
629-
Some(origin_path.extend(&xpub.derivation_path))
630-
}
717+
DescriptorPublicKey::XPub(ref xpub) => xpub.full_derivation_path(),
631718
DescriptorPublicKey::Single(ref single) => {
632719
Some(if let Some((_, ref path)) = single.origin {
633720
path.clone()
@@ -649,7 +736,7 @@ impl DescriptorPublicKey {
649736
pub fn has_wildcard(&self) -> bool {
650737
match *self {
651738
DescriptorPublicKey::Single(..) => false,
652-
DescriptorPublicKey::XPub(ref xpub) => xpub.wildcard != Wildcard::None,
739+
DescriptorPublicKey::XPub(ref xpub) => xpub.has_wildcard(),
653740
DescriptorPublicKey::MultiXPub(ref xpub) => xpub.wildcard != Wildcard::None,
654741
}
655742
}
@@ -673,40 +760,19 @@ impl DescriptorPublicKey {
673760
///
674761
/// - If `index` is hardened.
675762
pub fn at_derivation_index(self, index: u32) -> Result<DefiniteDescriptorKey, ConversionError> {
676-
let definite = match self {
677-
DescriptorPublicKey::Single(_) => self,
678-
DescriptorPublicKey::XPub(xpub) => {
679-
let derivation_path = match xpub.wildcard {
680-
Wildcard::None => xpub.derivation_path,
681-
Wildcard::Unhardened => xpub.derivation_path.into_child(
682-
bip32::ChildNumber::from_normal_idx(index)
683-
.ok()
684-
.ok_or(ConversionError::HardenedChild)?,
685-
),
686-
Wildcard::Hardened => xpub.derivation_path.into_child(
687-
bip32::ChildNumber::from_hardened_idx(index)
688-
.ok()
689-
.ok_or(ConversionError::HardenedChild)?,
690-
),
691-
};
692-
DescriptorPublicKey::XPub(DescriptorXKey {
693-
origin: xpub.origin,
694-
xkey: xpub.xkey,
695-
derivation_path,
696-
wildcard: Wildcard::None,
697-
})
698-
}
699-
DescriptorPublicKey::MultiXPub(_) => return Err(ConversionError::MultiKey),
700-
};
701-
702-
Ok(DefiniteDescriptorKey::new(definite)
703-
.expect("The key should not contain any wildcards at this point"))
763+
match self {
764+
DescriptorPublicKey::Single(_) => Ok(DefiniteDescriptorKey::new(self)
765+
.expect("The key should not contain any wildcards at this point")),
766+
DescriptorPublicKey::XPub(xpub) => xpub.at_derivation_index(index),
767+
DescriptorPublicKey::MultiXPub(_) => Err(ConversionError::MultiKey),
768+
}
704769
}
705770

706771
/// Whether or not this key has multiple derivation paths.
707772
pub fn is_multipath(&self) -> bool {
708-
match *self {
709-
DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => false,
773+
match self {
774+
DescriptorPublicKey::Single(..) => false,
775+
DescriptorPublicKey::XPub(xpub) => xpub.is_multipath(),
710776
DescriptorPublicKey::MultiXPub(_) => true,
711777
}
712778
}
@@ -1083,18 +1149,7 @@ impl DefiniteDescriptorKey {
10831149
SinglePubKey::FullKey(pk) => Ok(pk),
10841150
SinglePubKey::XOnly(xpk) => Ok(xpk.to_public_key()),
10851151
},
1086-
DescriptorPublicKey::XPub(ref xpk) => match xpk.wildcard {
1087-
Wildcard::Unhardened | Wildcard::Hardened => {
1088-
unreachable!("we've excluded this error case")
1089-
}
1090-
Wildcard::None => match xpk.xkey.derive_pub(secp, &xpk.derivation_path.as_ref()) {
1091-
Ok(xpub) => Ok(bitcoin::PublicKey::new(xpub.public_key)),
1092-
Err(bip32::Error::CannotDeriveFromHardenedKey) => {
1093-
Err(ConversionError::HardenedChild)
1094-
}
1095-
Err(e) => unreachable!("cryptographically unreachable: {}", e),
1096-
},
1097-
},
1152+
DescriptorPublicKey::XPub(ref xpk) => xpk.derive_public_key(secp),
10981153
DescriptorPublicKey::MultiXPub(_) => {
10991154
unreachable!("A definite key cannot contain a multipath key.")
11001155
}

0 commit comments

Comments
 (0)