Skip to content

Commit c7c39f1

Browse files
committed
Merge #448: DerivedDescriptor unfinished business
2ad6555 Rename "derive" things that are not doing derivation (LLFourn) 6dc6aca Make DerivedDescriptorKey first-class citizen (LLFourn) Pull request description: This PR further develops the idea introduced in #345. It has two commits both with relatively detailed commit messages that can be reviewed separately. In summary: 1. Develop the `DerivedDescriptorKey` (a key that is guaranteed not to have wildcard in it) idea more by adding missing functionality and refining the API. In addition, I removed the error cases from `ConversionError` which seems to have been omitted from #345. 2. Since the introduction of `DerivedDescriptorKey`, the overlapping usage of the term "derive" has become quite confusing. In addition to the usual definition of "derive" we have also used it to mean "replace a wildcard with a particular derivation index". I deprecated and renamed everything that uses the latter definition. ACKs for top commit: apoelstra: ACK 2ad6555 sanket1729: ACK 2ad6555. Thanks for the clean changes. Tree-SHA512: 0198404a1bfecab2324a8785117248fc566cfbb53decbd234928e378f102bdc5c5de6d31b437b8f1b0ba90ef524a362a46150028f80a4b029589406233abd2fe
2 parents d524c6d + 2ad6555 commit c7c39f1

File tree

6 files changed

+209
-140
lines changed

6 files changed

+209
-140
lines changed

examples/xpub_descriptors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::str::FromStr;
1818

1919
use miniscript::bitcoin::secp256k1::{Secp256k1, Verification};
2020
use miniscript::bitcoin::{Address, Network};
21-
use miniscript::{Descriptor, DescriptorPublicKey};
21+
use miniscript::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey};
2222

2323
const XPUB_1: &str = "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB";
2424
const XPUB_2: &str = "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH";
@@ -40,9 +40,9 @@ fn p2wsh<C: Verification>(secp: &Secp256k1<C>) -> Address {
4040
let s = format!("wsh(sortedmulti(1,{},{}))", XPUB_1, XPUB_2);
4141
// let s = format!("wsh(sortedmulti(1,{},{}))", XPUB_2, XPUB_1);
4242

43-
let address = Descriptor::<DescriptorPublicKey>::from_str(&s)
43+
let address = Descriptor::<DefiniteDescriptorKey>::from_str(&s)
4444
.unwrap()
45-
.derived_descriptor(&secp, 0) // dummy index value if it not a wildcard
45+
.derived_descriptor(&secp)
4646
.unwrap()
4747
.address(Network::Bitcoin)
4848
.unwrap();

src/descriptor/key.rs

Lines changed: 81 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,9 @@ pub enum SinglePubKey {
7070
XOnly(XOnlyPublicKey),
7171
}
7272

73-
/// A derived [`DescriptorPublicKey`]
74-
///
75-
/// Derived keys are guaranteed to never contain wildcards
73+
/// A [`DescriptorPublicKey`] without any wildcards.
7674
#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
77-
pub struct DerivedDescriptorKey {
78-
key: DescriptorPublicKey,
79-
index: u32,
80-
}
75+
pub struct DefiniteDescriptorKey(DescriptorPublicKey);
8176

8277
impl fmt::Display for DescriptorSecretKey {
8378
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -357,22 +352,14 @@ impl FromStr for DescriptorPublicKey {
357352
/// Descriptor key conversion error
358353
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
359354
pub enum ConversionError {
360-
/// Attempted to convert a key with a wildcard to a bitcoin public key
361-
Wildcard,
362355
/// Attempted to convert a key with hardened derivations to a bitcoin public key
363356
HardenedChild,
364-
/// Attempted to convert a key with a hardened wildcard to a bitcoin public key
365-
HardenedWildcard,
366357
}
367358

368359
impl fmt::Display for ConversionError {
369360
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370361
f.write_str(match *self {
371-
ConversionError::Wildcard => "uninstantiated wildcard in bip32 path",
372362
ConversionError::HardenedChild => "hardened child step in bip32 path",
373-
ConversionError::HardenedWildcard => {
374-
"hardened and uninstantiated wildcard in bip32 path"
375-
}
376363
})
377364
}
378365
}
@@ -383,7 +370,7 @@ impl error::Error for ConversionError {
383370
use self::ConversionError::*;
384371

385372
match self {
386-
Wildcard | HardenedChild | HardenedWildcard => None,
373+
HardenedChild => None,
387374
}
388375
}
389376
}
@@ -441,40 +428,50 @@ impl DescriptorPublicKey {
441428
}
442429
}
443430

