Skip to content

Commit 28615fd

Browse files
committed
Define a new type for derived DescriptorPublicKeys
1 parent de74dc0 commit 28615fd

File tree

2 files changed

+90
-17
lines changed

2 files changed

+90
-17
lines changed

src/descriptor/key.rs

Lines changed: 80 additions & 6 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 {
@@ -434,7 +442,7 @@ impl DescriptorPublicKey {
434442
/// If this public key has a wildcard, replace it by the given index
435443
///
436444
/// Panics if given an index ≥ 2^31
437-
pub fn derive(mut self, index: u32) -> DescriptorPublicKey {
445+
pub fn derive(mut self, index: u32) -> DerivedDescriptorKey {
438446
if let DescriptorPublicKey::XPub(mut xpub) = self {
439447
match xpub.wildcard {
440448
Wildcard::None => {}
@@ -452,7 +460,9 @@ impl DescriptorPublicKey {
452460
xpub.wildcard = Wildcard::None;
453461
self = DescriptorPublicKey::XPub(xpub);
454462
}
455-
self
463+
464+
DerivedDescriptorKey::new(self, index)
465+
.expect("The key should not contain any wildcards at this point")
456466
}
457467

458468
/// Computes the public key corresponding to this descriptor key.
@@ -467,7 +477,7 @@ impl DescriptorPublicKey {
467477
/// to avoid hardened derivation steps, start from a `DescriptorSecretKey`
468478
/// and call `as_public`, or call `TranslatePk2::translate_pk2` with
469479
/// some function which has access to secret key data.
470-
pub fn derive_public_key<C: secp256k1::Verification>(
480+
pub fn derive_public_key<C: Verification>(
471481
&self,
472482
secp: &Secp256k1<C>,
473483
) -> Result<bitcoin::PublicKey, ConversionError> {
@@ -713,6 +723,70 @@ impl MiniscriptKey for DescriptorPublicKey {
713723
}
714724
}
715725

726+
impl DerivedDescriptorKey {
727+
/// Computes the raw [`bitcoin::PublicKey`] for this descriptor key.
728+
///
729+
/// Will return an error if the key has any hardened derivation steps
730+
/// in its path, but unlike [`DescriptorPublicKey::derive_public_key`]
731+
/// this won't error in case of wildcards, because derived keys are
732+
/// guaranteed to never contain one.
733+
pub fn derive_public_key<C: Verification>(
734+
&self,
735+
secp: &Secp256k1<C>,
736+
) -> Result<bitcoin::PublicKey, ConversionError> {
737+
self.key.derive_public_key(secp)
738+
}
739+
740+
/// Return the derivation index of this key
741+
pub fn get_index(&self) -> u32 {
742+
self.index
743+
}
744+
745+
/// Construct an instance from a descriptor key and a derivation index
746+
///
747+
/// Returns `None` if the key contains a wildcard
748+
fn new(key: DescriptorPublicKey, index: u32) -> Option<Self> {
749+
match key {
750+
DescriptorPublicKey::XPub(ref xpk) if xpk.wildcard != Wildcard::None => None,
751+
k => Some(DerivedDescriptorKey { key: k, index }),
752+
}
753+
}
754+
}
755+
756+
impl fmt::Display for DerivedDescriptorKey {
757+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
758+
self.key.fmt(f)
759+
}
760+
}
761+
762+
impl MiniscriptKey for DerivedDescriptorKey {
763+
// This allows us to be able to derive public keys even for PkH s
764+
type Hash = Self;
765+
766+
fn is_uncompressed(&self) -> bool {
767+
self.key.is_uncompressed()
768+
}
769+
770+
fn is_x_only_key(&self) -> bool {
771+
self.key.is_x_only_key()
772+
}
773+
774+
fn to_pubkeyhash(&self) -> Self {
775+
self.clone()
776+
}
777+
}
778+
779+
impl ToPublicKey for DerivedDescriptorKey {
780+
fn to_public_key(&self) -> bitcoin::PublicKey {
781+
let secp = Secp256k1::verification_only();
782+
self.key.derive_public_key(&secp).unwrap()
783+
}
784+
785+
fn hash_to_hash160(hash: &Self) -> hash160::Hash {
786+
hash.to_public_key().to_pubkeyhash()
787+
}
788+
}
789+
716790
#[cfg(test)]
717791
mod test {
718792
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
@@ -671,7 +671,7 @@ impl Descriptor<DescriptorPublicKey> {
671671
///
672672
/// In most cases, you would want to use [`Self::derived_descriptor`] directly to obtain
673673
/// a [`Descriptor<bitcoin::PublicKey>`]
674-
pub fn derive(&self, index: u32) -> Descriptor<DescriptorPublicKey> {
674+
pub fn derive(&self, index: u32) -> Descriptor<DerivedDescriptorKey> {
675675
self.translate_pk2_infallible(|pk| pk.clone().derive(index))
676676
}
677677

@@ -1626,18 +1626,17 @@ mod tests {
16261626
let index = 5;
16271627

16281628
// Parse descriptor
1629-
let mut desc_one = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_one).unwrap();
1630-
let mut desc_two = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_two).unwrap();
1629+
let desc_one = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_one).unwrap();
1630+
let desc_two = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_two).unwrap();
16311631

16321632
// Same string formatting
16331633
assert_eq!(desc_one.to_string(), raw_desc_one);
16341634
assert_eq!(desc_two.to_string(), raw_desc_two);
16351635

1636-
// Derive a child if the descriptor is ranged
1637-
if raw_desc_one.contains("*") && raw_desc_two.contains("*") {
1638-
desc_one = desc_one.derive(index);
1639-
desc_two = desc_two.derive(index);
1640-
}
1636+
// Derive a child in case the descriptor is ranged. If it's not this won't have any
1637+
// effect
1638+
let desc_one = desc_one.derive(index);
1639+
let desc_two = desc_two.derive(index);
16411640

16421641
// Same address
16431642
let addr_one = desc_one
@@ -1733,7 +1732,7 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
17331732
res_descriptor_str.parse().unwrap();
17341733
let res_descriptor = Descriptor::new_sh(res_policy.compile().unwrap()).unwrap();
17351734

1736-
assert_eq!(res_descriptor, derived_descriptor);
1735+
assert_eq!(res_descriptor.to_string(), derived_descriptor.to_string());
17371736
}
17381737

17391738
#[test]

0 commit comments

Comments
 (0)