Skip to content

Commit 9cf065b

Browse files
committed
Define a new type for derived DescriptorPublicKeys
1 parent 43eb331 commit 9cf065b

File tree

2 files changed

+95
-18
lines changed

2 files changed

+95
-18
lines changed

src/descriptor/key.rs

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ use std::{error, fmt, str::FromStr};
22

33
use bitcoin::{
44
self,
5-
hashes::Hash,
5+
hashes::{hash160, Hash},
66
hashes::{hex::FromHex, HashEngine},
7-
secp256k1,
8-
secp256k1::{Secp256k1, Signing},
7+
secp256k1::{Secp256k1, Signing, Verification},
98
util::bip32,
109
XOnlyPublicKey, XpubIdentifier,
1110
};
@@ -70,6 +69,15 @@ pub enum SinglePubKey {
7069
XOnly(XOnlyPublicKey),
7170
}
7271

72+
/// A derived [`DescriptorPublicKey`]
73+
///
74+
/// Derived keys are guaranteed to never contain wildcards
75+
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
76+
pub struct DerivedDescriptorKey {
77+
key: DescriptorPublicKey,
78+
index: u32,
79+
}
80+
7381
impl fmt::Display for DescriptorSecretKey {
7482
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
7583
match self {
@@ -437,11 +445,14 @@ impl DescriptorPublicKey {
437445
/// - If this key is an xpub but does not have a wildcard, returns `self`.
438446
/// - Otherwise, returns the derived xpub at `index` (removing the wildcard).
439447
///
448+
/// Since it's guaranteed that extended keys won't have wildcards, the key is returned as
449+
/// [`DerivedDescriptorKey`].
450+
///
440451
/// # Panics
441452
///
442453
/// If `index` ≥ 2^31
443-
pub fn derive(self, index: u32) -> DescriptorPublicKey {
444-
match self {
454+
pub fn derive(self, index: u32) -> DerivedDescriptorKey {
455+
let derived = match self {
445456
DescriptorPublicKey::Single(_) => self,
446457
DescriptorPublicKey::XPub(xpub) => {
447458
let derivation_path = match xpub.wildcard {
@@ -460,7 +471,10 @@ impl DescriptorPublicKey {
460471
wildcard: Wildcard::None,
461472
})
462473
}
463-
}
474+
};
475+
476+
DerivedDescriptorKey::new(derived, index)
477+
.expect("The key should not contain any wildcards at this point")
464478
}
465479

466480
/// Computes the public key corresponding to this descriptor key.
@@ -475,7 +489,7 @@ impl DescriptorPublicKey {
475489
/// to avoid hardened derivation steps, start from a `DescriptorSecretKey`
476490
/// and call `to_public`, or call `TranslatePk2::translate_pk2` with
477491
/// some function which has access to secret key data.
478-
pub fn derive_public_key<C: secp256k1::Verification>(
492+
pub fn derive_public_key<C: Verification>(
479493
&self,
480494
secp: &Secp256k1<C>,
481495
) -> Result<bitcoin::PublicKey, ConversionError> {
@@ -720,6 +734,70 @@ impl MiniscriptKey for DescriptorPublicKey {
720734
}
721735
}
722736

737+
impl DerivedDescriptorKey {
738+
/// Computes the raw [`bitcoin::PublicKey`] for this descriptor key.
739+
///
740+
/// Will return an error if the key has any hardened derivation steps
741+
/// in its path, but unlike [`DescriptorPublicKey::derive_public_key`]
742+
/// this won't error in case of wildcards, because derived keys are
743+
/// guaranteed to never contain one.
744+
pub fn derive_public_key<C: Verification>(
745+
&self,
746+
secp: &Secp256k1<C>,
747+
) -> Result<bitcoin::PublicKey, ConversionError> {
748+
self.key.derive_public_key(secp)
749+
}
750+
751+
/// Return the derivation index of this key
752+
pub fn index(&self) -> u32 {
753+
self.index
754+
}
755+
756+
/// Construct an instance from a descriptor key and a derivation index
757+
///
758+
/// Returns `None` if the key contains a wildcard
759+
fn new(key: DescriptorPublicKey, index: u32) -> Option<Self> {
760+
match key {
761+
DescriptorPublicKey::XPub(ref xpk) if xpk.wildcard != Wildcard::None => None,
762+
k => Some(DerivedDescriptorKey { key: k, index }),
763+
}
764+
}
765+
}
766+
767+
impl fmt::Display for DerivedDescriptorKey {
768+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
769+
self.key.fmt(f)
770+
}
771+
}
772+
773+
impl MiniscriptKey for DerivedDescriptorKey {
774+
// This allows us to be able to derive public keys even for PkH s
775+
type Hash = Self;
776+
777+
fn is_uncompressed(&self) -> bool {
778+
self.key.is_uncompressed()
779+
}
780+
781+
fn is_x_only_key(&self) -> bool {
782+
self.key.is_x_only_key()
783+
}
784+
785+
fn to_pubkeyhash(&self) -> Self {
786+
self.clone()
787+
}
788+
}
789+
790+
impl ToPublicKey for DerivedDescriptorKey {
791+
fn to_public_key(&self) -> bitcoin::PublicKey {
792+
let secp = Secp256k1::verification_only();
793+
self.key.derive_public_key(&secp).unwrap()
794+
}
795+
796+
fn hash_to_hash160(hash: &Self) -> hash160::Hash {
797+
hash.to_public_key().to_pubkeyhash()
798+
}
799+
}
800+
723801
#[cfg(test)]
724802
mod test {
725803
use super::{DescriptorKeyParseError, DescriptorPublicKey, DescriptorSecretKey};

src/descriptor/mod.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ mod checksum;
6363
mod key;
6464

6565
pub use self::key::{
66-
ConversionError, DescriptorKeyParseError, DescriptorPublicKey, DescriptorSecretKey,
67-
DescriptorXKey, InnerXKey, SinglePriv, SinglePub, SinglePubKey, Wildcard,
66+
ConversionError, DerivedDescriptorKey, DescriptorKeyParseError, DescriptorPublicKey,
67+
DescriptorSecretKey, DescriptorXKey, InnerXKey, SinglePriv, SinglePub, SinglePubKey, Wildcard,
6868
};
6969

7070
/// Alias type for a map of public key to secret key
@@ -661,7 +661,7 @@ impl Descriptor<DescriptorPublicKey> {
661661
///
662662
/// In most cases, you would want to use [`Self::derived_descriptor`] directly to obtain
663663
/// a [`Descriptor<bitcoin::PublicKey>`]
664-
pub fn derive(&self, index: u32) -> Descriptor<DescriptorPublicKey> {
664+
pub fn derive(&self, index: u32) -> Descriptor<DerivedDescriptorKey> {
665665
self.translate_pk2_infallible(|pk| pk.clone().derive(index))
666666
}
667667

@@ -1616,18 +1616,17 @@ mod tests {
16161616
let index = 5;
16171617

16181618
// Parse descriptor
1619-
let mut desc_one = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_one).unwrap();
1620-
let mut desc_two = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_two).unwrap();
1619+
let desc_one = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_one).unwrap();
1620+
let desc_two = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_two).unwrap();
16211621

16221622
// Same string formatting
16231623
assert_eq!(desc_one.to_string(), raw_desc_one);
16241624
assert_eq!(desc_two.to_string(), raw_desc_two);
16251625

1626-
// Derive a child if the descriptor is ranged
1627-
if raw_desc_one.contains("*") && raw_desc_two.contains("*") {
1628-
desc_one = desc_one.derive(index);
1629-
desc_two = desc_two.derive(index);
1630-
}
1626+
// Derive a child in case the descriptor is ranged. If it's not this won't have any
1627+
// effect
1628+
let desc_one = desc_one.derive(index);
1629+
let desc_two = desc_two.derive(index);
16311630

16321631
// Same address
16331632
let addr_one = desc_one
@@ -1723,7 +1722,7 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
17231722
res_descriptor_str.parse().unwrap();
17241723
let res_descriptor = Descriptor::new_sh(res_policy.compile().unwrap()).unwrap();
17251724

1726-
assert_eq!(res_descriptor, derived_descriptor);
1725+
assert_eq!(res_descriptor.to_string(), derived_descriptor.to_string());
17271726
}
17281727

17291728
#[test]

0 commit comments

Comments
 (0)