444-
/// Whether or not the key has a wildcards
431+
/// Whether or not the key has a wildcard
432+
#[deprecated(note = "use has_wildcard instead")]
445433
pub fn is_deriveable(&self) -> bool {
434+
self.has_wildcard()
435+
}
436+
437+
/// Whether or not the key has a wildcard
438+
pub fn has_wildcard(&self) -> bool {
446439
match *self {
447440
DescriptorPublicKey::Single(..) => false,
448441
DescriptorPublicKey::XPub(ref xpub) => xpub.wildcard != Wildcard::None,
449442
}
450443
}
451444

452-
/// Derives the [`DescriptorPublicKey`] at `index` if this key is an xpub and has a wildcard.
445+
#[deprecated(note = "use at_derivation_index instead")]
446+
/// Deprecated name of [`at_derivation_index`].
447+
pub fn derive(self, index: u32) -> DefiniteDescriptorKey {
448+
self.at_derivation_index(index)
449+
}
450+
451+
/// Replaces any wildcard (i.e. `/*`) in the key with a particular derivation index, turning it into a
452+
/// *definite* key (i.e. one where all the derivation paths are set).
453453
///
454454
/// # Returns
455455
///
456456
/// - If this key is not an xpub, returns `self`.
457457
/// - If this key is an xpub but does not have a wildcard, returns `self`.
458-
/// - Otherwise, returns the derived xpub at `index` (removing the wildcard).
459-
///
460-
/// Since it's guaranteed that extended keys won't have wildcards, the key is returned as
461-
/// [`DerivedDescriptorKey`].
458+
/// - Otherwise, returns the xpub at derivation `index` (removing the wildcard).
462459
///
463460
/// # Panics
464461
///
465462
/// If `index` ≥ 2^31
466-
pub fn derive(self, index: u32) -> DerivedDescriptorKey {
467-
let derived = match self {
463+
pub fn at_derivation_index(self, index: u32) -> DefiniteDescriptorKey {
464+
let definite = match self {
468465
DescriptorPublicKey::Single(_) => self,
469466
DescriptorPublicKey::XPub(xpub) => {
470467
let derivation_path = match xpub.wildcard {
471468
Wildcard::None => xpub.derivation_path,
472-
Wildcard::Unhardened => xpub
473-
.derivation_path
474-
.into_child(bip32::ChildNumber::from_normal_idx(index).unwrap()),
475-
Wildcard::Hardened => xpub
476-
.derivation_path
477-
.into_child(bip32::ChildNumber::from_hardened_idx(index).unwrap()),
469+
Wildcard::Unhardened => xpub.derivation_path.into_child(
470+
bip32::ChildNumber::from_normal_idx(index).expect("index must < 2^31"),
471+
),
472+
Wildcard::Hardened => xpub.derivation_path.into_child(
473+
bip32::ChildNumber::from_hardened_idx(index).expect("index must < 2^31"),
474+
),
478475
};
479476
DescriptorPublicKey::XPub(DescriptorXKey {
480477
origin: xpub.origin,
@@ -485,7 +482,7 @@ impl DescriptorPublicKey {
485482
}
486483
};
487484

488-
DerivedDescriptorKey::new(derived, index)
485+
DefiniteDescriptorKey::new(definite)
489486
.expect("The key should not contain any wildcards at this point")
490487
}
491488

@@ -494,13 +491,10 @@ impl DescriptorPublicKey {
494491
/// and returns the obtained full [`bitcoin::PublicKey`]. All BIP32 derivations
495492
/// always return a compressed key
496493
///
497-
/// Will return an error if the descriptor key has any hardened
498-
/// derivation steps in its path, or if the key has any wildcards.
494+
/// Will return an error if the descriptor key has any hardened derivation steps in its path. To
495+
/// avoid this error you should replace any such public keys first with [`translate_pk`].
499496
///
500-
/// To ensure there are no wildcards, call `.derive(0)` or similar;
501-
/// to avoid hardened derivation steps, start from a `DescriptorSecretKey`
502-
/// and call `to_public`, or call `TranslatePk2::translate_pk2` with
503-
/// some function which has access to secret key data.
497+
/// [`translate_pk`]: crate::TranslatePk::translate_pk
504498
pub fn derive_public_key<C: Verification>(
505499
&self,
506500
secp: &Secp256k1<C>,
@@ -511,8 +505,9 @@ impl DescriptorPublicKey {
511505
SinglePubKey::XOnly(xpk) => Ok(xpk.to_public_key()),
512506
},
513507
DescriptorPublicKey::XPub(ref xpk) => match xpk.wildcard {
514-
Wildcard::Unhardened => Err(ConversionError::Wildcard),
515-
Wildcard::Hardened => Err(ConversionError::HardenedWildcard),
508+
Wildcard::Unhardened | Wildcard::Hardened => {
509+
unreachable!("we've excluded this error case")
510+
}
516511
Wildcard::None => match xpk.xkey.derive_pub(secp, &xpk.derivation_path.as_ref()) {
517512
Ok(xpub) => Ok(bitcoin::PublicKey::new(xpub.public_key)),
518513
Err(bip32::Error::CannotDeriveFromHardenedKey) => {
@@ -767,7 +762,7 @@ impl MiniscriptKey for DescriptorPublicKey {
767762
}
768763
}
769764

770-
impl DerivedDescriptorKey {
765+
impl DefiniteDescriptorKey {
771766
/// Computes the raw [`bitcoin::PublicKey`] for this descriptor key.
772767
///
773768
/// Will return an error if the key has any hardened derivation steps
@@ -778,32 +773,51 @@ impl DerivedDescriptorKey {
778773
&self,
779774
secp: &Secp256k1<C>,
780775
) -> Result<bitcoin::PublicKey, ConversionError> {
781-
self.key.derive_public_key(secp)
782-
}
783-
784-
/// Return the derivation index of this key
785-
pub fn index(&self) -> u32 {
786-
self.index
776+
self.0.derive_public_key(secp)
787777
}
788778

789779
/// Construct an instance from a descriptor key and a derivation index
790780
///
791781
/// Returns `None` if the key contains a wildcard
792-
fn new(key: DescriptorPublicKey, index: u32) -> Option<Self> {
793-
match key {
794-
DescriptorPublicKey::XPub(ref xpk) if xpk.wildcard != Wildcard::None => None,
795-
k => Some(DerivedDescriptorKey { key: k, index }),
782+
fn new(key: DescriptorPublicKey) -> Option<Self> {
783+
if key.has_wildcard() {
784+
None
785+
} else {
786+
Some(Self(key))
796787
}
797788
}
789+
790+
/// The fingerprint of the master key associated with this key, `0x00000000` if none.
791+
pub fn master_fingerprint(&self) -> bip32::Fingerprint {
792+
self.0.master_fingerprint()
793+
}
794+
795+
/// Full path, from the master key
796+
pub fn full_derivation_path(&self) -> bip32::DerivationPath {
797+
self.0.full_derivation_path()
798+
}
799+
}
800+
801+
impl FromStr for DefiniteDescriptorKey {
802+
type Err = DescriptorKeyParseError;
803+
804+
fn from_str(s: &str) -> Result<Self, Self::Err> {
805+
let inner = DescriptorPublicKey::from_str(s)?;
806+
Ok(
807+
DefiniteDescriptorKey::new(inner).ok_or(DescriptorKeyParseError(
808+
"cannot parse key with a wilcard as a DerivedDescriptorKey",
809+
))?,
810+
)
811+
}
798812
}
799813

800-
impl fmt::Display for DerivedDescriptorKey {
814+
impl fmt::Display for DefiniteDescriptorKey {
801815
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
802-
self.key.fmt(f)
816+
self.0.fmt(f)
803817
}
804818
}
805819

806-
impl MiniscriptKey for DerivedDescriptorKey {
820+
impl MiniscriptKey for DefiniteDescriptorKey {
807821
// This allows us to be able to derive public keys even for PkH s
808822
type RawPkHash = Self;
809823
type Sha256 = sha256::Hash;
@@ -812,22 +826,22 @@ impl MiniscriptKey for DerivedDescriptorKey {
812826
type Hash160 = hash160::Hash;
813827

814828
fn is_uncompressed(&self) -> bool {
815-
self.key.is_uncompressed()
829+
self.0.is_uncompressed()
816830
}
817831

818832
fn is_x_only_key(&self) -> bool {
819-
self.key.is_x_only_key()
833+
self.0.is_x_only_key()
820834
}
821835

822836
fn to_pubkeyhash(&self) -> Self {
823837
self.clone()
824838
}
825839
}
826840

827-
impl ToPublicKey for DerivedDescriptorKey {
841+
impl ToPublicKey for DefiniteDescriptorKey {
828842
fn to_public_key(&self) -> bitcoin::PublicKey {
829843
let secp = Secp256k1::verification_only();
830-
self.key.derive_public_key(&secp).unwrap()
844+
self.0.derive_public_key(&secp).unwrap()
831845
}
832846

833847
fn hash_to_hash160(hash: &Self) -> hash160::Hash {
@@ -851,6 +865,12 @@ impl ToPublicKey for DerivedDescriptorKey {
851865
}
852866
}
853867

868+
impl From<DefiniteDescriptorKey> for DescriptorPublicKey {
869+
fn from(d: DefiniteDescriptorKey) -> Self {
870+
d.0
871+
}
872+
}
873+
854874
#[cfg(test)]
855875
mod test {
856876
use core::str::FromStr;
@@ -957,17 +977,17 @@ mod test {
957977
let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2").unwrap();
958978
assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00");
959979
assert_eq!(public_key.full_derivation_path().to_string(), "m/0'/1'/2");
960-
assert_eq!(public_key.is_deriveable(), false);
980+
assert_eq!(public_key.has_wildcard(), false);
961981

962982
let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/*").unwrap();
963983
assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00");
964984
assert_eq!(public_key.full_derivation_path().to_string(), "m/0'/1'");
965-
assert_eq!(public_key.is_deriveable(), true);
985+
assert_eq!(public_key.has_wildcard(), true);
966986

967987
let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/*h").unwrap();
968988
assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00");
969989
assert_eq!(public_key.full_derivation_path().to_string(), "m/0'/1'");
970-
assert_eq!(public_key.is_deriveable(), true);
990+
assert_eq!(public_key.has_wildcard(), true);
971991
}
972992

973993
#[test]
@@ -979,7 +999,7 @@ mod test {
979999
assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2");
9801000
assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
9811001
assert_eq!(public_key.full_derivation_path().to_string(), "m/0'/1'/2");
982-
assert_eq!(public_key.is_deriveable(), false);
1002+
assert_eq!(public_key.has_wildcard(), false);
9831003

9841004
let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2'").unwrap();
9851005
let public_key = secret_key.to_public(&secp).unwrap();

0 commit comments

Comments
 (0)