Skip to content

Commit 878c000

Browse files
committed
Define a new type for derived DescriptorPublicKeys
1 parent d8cc633 commit 878c000

File tree

2 files changed

+153
-25
lines changed

2 files changed

+153
-25
lines changed

src/descriptor/key.rs

+129-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use std::{error, fmt, str::FromStr};
1+
use std::{cmp, error, fmt, hash, 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
};
@@ -58,6 +57,15 @@ pub enum DescriptorSecretKey {
5857
XPrv(DescriptorXKey<bip32::ExtendedPrivKey>),
5958
}
6059

60+
/// A derived [`DescriptorPublicKey`]
61+
///
62+
/// Derived keys are guaranteed to never contain wildcards
63+
pub struct DerivedDescriptorKey<'secp, C: 'secp + Verification> {
64+
key: DescriptorPublicKey,
65+
index: u32,
66+
secp: &'secp Secp256k1<C>,
67+
}
68+
6169
impl fmt::Display for DescriptorSecretKey {
6270
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
6371
match self {
@@ -438,7 +446,11 @@ impl DescriptorPublicKey {
438446
/// If this public key has a wildcard, replace it by the given index
439447
///
440448
/// Panics if given an index ≥ 2^31
441-
pub fn derive(mut self, index: u32) -> DescriptorPublicKey {
449+
pub fn derive<'secp, C: Verification>(
450+
mut self,
451+
index: u32,
452+
secp: &'secp Secp256k1<C>,
453+
) -> DerivedDescriptorKey<'secp, C> {
442454
if let DescriptorPublicKey::XPub(mut xpub) = self {
443455
match xpub.wildcard {
444456
Wildcard::None => {}
@@ -456,7 +468,8 @@ impl DescriptorPublicKey {
456468
xpub.wildcard = Wildcard::None;
457469
self = DescriptorPublicKey::XPub(xpub);
458470
}
459-
self
471+
472+
DerivedDescriptorKey::new(self, index, secp)
460473
}
461474

462475
/// Computes the public key corresponding to this descriptor key.
@@ -471,7 +484,7 @@ impl DescriptorPublicKey {
471484
/// to avoid hardened derivation steps, start from a `DescriptorSecretKey`
472485
/// and call `as_public`, or call `TranslatePk2::translate_pk2` with
473486
/// some function which has access to secret key data.
474-
pub fn derive_public_key<C: secp256k1::Verification>(
487+
pub fn derive_public_key<C: Verification>(
475488
&self,
476489
secp: &Secp256k1<C>,
477490
) -> Result<bitcoin::PublicKey, ConversionError> {
@@ -717,6 +730,115 @@ impl MiniscriptKey for DescriptorPublicKey {
717730
}
718731
}
719732

733+
impl<'secp, C: Verification> DerivedDescriptorKey<'secp, C> {
734+
/// Computes the raw [`bitcoin::PublicKey`] for this descriptor key.
735+
///
736+
/// Will return an error if the key has any hardened derivation steps
737+
/// in its path, but unlike [`DescriptorPublicKey::derive_public_key`]
738+
/// this won't error in case of wildcards, because derived keys are
739+
/// guaranteed to never contain one.
740+
pub fn derive_public_key(&self) -> Result<bitcoin::PublicKey, ConversionError> {
741+
self.key.derive_public_key(self.secp)
742+
}
743+
744+
/// Return the derivation index of this key
745+
pub fn get_index(&self) -> u32 {
746+
self.index
747+
}
748+
749+
/// Construct an instance from a descriptor key and a derivation index
750+
///
751+
/// Panics if the key contains a wildcard
752+
fn new(key: DescriptorPublicKey, index: u32, secp: &'secp Secp256k1<C>) -> Self {
753+
if let DescriptorPublicKey::XPub(ref xpk) = &key {
754+
assert!(
755+
xpk.wildcard == Wildcard::None,
756+
"Derived descriptor keys cannot contain any wildcards"
757+
);
758+
}
759+
760+
DerivedDescriptorKey { key, index, secp }
761+
}
762+
}
763+
764+
impl<'secp, C: Verification> fmt::Display for DerivedDescriptorKey<'secp, C> {
765+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
766+
self.key.fmt(f)
767+
}
768+
}
769+
impl<'secp, C: Verification> fmt::Debug for DerivedDescriptorKey<'secp, C> {
770+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
771+
f.debug_struct("DerivedDescriptorKey")
772+
.field("key", &self.key)
773+
.field("index", &self.index)
774+
.finish()
775+
}
776+
}
777+
778+
impl<'secp, C: Verification> PartialEq for DerivedDescriptorKey<'secp, C> {
779+
fn eq(&self, other: &Self) -> bool {
780+
self.key.eq(&other.key) && self.index.eq(&other.index)
781+
}
782+
}
783+
impl<'secp, C: Verification> Eq for DerivedDescriptorKey<'secp, C> {}
784+
785+
impl<'secp, C: Verification> PartialOrd for DerivedDescriptorKey<'secp, C> {
786+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
787+
Some(self.cmp(other))
788+
}
789+
}
790+
impl<'secp, C: Verification> Ord for DerivedDescriptorKey<'secp, C> {
791+
fn cmp(&self, other: &Self) -> cmp::Ordering {
792+
self.key
793+
.cmp(&other.key)
794+
.then_with(|| self.index.cmp(&other.index))
795+
}
796+
}
797+
798+
impl<'secp, C: Verification> Clone for DerivedDescriptorKey<'secp, C> {
799+
fn clone(&self) -> Self {
800+
DerivedDescriptorKey {
801+
key: self.key.clone(),
802+
index: self.index,
803+
secp: self.secp,
804+
}
805+
}
806+
}
807+
808+
impl<'secp, C: Verification> hash::Hash for DerivedDescriptorKey<'secp, C> {
809+
fn hash<H: hash::Hasher>(&self, state: &mut H) {
810+
self.key.hash(state);
811+
self.index.hash(state);
812+
}
813+
}
814+
815+
impl<'secp, C: Verification> MiniscriptKey for DerivedDescriptorKey<'secp, C> {
816+
// This allows us to be able to derive public keys even for PkH s
817+
type Hash = Self;
818+
819+
fn is_uncompressed(&self) -> bool {
820+
self.key.is_uncompressed()
821+
}
822+
823+
fn is_x_only_key(&self) -> bool {
824+
self.key.is_x_only_key()
825+
}
826+
827+
fn to_pubkeyhash(&self) -> Self {
828+
self.clone()
829+
}
830+
}
831+
832+
impl<'secp, C: Verification> ToPublicKey for DerivedDescriptorKey<'secp, C> {
833+
fn to_public_key(&self) -> bitcoin::PublicKey {
834+
self.key.derive_public_key(&self.secp).unwrap()
835+
}
836+
837+
fn hash_to_hash160(hash: &Self) -> hash160::Hash {
838+
hash.to_public_key().to_pubkeyhash()
839+
}
840+
}
841+
720842
#[cfg(test)]
721843
mod test {
722844
use super::{DescriptorKeyParseError, DescriptorPublicKey, DescriptorSecretKey};

src/descriptor/mod.rs

+24-18
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ mod checksum;
6161
mod key;
6262

6363
pub use self::key::{
64-
ConversionError, DescriptorKeyParseError, DescriptorPublicKey, DescriptorSecretKey,
65-
DescriptorSinglePriv, DescriptorSinglePub, DescriptorXKey, InnerXKey, SinglePubKey, Wildcard,
64+
ConversionError, DerivedDescriptorKey, DescriptorKeyParseError, DescriptorPublicKey,
65+
DescriptorSecretKey, DescriptorSinglePriv, DescriptorSinglePub, DescriptorXKey, InnerXKey,
66+
SinglePubKey, Wildcard,
6667
};
6768

6869
/// Alias type for a map of public key to secret key
@@ -653,8 +654,12 @@ impl Descriptor<DescriptorPublicKey> {
653654
///
654655
/// In most cases, you would want to use [`Self::derived_descriptor`] directly to obtain
655656
/// a [`Descriptor<bitcoin::PublicKey>`]
656-
pub fn derive(&self, index: u32) -> Descriptor<DescriptorPublicKey> {
657-
self.translate_pk2_infallible(|pk| pk.clone().derive(index))
657+
pub fn derive<'secp, C: secp256k1::Verification>(
658+
&self,
659+
index: u32,
660+
secp: &'secp secp256k1::Secp256k1<C>,
661+
) -> Descriptor<DerivedDescriptorKey<'secp, C>> {
662+
self.translate_pk2_infallible(|pk| pk.clone().derive(index, secp))
658663
}
659664

660665
/// Derive a [`Descriptor`] with a concrete [`bitcoin::PublicKey`] at a given index
@@ -687,8 +692,8 @@ impl Descriptor<DescriptorPublicKey> {
687692
index: u32,
688693
) -> Result<Descriptor<bitcoin::PublicKey>, ConversionError> {
689694
let derived = self
690-
.derive(index)
691-
.translate_pk2(|xpk| xpk.derive_public_key(secp))?;
695+
.derive(index, secp)
696+
.translate_pk2(|xpk| xpk.derive_public_key())?;
692697
Ok(derived)
693698
}
694699

@@ -842,7 +847,7 @@ mod tests {
842847
use std::cmp;
843848
use std::collections::HashMap;
844849
use std::str::FromStr;
845-
use {Descriptor, DummyKey, Error, Miniscript, Satisfier, TranslatePk2};
850+
use {Descriptor, DummyKey, Error, Miniscript, Satisfier};
846851

847852
#[cfg(feature = "compiler")]
848853
use policy;
@@ -1586,27 +1591,26 @@ mod tests {
15861591
let index = 5;
15871592

15881593
// Parse descriptor
1589-
let mut desc_one = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_one).unwrap();
1590-
let mut desc_two = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_two).unwrap();
1594+
let desc_one = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_one).unwrap();
1595+
let desc_two = Descriptor::<DescriptorPublicKey>::from_str(raw_desc_two).unwrap();
15911596

15921597
// Same string formatting
15931598
assert_eq!(desc_one.to_string(), raw_desc_one);
15941599
assert_eq!(desc_two.to_string(), raw_desc_two);
15951600

1596-
// Derive a child if the descriptor is ranged
1597-
if raw_desc_one.contains("*") && raw_desc_two.contains("*") {
1598-
desc_one = desc_one.derive(index);
1599-
desc_two = desc_two.derive(index);
1600-
}
1601+
// Derive a child in case the descriptor is ranged. If it's not this won't have any
1602+
// effect
1603+
let desc_one = desc_one.derive(index, &secp_ctx);
1604+
let desc_two = desc_two.derive(index, &secp_ctx);
16011605

16021606
// Same address
16031607
let addr_one = desc_one
1604-
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
1608+
.translate_pk2(|xpk| xpk.derive_public_key())
16051609
.unwrap()
16061610
.address(bitcoin::Network::Bitcoin)
16071611
.unwrap();
16081612
let addr_two = desc_two
1609-
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
1613+
.translate_pk2(|xpk| xpk.derive_public_key())
16101614
.unwrap()
16111615
.address(bitcoin::Network::Bitcoin)
16121616
.unwrap();
@@ -1677,13 +1681,15 @@ mod tests {
16771681
#[test]
16781682
#[cfg(feature = "compiler")]
16791683
fn parse_and_derive() {
1684+
let secp = &secp256k1::Secp256k1::verification_only();
1685+
16801686
let descriptor_str = "thresh(2,\
16811687
pk([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*),\
16821688
pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1),\
16831689
pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
16841690
let policy: policy::concrete::Policy<DescriptorPublicKey> = descriptor_str.parse().unwrap();
16851691
let descriptor = Descriptor::new_sh(policy.compile().unwrap()).unwrap();
1686-
let derived_descriptor = descriptor.derive(42);
1692+
let derived_descriptor = descriptor.derive(42, &secp);
16871693

16881694
let res_descriptor_str = "thresh(2,\
16891695
pk([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/42),\
@@ -1693,7 +1699,7 @@ pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
16931699
res_descriptor_str.parse().unwrap();
16941700
let res_descriptor = Descriptor::new_sh(res_policy.compile().unwrap()).unwrap();
16951701

1696-
assert_eq!(res_descriptor, derived_descriptor);
1702+
assert_eq!(res_descriptor.to_string(), derived_descriptor.to_string());
16971703
}
16981704

16991705
#[test]

0 commit comments

Comments
 (0)