miniscript/descriptor/
key.rs

1// SPDX-License-Identifier: CC0-1.0
2
3use core::convert::TryInto;
4use core::fmt;
5use core::str::FromStr;
6#[cfg(feature = "std")]
7use std::error;
8
9use bitcoin::bip32::{self, XKeyIdentifier};
10use bitcoin::hashes::hex::FromHex;
11use bitcoin::hashes::{hash160, ripemd160, sha256, Hash, HashEngine};
12use bitcoin::key::XOnlyPublicKey;
13use bitcoin::secp256k1::{Secp256k1, Signing, Verification};
14
15use crate::prelude::*;
16#[cfg(feature = "serde")]
17use crate::serde::{Deserialize, Deserializer, Serialize, Serializer};
18use crate::{hash256, MiniscriptKey, ToPublicKey};
19
20/// The descriptor pubkey, either a single pubkey or an xpub.
21#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
22pub enum DescriptorPublicKey {
23    /// Single public key.
24    Single(SinglePub),
25    /// Extended public key (xpub).
26    XPub(DescriptorXKey<bip32::Xpub>),
27    /// Multiple extended public keys.
28    MultiXPub(DescriptorMultiXKey<bip32::Xpub>),
29}
30
31/// The descriptor secret key, either a single private key or an xprv.
32#[derive(Debug, Eq, PartialEq, Clone)]
33pub enum DescriptorSecretKey {
34    /// Single private key.
35    Single(SinglePriv),
36    /// Extended private key (xpriv).
37    XPrv(DescriptorXKey<bip32::Xpriv>),
38    /// Multiple extended private keys.
39    MultiXPrv(DescriptorMultiXKey<bip32::Xpriv>),
40}
41
42/// A descriptor [`SinglePubKey`] with optional origin information.
43#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
44pub struct SinglePub {
45    /// Origin information (fingerprint and derivation path).
46    pub origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
47    /// The public key.
48    pub key: SinglePubKey,
49}
50
51/// A descriptor [`bitcoin::PrivateKey`] with optional origin information.
52#[derive(Debug, Eq, PartialEq, Clone)]
53pub struct SinglePriv {
54    /// Origin information (fingerprint and derivation path).
55    pub origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
56    /// The private key.
57    pub key: bitcoin::PrivateKey,
58}
59
60/// An extended key with origin, derivation path, and wildcard.
61#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
62pub struct DescriptorXKey<K: InnerXKey> {
63    /// Origin information
64    pub origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
65    /// The extended key
66    pub xkey: K,
67    /// The derivation path
68    pub derivation_path: bip32::DerivationPath,
69    /// Whether the descriptor is wildcard
70    pub wildcard: Wildcard,
71}
72
73/// The derivation paths in a multipath key expression.
74#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
75pub struct DerivPaths(Vec<bip32::DerivationPath>);
76
77impl DerivPaths {
78    /// Create a non empty derivation paths list.
79    pub fn new(paths: Vec<bip32::DerivationPath>) -> Option<DerivPaths> {
80        if paths.is_empty() {
81            None
82        } else {
83            Some(DerivPaths(paths))
84        }
85    }
86
87    /// Get the list of derivation paths.
88    pub fn paths(&self) -> &Vec<bip32::DerivationPath> { &self.0 }
89
90    /// Get the list of derivation paths.
91    pub fn into_paths(self) -> Vec<bip32::DerivationPath> { self.0 }
92}
93
94/// Instance of one or more extended keys, as specified in BIP 389.
95#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
96pub struct DescriptorMultiXKey<K: InnerXKey> {
97    /// Origin information
98    pub origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
99    /// The extended key
100    pub xkey: K,
101    /// The derivation paths. Never empty.
102    pub derivation_paths: DerivPaths,
103    /// Whether the descriptor is wildcard
104    pub wildcard: Wildcard,
105}
106
107/// Single public key without any origin or range information.
108#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
109pub enum SinglePubKey {
110    /// A bitcoin public key (compressed or uncompressed).
111    FullKey(bitcoin::PublicKey),
112    /// An xonly public key.
113    XOnly(XOnlyPublicKey),
114}
115
116/// A [`DescriptorPublicKey`] without any wildcards.
117#[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash)]
118pub struct DefiniteDescriptorKey(DescriptorPublicKey);
119
120impl fmt::Display for DescriptorSecretKey {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        match self {
123            DescriptorSecretKey::Single(ref sk) => {
124                maybe_fmt_master_id(f, &sk.origin)?;
125                sk.key.fmt(f)?;
126                Ok(())
127            }
128            DescriptorSecretKey::XPrv(ref xprv) => {
129                maybe_fmt_master_id(f, &xprv.origin)?;
130                xprv.xkey.fmt(f)?;
131                fmt_derivation_path(f, &xprv.derivation_path)?;
132                match xprv.wildcard {
133                    Wildcard::None => {}
134                    Wildcard::Unhardened => write!(f, "/*")?,
135                    Wildcard::Hardened => write!(f, "/*h")?,
136                }
137                Ok(())
138            }
139            DescriptorSecretKey::MultiXPrv(ref xprv) => {
140                maybe_fmt_master_id(f, &xprv.origin)?;
141                xprv.xkey.fmt(f)?;
142                fmt_derivation_paths(f, xprv.derivation_paths.paths())?;
143                match xprv.wildcard {
144                    Wildcard::None => {}
145                    Wildcard::Unhardened => write!(f, "/*")?,
146                    Wildcard::Hardened => write!(f, "/*h")?,
147                }
148                Ok(())
149            }
150        }
151    }
152}
153
154/// Trait for "extended key" types like `xpub` and `xprv`. Used internally to generalize parsing and
155/// handling of `bip32::Xpub` and `bip32::Xpriv`.
156pub trait InnerXKey: fmt::Display + FromStr {
157    /// Returns the fingerprint of the key
158    fn xkey_fingerprint<C: Signing>(&self, secp: &Secp256k1<C>) -> bip32::Fingerprint;
159
160    /// Returns whether hardened steps can be derived on the key
161    ///
162    /// `true` for `bip32::Xpriv` and `false` for `bip32::Xpub`.
163    fn can_derive_hardened() -> bool;
164}
165
166impl InnerXKey for bip32::Xpub {
167    fn xkey_fingerprint<C: Signing>(&self, _secp: &Secp256k1<C>) -> bip32::Fingerprint {
168        self.fingerprint()
169    }
170
171    fn can_derive_hardened() -> bool { false }
172}
173
174impl InnerXKey for bip32::Xpriv {
175    fn xkey_fingerprint<C: Signing>(&self, secp: &Secp256k1<C>) -> bip32::Fingerprint {
176        self.fingerprint(secp)
177    }
178
179    fn can_derive_hardened() -> bool { true }
180}
181
182/// Whether a descriptor has a wildcard in it
183#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
184pub enum Wildcard {
185    /// No wildcard
186    None,
187    /// Unhardened wildcard, e.g. *
188    Unhardened,
189    /// Unhardened wildcard, e.g. *h
190    Hardened,
191}
192
193impl SinglePriv {
194    /// Returns the public key of this key.
195    fn to_public<C: Signing>(&self, secp: &Secp256k1<C>) -> SinglePub {
196        let pub_key = self.key.public_key(secp);
197
198        SinglePub { origin: self.origin.clone(), key: SinglePubKey::FullKey(pub_key) }
199    }
200}
201
202impl DescriptorXKey<bip32::Xpriv> {
203    /// Returns the public version of this key, applying all the hardened derivation steps on the
204    /// private key before turning it into a public key.
205    ///
206    /// If the key already has an origin, the derivation steps applied will be appended to the path
207    /// already present, otherwise this key will be treated as a master key and an origin will be
208    /// added with this key's fingerprint and the derivation steps applied.
209    fn to_public<C: Signing>(
210        &self,
211        secp: &Secp256k1<C>,
212    ) -> Result<DescriptorXKey<bip32::Xpub>, DescriptorKeyParseError> {
213        let unhardened = self
214            .derivation_path
215            .into_iter()
216            .rev()
217            .take_while(|c| c.is_normal())
218            .count();
219        let last_hardened_idx = self.derivation_path.len() - unhardened;
220
221        let hardened_path = &self.derivation_path[..last_hardened_idx];
222        let unhardened_path = &self.derivation_path[last_hardened_idx..];
223
224        let xprv = self
225            .xkey
226            .derive_priv(secp, &hardened_path)
227            .map_err(|_| DescriptorKeyParseError("Unable to derive the hardened steps"))?;
228        let xpub = bip32::Xpub::from_priv(secp, &xprv);
229
230        let origin = match &self.origin {
231            Some((fingerprint, path)) => Some((
232                *fingerprint,
233                path.into_iter()
234                    .chain(hardened_path.iter())
235                    .cloned()
236                    .collect(),
237            )),
238            None => {
239                if hardened_path.is_empty() {
240                    None
241                } else {
242                    Some((self.xkey.fingerprint(secp), hardened_path.into()))
243                }
244            }
245        };
246
247        Ok(DescriptorXKey {
248            origin,
249            xkey: xpub,
250            derivation_path: unhardened_path.into(),
251            wildcard: self.wildcard,
252        })
253    }
254}
255
256/// Descriptor Key parsing errors
257// FIXME: replace with error enums
258#[derive(Debug, PartialEq, Clone, Copy)]
259pub struct DescriptorKeyParseError(&'static str);
260
261impl fmt::Display for DescriptorKeyParseError {
262    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(self.0) }
263}
264
265#[cfg(feature = "std")]
266impl error::Error for DescriptorKeyParseError {
267    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
268}
269
270impl fmt::Display for DescriptorPublicKey {
271    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272        match *self {
273            DescriptorPublicKey::Single(ref pk) => {
274                maybe_fmt_master_id(f, &pk.origin)?;
275                match pk.key {
276                    SinglePubKey::FullKey(full_key) => full_key.fmt(f),
277                    SinglePubKey::XOnly(x_only_key) => x_only_key.fmt(f),
278                }?;
279                Ok(())
280            }
281            DescriptorPublicKey::XPub(ref xpub) => {
282                maybe_fmt_master_id(f, &xpub.origin)?;
283                xpub.xkey.fmt(f)?;
284                fmt_derivation_path(f, &xpub.derivation_path)?;
285                match xpub.wildcard {
286                    Wildcard::None => {}
287                    Wildcard::Unhardened => write!(f, "/*")?,
288                    Wildcard::Hardened => write!(f, "/*h")?,
289                }
290                Ok(())
291            }
292            DescriptorPublicKey::MultiXPub(ref xpub) => {
293                maybe_fmt_master_id(f, &xpub.origin)?;
294                xpub.xkey.fmt(f)?;
295                fmt_derivation_paths(f, xpub.derivation_paths.paths())?;
296                match xpub.wildcard {
297                    Wildcard::None => {}
298                    Wildcard::Unhardened => write!(f, "/*")?,
299                    Wildcard::Hardened => write!(f, "/*h")?,
300                }
301                Ok(())
302            }
303        }
304    }
305}
306
307impl DescriptorSecretKey {
308    /// Returns the public version of this key.
309    ///
310    /// If the key is an "XPrv", the hardened derivation steps will be applied
311    /// before converting it to a public key.
312    ///
313    /// It will return an error if the key is a "multi-xpriv", as we wouldn't
314    /// always be able to apply hardened derivation steps if there are multiple
315    /// paths.
316    pub fn to_public<C: Signing>(
317        &self,
318        secp: &Secp256k1<C>,
319    ) -> Result<DescriptorPublicKey, DescriptorKeyParseError> {
320        let pk = match self {
321            DescriptorSecretKey::Single(prv) => DescriptorPublicKey::Single(prv.to_public(secp)),
322            DescriptorSecretKey::XPrv(xprv) => DescriptorPublicKey::XPub(xprv.to_public(secp)?),
323            DescriptorSecretKey::MultiXPrv(_) => {
324                return Err(DescriptorKeyParseError(
325                    "Can't make an extended private key with multiple paths into a public key.",
326                ))
327            }
328        };
329
330        Ok(pk)
331    }
332
333    /// Whether or not this key has multiple derivation paths.
334    pub fn is_multipath(&self) -> bool {
335        match *self {
336            DescriptorSecretKey::Single(..) | DescriptorSecretKey::XPrv(..) => false,
337            DescriptorSecretKey::MultiXPrv(_) => true,
338        }
339    }
340
341    /// Get as many keys as derivation paths in this key.
342    ///
343    /// For raw keys and single-path extended keys it will return the key itself.
344    /// For multipath extended keys it will return a single-path extended key per derivation
345    /// path.
346    pub fn into_single_keys(self) -> Vec<DescriptorSecretKey> {
347        match self {
348            DescriptorSecretKey::Single(..) | DescriptorSecretKey::XPrv(..) => vec![self],
349            DescriptorSecretKey::MultiXPrv(xpub) => {
350                let DescriptorMultiXKey { origin, xkey, derivation_paths, wildcard } = xpub;
351                derivation_paths
352                    .into_paths()
353                    .into_iter()
354                    .map(|derivation_path| {
355                        DescriptorSecretKey::XPrv(DescriptorXKey {
356                            origin: origin.clone(),
357                            xkey,
358                            derivation_path,
359                            wildcard,
360                        })
361                    })
362                    .collect()
363            }
364        }
365    }
366}
367
368/// Writes the fingerprint of the origin, if there is one.
369fn maybe_fmt_master_id(
370    f: &mut fmt::Formatter,
371    origin: &Option<(bip32::Fingerprint, bip32::DerivationPath)>,
372) -> fmt::Result {
373    if let Some((ref master_id, ref master_deriv)) = *origin {
374        fmt::Formatter::write_str(f, "[")?;
375        for byte in master_id.as_bytes().iter() {
376            write!(f, "{:02x}", byte)?;
377        }
378        fmt_derivation_path(f, master_deriv)?;
379        fmt::Formatter::write_str(f, "]")?;
380    }
381
382    Ok(())
383}
384
385/// Writes a derivation path to the formatter, no leading 'm'
386fn fmt_derivation_path(f: &mut fmt::Formatter, path: &bip32::DerivationPath) -> fmt::Result {
387    for child in path {
388        write!(f, "/{}", child)?;
389    }
390    Ok(())
391}
392
393/// Writes multiple derivation paths to the formatter, no leading 'm'.
394/// NOTE: we assume paths only differ at a sindle index, as prescribed by BIP389.
395/// Will panic if the list of paths is empty.
396fn fmt_derivation_paths(f: &mut fmt::Formatter, paths: &[bip32::DerivationPath]) -> fmt::Result {
397    for (i, child) in paths[0].into_iter().enumerate() {
398        if paths.len() > 1 && child != &paths[1][i] {
399            write!(f, "/<")?;
400            for (j, p) in paths.iter().enumerate() {
401                write!(f, "{}", p[i])?;
402                if j != paths.len() - 1 {
403                    write!(f, ";")?;
404                }
405            }
406            write!(f, ">")?;
407        } else {
408            write!(f, "/{}", child)?;
409        }
410    }
411    Ok(())
412}
413
414impl FromStr for DescriptorPublicKey {
415    type Err = DescriptorKeyParseError;
416
417    fn from_str(s: &str) -> Result<Self, Self::Err> {
418        // A "raw" public key without any origin is the least we accept.
419        if s.len() < 64 {
420            return Err(DescriptorKeyParseError(
421                "Key too short (<66 char), doesn't match any format",
422            ));
423        }
424
425        let (key_part, origin) = parse_key_origin(s)?;
426
427        if key_part.contains("pub") {
428            let (xpub, derivation_paths, wildcard) = parse_xkey_deriv::<bip32::Xpub>(key_part)?;
429            if derivation_paths.len() > 1 {
430                Ok(DescriptorPublicKey::MultiXPub(DescriptorMultiXKey {
431                    origin,
432                    xkey: xpub,
433                    derivation_paths: DerivPaths::new(derivation_paths).expect("Not empty"),
434                    wildcard,
435                }))
436            } else {
437                Ok(DescriptorPublicKey::XPub(DescriptorXKey {
438                    origin,
439                    xkey: xpub,
440                    derivation_path: derivation_paths.into_iter().next().unwrap_or_default(),
441                    wildcard,
442                }))
443            }
444        } else {
445            let key = match key_part.len() {
446                64 => {
447                    let x_only_key = XOnlyPublicKey::from_str(key_part).map_err(|_| {
448                        DescriptorKeyParseError("Error while parsing simple xonly key")
449                    })?;
450                    SinglePubKey::XOnly(x_only_key)
451                }
452                66 | 130 => {
453                    if !(&key_part[0..2] == "02"
454                        || &key_part[0..2] == "03"
455                        || &key_part[0..2] == "04")
456                    {
457                        return Err(DescriptorKeyParseError(
458                            "Only publickeys with prefixes 02/03/04 are allowed",
459                        ));
460                    }
461                    let key = bitcoin::PublicKey::from_str(key_part).map_err(|_| {
462                        DescriptorKeyParseError("Error while parsing simple public key")
463                    })?;
464                    SinglePubKey::FullKey(key)
465                }
466                _ => {
467                    return Err(DescriptorKeyParseError(
468                        "Public keys must be 64/66/130 characters in size",
469                    ))
470                }
471            };
472            Ok(DescriptorPublicKey::Single(SinglePub { key, origin }))
473        }
474    }
475}
476
477/// Descriptor key conversion error
478#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
479pub enum ConversionError {
480    /// Attempted to convert a key with hardened derivations to a bitcoin public key
481    HardenedChild,
482    /// Attempted to convert a key with multiple derivation paths to a bitcoin public key
483    MultiKey,
484}
485
486impl fmt::Display for ConversionError {
487    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
488        f.write_str(match *self {
489            ConversionError::HardenedChild => "hardened child step in bip32 path",
490            ConversionError::MultiKey => "multiple existing keys",
491        })
492    }
493}
494
495#[cfg(feature = "std")]
496impl error::Error for ConversionError {
497    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
498        use self::ConversionError::*;
499
500        match self {
501            HardenedChild | MultiKey => None,
502        }
503    }
504}
505
506impl DescriptorPublicKey {
507    /// The fingerprint of the master key associated with this key, `0x00000000` if none.
508    pub fn master_fingerprint(&self) -> bip32::Fingerprint {
509        match *self {
510            DescriptorPublicKey::XPub(ref xpub) => {
511                if let Some((fingerprint, _)) = xpub.origin {
512                    fingerprint
513                } else {
514                    xpub.xkey.fingerprint()
515                }
516            }
517            DescriptorPublicKey::MultiXPub(ref xpub) => {
518                if let Some((fingerprint, _)) = xpub.origin {
519                    fingerprint
520                } else {
521                    xpub.xkey.fingerprint()
522                }
523            }
524            DescriptorPublicKey::Single(ref single) => {
525                if let Some((fingerprint, _)) = single.origin {
526                    fingerprint
527                } else {
528                    let mut engine = XKeyIdentifier::engine();
529                    match single.key {
530                        SinglePubKey::FullKey(pk) => {
531                            pk.write_into(&mut engine).expect("engines don't error")
532                        }
533                        SinglePubKey::XOnly(x_only_pk) => engine.input(&x_only_pk.serialize()),
534                    };
535                    bip32::Fingerprint::from(
536                        &XKeyIdentifier::from_engine(engine)[..4]
537                            .try_into()
538                            .expect("4 byte slice"),
539                    )
540                }
541            }
542        }
543    }
544
545    /// Full path, from the master key
546    ///
547    /// For wildcard keys this will return the path up to the wildcard, so you
548    /// can get full paths by appending one additional derivation step, according
549    /// to the wildcard type (hardened or normal).
550    ///
551    /// For multipath extended keys, this returns `None`.
552    pub fn full_derivation_path(&self) -> Option<bip32::DerivationPath> {
553        match *self {
554            DescriptorPublicKey::XPub(ref xpub) => {
555                let origin_path = if let Some((_, ref path)) = xpub.origin {
556                    path.clone()
557                } else {
558                    bip32::DerivationPath::from(vec![])
559                };
560                Some(origin_path.extend(&xpub.derivation_path))
561            }
562            DescriptorPublicKey::Single(ref single) => {
563                Some(if let Some((_, ref path)) = single.origin {
564                    path.clone()
565                } else {
566                    bip32::DerivationPath::from(vec![])
567                })
568            }
569            DescriptorPublicKey::MultiXPub(_) => None,
570        }
571    }
572
573    /// Returns a vector containing the full derivation paths from the master key.
574    /// The vector will contain just one element for single keys, and multiple elements
575    /// for multipath extended keys.
576    ///
577    /// For wildcard keys this will return the path up to the wildcard, so you
578    /// can get full paths by appending one additional derivation step, according
579    /// to the wildcard type (hardened or normal).
580    pub fn full_derivation_paths(&self) -> Vec<bip32::DerivationPath> {
581        match self {
582            DescriptorPublicKey::MultiXPub(xpub) => {
583                let origin_path = if let Some((_, ref path)) = xpub.origin {
584                    path.clone()
585                } else {
586                    bip32::DerivationPath::from(vec![])
587                };
588                xpub.derivation_paths
589                    .paths()
590                    .iter()
591                    .map(|p| origin_path.extend(p))
592                    .collect()
593            }
594            _ => vec![self
595                .full_derivation_path()
596                .expect("Must be Some for non-multipath keys")],
597        }
598    }
599
600    /// Whether or not the key has a wildcard
601    #[deprecated(note = "use has_wildcard instead")]
602    pub fn is_deriveable(&self) -> bool { self.has_wildcard() }
603
604    /// Whether or not the key has a wildcard
605    pub fn has_wildcard(&self) -> bool {
606        match *self {
607            DescriptorPublicKey::Single(..) => false,
608            DescriptorPublicKey::XPub(ref xpub) => xpub.wildcard != Wildcard::None,
609            DescriptorPublicKey::MultiXPub(ref xpub) => xpub.wildcard != Wildcard::None,
610        }
611    }
612
613    /// Whether or not the key has a wildcard
614    pub fn has_hardened_step(&self) -> bool {
615        let paths = match self {
616            DescriptorPublicKey::Single(..) => &[],
617            DescriptorPublicKey::XPub(xpub) => core::slice::from_ref(&xpub.derivation_path),
618            DescriptorPublicKey::MultiXPub(xpub) => &xpub.derivation_paths.paths()[..],
619        };
620        for p in paths {
621            for step in p.into_iter() {
622                if step.is_hardened() {
623                    return true;
624                }
625            }
626        }
627        false
628    }
629
630    #[deprecated(note = "use at_derivation_index instead")]
631    /// Deprecated name for [`Self::at_derivation_index`].
632    pub fn derive(self, index: u32) -> Result<DefiniteDescriptorKey, ConversionError> {
633        self.at_derivation_index(index)
634    }
635
636    /// Replaces any wildcard (i.e. `/*`) in the key with a particular derivation index, turning it into a
637    /// *definite* key (i.e. one where all the derivation paths are set).
638    ///
639    /// # Returns
640    ///
641    /// - If this key is not an xpub, returns `self`.
642    /// - If this key is an xpub but does not have a wildcard, returns `self`.
643    /// - Otherwise, returns the xpub at derivation `index` (removing the wildcard).
644    ///
645    /// # Errors
646    ///
647    /// - If `index` is hardened.
648    pub fn at_derivation_index(self, index: u32) -> Result<DefiniteDescriptorKey, ConversionError> {
649        let definite = match self {
650            DescriptorPublicKey::Single(_) => self,
651            DescriptorPublicKey::XPub(xpub) => {
652                let derivation_path = match xpub.wildcard {
653                    Wildcard::None => xpub.derivation_path,
654                    Wildcard::Unhardened => xpub.derivation_path.into_child(
655                        bip32::ChildNumber::from_normal_idx(index)
656                            .ok()
657                            .ok_or(ConversionError::HardenedChild)?,
658                    ),
659                    Wildcard::Hardened => xpub.derivation_path.into_child(
660                        bip32::ChildNumber::from_hardened_idx(index)
661                            .ok()
662                            .ok_or(ConversionError::HardenedChild)?,
663                    ),
664                };
665                DescriptorPublicKey::XPub(DescriptorXKey {
666                    origin: xpub.origin,
667                    xkey: xpub.xkey,
668                    derivation_path,
669                    wildcard: Wildcard::None,
670                })
671            }
672            DescriptorPublicKey::MultiXPub(_) => return Err(ConversionError::MultiKey),
673        };
674
675        Ok(DefiniteDescriptorKey::new(definite)
676            .expect("The key should not contain any wildcards at this point"))
677    }
678
679    /// Whether or not this key has multiple derivation paths.
680    pub fn is_multipath(&self) -> bool {
681        match *self {
682            DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => false,
683            DescriptorPublicKey::MultiXPub(_) => true,
684        }
685    }
686
687    /// Get as many keys as derivation paths in this key.
688    ///
689    /// For raw public key and single-path extended keys it will return the key itself.
690    /// For multipath extended keys it will return a single-path extended key per derivation
691    /// path.
692    pub fn into_single_keys(self) -> Vec<DescriptorPublicKey> {
693        match self {
694            DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => vec![self],
695            DescriptorPublicKey::MultiXPub(xpub) => {
696                let DescriptorMultiXKey { origin, xkey, derivation_paths, wildcard } = xpub;
697                derivation_paths
698                    .into_paths()
699                    .into_iter()
700                    .map(|derivation_path| {
701                        DescriptorPublicKey::XPub(DescriptorXKey {
702                            origin: origin.clone(),
703                            xkey,
704                            derivation_path,
705                            wildcard,
706                        })
707                    })
708                    .collect()
709            }
710        }
711    }
712}
713
714impl FromStr for DescriptorSecretKey {
715    type Err = DescriptorKeyParseError;
716
717    fn from_str(s: &str) -> Result<Self, Self::Err> {
718        let (key_part, origin) = parse_key_origin(s)?;
719
720        if key_part.len() <= 52 {
721            let sk = bitcoin::PrivateKey::from_str(key_part)
722                .map_err(|_| DescriptorKeyParseError("Error while parsing a WIF private key"))?;
723            Ok(DescriptorSecretKey::Single(SinglePriv { key: sk, origin: None }))
724        } else {
725            let (xpriv, derivation_paths, wildcard) = parse_xkey_deriv::<bip32::Xpriv>(key_part)?;
726            if derivation_paths.len() > 1 {
727                Ok(DescriptorSecretKey::MultiXPrv(DescriptorMultiXKey {
728                    origin,
729                    xkey: xpriv,
730                    derivation_paths: DerivPaths::new(derivation_paths).expect("Not empty"),
731                    wildcard,
732                }))
733            } else {
734                Ok(DescriptorSecretKey::XPrv(DescriptorXKey {
735                    origin,
736                    xkey: xpriv,
737                    derivation_path: derivation_paths.into_iter().next().unwrap_or_default(),
738                    wildcard,
739                }))
740            }
741        }
742    }
743}
744
745// Parse the origin information part of a descriptor key.
746fn parse_key_origin(s: &str) -> Result<(&str, Option<bip32::KeySource>), DescriptorKeyParseError> {
747    for ch in s.as_bytes() {
748        if *ch < 20 || *ch > 127 {
749            return Err(DescriptorKeyParseError("Encountered an unprintable character"));
750        }
751    }
752
753    if s.is_empty() {
754        return Err(DescriptorKeyParseError("Empty key"));
755    }
756    let mut parts = s[1..].split(']');
757
758    if let Some('[') = s.chars().next() {
759        let mut raw_origin = parts
760            .next()
761            .ok_or(DescriptorKeyParseError("Unclosed '['"))?
762            .split('/');
763
764        let origin_id_hex = raw_origin
765            .next()
766            .ok_or(DescriptorKeyParseError("No master fingerprint found after '['"))?;
767
768        if origin_id_hex.len() != 8 {
769            return Err(DescriptorKeyParseError("Master fingerprint should be 8 characters long"));
770        }
771        let parent_fingerprint = bip32::Fingerprint::from_hex(origin_id_hex).map_err(|_| {
772            DescriptorKeyParseError("Malformed master fingerprint, expected 8 hex chars")
773        })?;
774        let origin_path = raw_origin
775            .map(bip32::ChildNumber::from_str)
776            .collect::<Result<bip32::DerivationPath, bip32::Error>>()
777            .map_err(|_| DescriptorKeyParseError("Error while parsing master derivation path"))?;
778
779        let key = parts
780            .next()
781            .ok_or(DescriptorKeyParseError("No key after origin."))?;
782
783        if parts.next().is_some() {
784            Err(DescriptorKeyParseError("Multiple ']' in Descriptor Public Key"))
785        } else {
786            Ok((key, Some((parent_fingerprint, origin_path))))
787        }
788    } else {
789        Ok((s, None))
790    }
791}
792
793/// Parse an extended key concatenated to a derivation path.
794fn parse_xkey_deriv<K: InnerXKey>(
795    key_deriv: &str,
796) -> Result<(K, Vec<bip32::DerivationPath>, Wildcard), DescriptorKeyParseError> {
797    let mut key_deriv = key_deriv.split('/');
798    let xkey_str = key_deriv
799        .next()
800        .ok_or(DescriptorKeyParseError("No key found after origin description"))?;
801    let xkey =
802        K::from_str(xkey_str).map_err(|_| DescriptorKeyParseError("Error while parsing xkey."))?;
803
804    let mut wildcard = Wildcard::None;
805    let mut multipath = false;
806    let derivation_paths = key_deriv
807        .filter_map(|p| {
808            if wildcard == Wildcard::None && p == "*" {
809                wildcard = Wildcard::Unhardened;
810                None
811            } else if wildcard == Wildcard::None && (p == "*'" || p == "*h") {
812                wildcard = Wildcard::Hardened;
813                None
814            } else if wildcard != Wildcard::None {
815                Some(Err(DescriptorKeyParseError(
816                    "'*' may only appear as last element in a derivation path.",
817                )))
818            } else {
819                // BIP389 defines a new step in the derivation path. This step contains two or more
820                // derivation indexes in the form '<1;2;3';4h;5H;6>'.
821                if p.starts_with('<') && p.ends_with('>') {
822                    // There may only be one occurence of this step.
823                    if multipath {
824                        return Some(Err(DescriptorKeyParseError(
825                            "'<' may only appear once in a derivation path.",
826                        )));
827                    }
828                    multipath = true;
829
830                    // The step must contain at least two derivation indexes.
831                    // So it's at least '<' + a number + ';' + a number + '>'.
832                    if p.len() < 5 || !p.contains(';') {
833                        return Some(Err(DescriptorKeyParseError(
834                            "Invalid multi index step in multipath descriptor.",
835                        )));
836                    }
837
838                    // Collect all derivation indexes at this step.
839                    let indexes = p[1..p.len() - 1].split(';');
840                    Some(
841                        indexes
842                            .into_iter()
843                            .map(|s| {
844                                bip32::ChildNumber::from_str(s).map_err(|_| {
845                                    DescriptorKeyParseError(
846                                        "Error while parsing index in key derivation path.",
847                                    )
848                                })
849                            })
850                            .collect::<Result<Vec<bip32::ChildNumber>, _>>(),
851                    )
852                } else {
853                    // Not a BIP389 step, just a regular derivation index.
854                    Some(
855                        bip32::ChildNumber::from_str(p)
856                            .map(|i| vec![i])
857                            .map_err(|_| {
858                                DescriptorKeyParseError("Error while parsing key derivation path")
859                            }),
860                    )
861                }
862            }
863        })
864        // Now we've got all derivation indexes in a list of vectors of indexes. If the derivation
865        // path was empty then this list is empty. If the derivation path didn't contain any BIP389
866        // step all the vectors of indexes contain a single element. If it did though, one of the
867        // vectors contains more than one element.
868        // Now transform this list of vectors of steps into distinct derivation paths.
869        .try_fold(Vec::new(), |mut paths, index_list| {
870            let mut index_list = index_list?.into_iter();
871            let first_index = index_list
872                .next()
873                .expect("There is always at least one element");
874
875            if paths.is_empty() {
876                paths.push(vec![first_index]);
877            } else {
878                for path in paths.iter_mut() {
879                    path.push(first_index);
880                }
881            }
882
883            // If the step is a BIP389 one, create as many paths as there is indexes.
884            for (i, index) in index_list.enumerate() {
885                paths.push(paths[0].clone());
886                *paths[i + 1].last_mut().expect("Never empty") = index;
887            }
888
889            Ok(paths)
890        })?
891        .into_iter()
892        .map(|index_list| index_list.into_iter().collect::<bip32::DerivationPath>())
893        .collect::<Vec<bip32::DerivationPath>>();
894
895    Ok((xkey, derivation_paths, wildcard))
896}
897
898impl<K: InnerXKey> DescriptorXKey<K> {
899    /// Compares this key with a `keysource` and returns the matching derivation path, if any.
900    ///
901    /// For keys that have an origin, the `keysource`'s fingerprint will be compared
902    /// with the origin's fingerprint, and the `keysource`'s path will be compared with the concatenation of the
903    /// origin's and key's paths.
904    ///
905    /// If the key `wildcard`, the last item of the `keysource`'s path will be ignored,
906    ///
907    /// ## Examples
908    ///
909    /// ```
910    /// # use std::str::FromStr;
911    /// # fn body() -> Result<(), ()> {
912    /// use miniscript::bitcoin::bip32;
913    /// use miniscript::descriptor::DescriptorPublicKey;
914    ///
915    /// let ctx = miniscript::bitcoin::secp256k1::Secp256k1::signing_only();
916    ///
917    /// let key = DescriptorPublicKey::from_str("[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*").or(Err(()))?;
918    /// let xpub = match key {
919    ///     DescriptorPublicKey::XPub(xpub) => xpub,
920    ///     _ => panic!("Parsing Error"),
921    /// };
922    ///
923    /// assert_eq!(
924    ///     xpub.matches(&(
925    ///         bip32::Fingerprint::from_str("d34db33f").or(Err(()))?,
926    ///         bip32::DerivationPath::from_str("m/44'/0'/0'/1/42").or(Err(()))?
927    ///     ), &ctx),
928    ///     Some(bip32::DerivationPath::from_str("m/44'/0'/0'/1").or(Err(()))?)
929    /// );
930    /// assert_eq!(
931    ///     xpub.matches(&(
932    ///         bip32::Fingerprint::from_str("ffffffff").or(Err(()))?,
933    ///         bip32::DerivationPath::from_str("m/44'/0'/0'/1/42").or(Err(()))?
934    ///     ), &ctx),
935    ///     None
936    /// );
937    /// assert_eq!(
938    ///     xpub.matches(&(
939    ///         bip32::Fingerprint::from_str("d34db33f").or(Err(()))?,
940    ///         bip32::DerivationPath::from_str("m/44'/0'/0'/100/0").or(Err(()))?
941    ///     ), &ctx),
942    ///     None
943    /// );
944    /// # Ok(())
945    /// # }
946    /// # body().unwrap()
947    /// ```
948    pub fn matches<C: Signing>(
949        &self,
950        keysource: &bip32::KeySource,
951        secp: &Secp256k1<C>,
952    ) -> Option<bip32::DerivationPath> {
953        let (fingerprint, path) = keysource;
954
955        let (compare_fingerprint, compare_path) = match self.origin {
956            Some((fingerprint, ref path)) => {
957                (fingerprint, path.into_iter().chain(&self.derivation_path).collect())
958            }
959            None => (
960                self.xkey.xkey_fingerprint(secp),
961                self.derivation_path.into_iter().collect::<Vec<_>>(),
962            ),
963        };
964
965        let path_excluding_wildcard = if self.wildcard != Wildcard::None && !path.is_empty() {
966            path.into_iter()
967                .take(path.as_ref().len() - 1)
968                .cloned()
969                .collect()
970        } else {
971            path.clone()
972        };
973
974        if &compare_fingerprint == fingerprint
975            && compare_path
976                .into_iter()
977                .eq(path_excluding_wildcard.into_iter())
978        {
979            Some(path_excluding_wildcard)
980        } else {
981            None
982        }
983    }
984}
985
986impl MiniscriptKey for DescriptorPublicKey {
987    type Sha256 = sha256::Hash;
988    type Hash256 = hash256::Hash;
989    type Ripemd160 = ripemd160::Hash;
990    type Hash160 = hash160::Hash;
991
992    fn is_uncompressed(&self) -> bool {
993        match self {
994            DescriptorPublicKey::Single(SinglePub {
995                key: SinglePubKey::FullKey(ref key), ..
996            }) => key.is_uncompressed(),
997            _ => false,
998        }
999    }
1000
1001    fn is_x_only_key(&self) -> bool {
1002        matches!(
1003            self,
1004            DescriptorPublicKey::Single(SinglePub { key: SinglePubKey::XOnly(ref _key), .. })
1005        )
1006    }
1007
1008    fn num_der_paths(&self) -> usize {
1009        match self {
1010            DescriptorPublicKey::Single(_) => 0,
1011            DescriptorPublicKey::XPub(_) => 1,
1012            DescriptorPublicKey::MultiXPub(xpub) => xpub.derivation_paths.paths().len(),
1013        }
1014    }
1015}
1016
1017impl DefiniteDescriptorKey {
1018    /// Computes the public key corresponding to this descriptor key.
1019    /// When deriving from an XOnlyPublicKey, it adds the default 0x02 y-coordinate
1020    /// and returns the obtained full [`bitcoin::PublicKey`]. All BIP32 derivations
1021    /// always return a compressed key
1022    ///
1023    /// Will return an error if the descriptor key has any hardened derivation steps in its path. To
1024    /// avoid this error you should replace any such public keys first with [`translate_pk`].
1025    ///
1026    /// [`translate_pk`]: crate::TranslatePk::translate_pk
1027    pub fn derive_public_key<C: Verification>(
1028        &self,
1029        secp: &Secp256k1<C>,
1030    ) -> Result<bitcoin::PublicKey, ConversionError> {
1031        match self.0 {
1032            DescriptorPublicKey::Single(ref pk) => match pk.key {
1033                SinglePubKey::FullKey(pk) => Ok(pk),
1034                SinglePubKey::XOnly(xpk) => Ok(xpk.to_public_key()),
1035            },
1036            DescriptorPublicKey::XPub(ref xpk) => match xpk.wildcard {
1037                Wildcard::Unhardened | Wildcard::Hardened => {
1038                    unreachable!("we've excluded this error case")
1039                }
1040                Wildcard::None => match xpk.xkey.derive_pub(secp, &xpk.derivation_path.as_ref()) {
1041                    Ok(xpub) => Ok(bitcoin::PublicKey::new(xpub.public_key)),
1042                    Err(bip32::Error::CannotDeriveFromHardenedKey) => {
1043                        Err(ConversionError::HardenedChild)
1044                    }
1045                    Err(e) => unreachable!("cryptographically unreachable: {}", e),
1046                },
1047            },
1048            DescriptorPublicKey::MultiXPub(_) => {
1049                unreachable!("A definite key cannot contain a multipath key.")
1050            }
1051        }
1052    }
1053
1054    /// Construct an instance from a descriptor key and a derivation index
1055    ///
1056    /// Returns `None` if the key contains a wildcard
1057    fn new(key: DescriptorPublicKey) -> Option<Self> {
1058        if key.has_wildcard() || key.is_multipath() || key.has_hardened_step() {
1059            None
1060        } else {
1061            Some(Self(key))
1062        }
1063    }
1064
1065    /// The fingerprint of the master key associated with this key, `0x00000000` if none.
1066    pub fn master_fingerprint(&self) -> bip32::Fingerprint { self.0.master_fingerprint() }
1067
1068    /// Full path from the master key if not a multipath extended key.
1069    pub fn full_derivation_path(&self) -> Option<bip32::DerivationPath> {
1070        self.0.full_derivation_path()
1071    }
1072
1073    /// Full paths from the master key. The vector will contain just one path for single
1074    /// keys, and multiple ones for multipath extended keys
1075    pub fn full_derivation_paths(&self) -> Vec<bip32::DerivationPath> {
1076        self.0.full_derivation_paths()
1077    }
1078
1079    /// Reference to the underlying `DescriptorPublicKey`
1080    pub fn as_descriptor_public_key(&self) -> &DescriptorPublicKey { &self.0 }
1081
1082    /// Converts the definite key into a generic one
1083    pub fn into_descriptor_public_key(self) -> DescriptorPublicKey { self.0 }
1084}
1085
1086impl FromStr for DefiniteDescriptorKey {
1087    type Err = DescriptorKeyParseError;
1088
1089    fn from_str(s: &str) -> Result<Self, Self::Err> {
1090        let inner = DescriptorPublicKey::from_str(s)?;
1091        DefiniteDescriptorKey::new(inner).ok_or(DescriptorKeyParseError(
1092            "cannot parse multi-path keys, keys with a wildcard or keys with hardened derivation steps as a DerivedDescriptorKey",
1093        ))
1094    }
1095}
1096
1097impl fmt::Display for DefiniteDescriptorKey {
1098    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) }
1099}
1100
1101impl MiniscriptKey for DefiniteDescriptorKey {
1102    type Sha256 = sha256::Hash;
1103    type Hash256 = hash256::Hash;
1104    type Ripemd160 = ripemd160::Hash;
1105    type Hash160 = hash160::Hash;
1106
1107    fn is_uncompressed(&self) -> bool { self.0.is_uncompressed() }
1108
1109    fn is_x_only_key(&self) -> bool { self.0.is_x_only_key() }
1110
1111    fn num_der_paths(&self) -> usize { self.0.num_der_paths() }
1112}
1113
1114impl ToPublicKey for DefiniteDescriptorKey {
1115    fn to_public_key(&self) -> bitcoin::PublicKey {
1116        let secp = Secp256k1::verification_only();
1117        self.derive_public_key(&secp).unwrap()
1118    }
1119
1120    fn to_sha256(hash: &sha256::Hash) -> sha256::Hash { *hash }
1121
1122    fn to_hash256(hash: &hash256::Hash) -> hash256::Hash { *hash }
1123
1124    fn to_ripemd160(hash: &ripemd160::Hash) -> ripemd160::Hash { *hash }
1125
1126    fn to_hash160(hash: &hash160::Hash) -> hash160::Hash { *hash }
1127}
1128
1129impl From<DefiniteDescriptorKey> for DescriptorPublicKey {
1130    fn from(d: DefiniteDescriptorKey) -> Self { d.0 }
1131}
1132
1133impl Borrow<DescriptorPublicKey> for DefiniteDescriptorKey {
1134    fn borrow(&self) -> &DescriptorPublicKey { &self.0 }
1135}
1136
1137#[cfg(feature = "serde")]
1138impl<'de> Deserialize<'de> for DescriptorPublicKey {
1139    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1140    where
1141        D: Deserializer<'de>,
1142    {
1143        let s = String::deserialize(deserializer)?;
1144        DescriptorPublicKey::from_str(&s).map_err(crate::serde::de::Error::custom)
1145    }
1146}
1147
1148#[cfg(feature = "serde")]
1149impl Serialize for DescriptorPublicKey {
1150    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1151    where
1152        S: Serializer,
1153    {
1154        serializer.serialize_str(&self.to_string())
1155    }
1156}
1157
1158#[cfg(test)]
1159mod test {
1160    use core::str::FromStr;
1161
1162    use bitcoin::{bip32, secp256k1};
1163    #[cfg(feature = "serde")]
1164    use serde_test::{assert_tokens, Token};
1165
1166    use super::{
1167        DescriptorKeyParseError, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey,
1168        MiniscriptKey, Wildcard,
1169    };
1170    use crate::prelude::*;
1171    use crate::DefiniteDescriptorKey;
1172
1173    #[test]
1174    fn parse_descriptor_key_errors() {
1175        // And ones with misplaced wildcard
1176        let desc = "[78412e3a/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*/44";
1177        assert_eq!(
1178            DescriptorPublicKey::from_str(desc),
1179            Err(DescriptorKeyParseError(
1180                "\'*\' may only appear as last element in a derivation path."
1181            ))
1182        );
1183
1184        // And ones with invalid fingerprints
1185        let desc = "[NonHexor]xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*";
1186        assert_eq!(
1187            DescriptorPublicKey::from_str(desc),
1188            Err(DescriptorKeyParseError("Malformed master fingerprint, expected 8 hex chars"))
1189        );
1190
1191        // And ones with invalid xpubs..
1192        let desc = "[78412e3a]xpub1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaLcgJvLJuZZvRcEL/1/*";
1193        assert_eq!(
1194            DescriptorPublicKey::from_str(desc),
1195            Err(DescriptorKeyParseError("Error while parsing xkey."))
1196        );
1197
1198        // ..or invalid raw keys
1199        let desc = "[78412e3a]0208a117f3897c3a13c9384b8695eed98dc31bc2500feb19a1af424cd47a5d83/1/*";
1200        assert_eq!(
1201            DescriptorPublicKey::from_str(desc),
1202            Err(DescriptorKeyParseError("Public keys must be 64/66/130 characters in size"))
1203        );
1204
1205        // ..or invalid separators
1206        let desc = "[78412e3a]]03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8";
1207        assert_eq!(
1208            DescriptorPublicKey::from_str(desc),
1209            Err(DescriptorKeyParseError("Multiple \']\' in Descriptor Public Key"))
1210        );
1211
1212        // fuzzer errors
1213        let desc = "[11111f11]033333333333333333333333333333323333333333333333333333333433333333]]333]]3]]101333333333333433333]]]10]333333mmmm";
1214        assert_eq!(
1215            DescriptorPublicKey::from_str(desc),
1216            Err(DescriptorKeyParseError("Multiple \']\' in Descriptor Public Key"))
1217        );
1218
1219        // fuzz failure, hybrid keys
1220        let desc = "0777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777";
1221        assert_eq!(
1222            DescriptorPublicKey::from_str(desc),
1223            Err(DescriptorKeyParseError("Only publickeys with prefixes 02/03/04 are allowed"))
1224        );
1225    }
1226
1227    #[test]
1228    fn parse_descriptor_secret_key_error() {
1229        // Xpubs are invalid
1230        let secret_key = "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL";
1231        assert_eq!(
1232            DescriptorSecretKey::from_str(secret_key),
1233            Err(DescriptorKeyParseError("Error while parsing xkey."))
1234        );
1235
1236        // And ones with invalid fingerprints
1237        let desc = "[NonHexor]tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/*";
1238        assert_eq!(
1239            DescriptorSecretKey::from_str(desc),
1240            Err(DescriptorKeyParseError("Malformed master fingerprint, expected 8 hex chars"))
1241        );
1242
1243        // ..or invalid raw keys
1244        let desc = "[78412e3a]L32jTfVLei6BYTPUpwpJSkrHx8iL9GZzeErVS8y4Y/1/*";
1245        assert_eq!(
1246            DescriptorSecretKey::from_str(desc),
1247            Err(DescriptorKeyParseError("Error while parsing a WIF private key"))
1248        );
1249    }
1250
1251    #[test]
1252    fn test_wildcard() {
1253        let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2").unwrap();
1254        assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00");
1255        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'/2");
1256        assert!(!public_key.has_wildcard());
1257
1258        let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/*").unwrap();
1259        assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00");
1260        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'");
1261        assert!(public_key.has_wildcard());
1262
1263        let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/*h").unwrap();
1264        assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00");
1265        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'");
1266        assert!(public_key.has_wildcard());
1267    }
1268
1269    #[test]
1270    fn test_deriv_on_xprv() {
1271        let secp = secp256k1::Secp256k1::signing_only();
1272
1273        let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2").unwrap();
1274        let public_key = secret_key.to_public(&secp).unwrap();
1275        assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2");
1276        assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
1277        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'/2");
1278        assert!(!public_key.has_wildcard());
1279
1280        let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2'").unwrap();
1281        let public_key = secret_key.to_public(&secp).unwrap();
1282        assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1'/2']tpubDDPuH46rv4dbFtmF6FrEtJEy1CvLZonyBoVxF6xsesHdYDdTBrq2mHhm8AbsPh39sUwL2nZyxd6vo4uWNTU9v4t893CwxjqPnwMoUACLvMV");
1283        assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
1284        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'/2'");
1285
1286        let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0/1/2").unwrap();
1287        let public_key = secret_key.to_public(&secp).unwrap();
1288        assert_eq!(public_key.to_string(), "tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr/0/1/2");
1289        assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
1290        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0/1/2");
1291
1292        let secret_key = DescriptorSecretKey::from_str("[aabbccdd]tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0/1/2").unwrap();
1293        let public_key = secret_key.to_public(&secp).unwrap();
1294        assert_eq!(public_key.to_string(), "[aabbccdd]tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr/0/1/2");
1295        assert_eq!(public_key.master_fingerprint().to_string(), "aabbccdd");
1296        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0/1/2");
1297
1298        let secret_key = DescriptorSecretKey::from_str("[aabbccdd/90']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2").unwrap();
1299        let public_key = secret_key.to_public(&secp).unwrap();
1300        assert_eq!(public_key.to_string(), "[aabbccdd/90'/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2");
1301        assert_eq!(public_key.master_fingerprint().to_string(), "aabbccdd");
1302        assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/90'/0'/1'/2");
1303    }
1304
1305    #[test]
1306    fn test_master_fingerprint() {
1307        assert_eq!(
1308            DescriptorPublicKey::from_str(
1309                "02a489e0ea42b56148d212d325b7c67c6460483ff931c303ea311edfef667c8f35",
1310            )
1311            .unwrap()
1312            .master_fingerprint()
1313            .as_bytes(),
1314            b"\xb0\x59\x11\x6a"
1315        );
1316    }
1317
1318    fn get_multipath_xpub(key_str: &str, num_paths: usize) -> DescriptorMultiXKey<bip32::Xpub> {
1319        let desc_key = DescriptorPublicKey::from_str(key_str).unwrap();
1320        assert_eq!(desc_key.num_der_paths(), num_paths);
1321        match desc_key {
1322            DescriptorPublicKey::MultiXPub(xpub) => xpub,
1323            _ => unreachable!(),
1324        }
1325    }
1326
1327    fn get_multipath_xprv(key_str: &str) -> DescriptorMultiXKey<bip32::Xpriv> {
1328        let desc_key = DescriptorSecretKey::from_str(key_str).unwrap();
1329        match desc_key {
1330            DescriptorSecretKey::MultiXPrv(xprv) => xprv,
1331            _ => unreachable!(),
1332        }
1333    }
1334
1335    #[test]
1336    fn multipath_extended_keys() {
1337        let secp = secp256k1::Secp256k1::signing_only();
1338
1339        // We can have a key in a descriptor that has multiple paths
1340        let xpub = get_multipath_xpub("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/<0;1;42;9854>", 4);
1341        assert_eq!(
1342            xpub.derivation_paths.paths(),
1343            &vec![
1344                bip32::DerivationPath::from_str("m/2/0").unwrap(),
1345                bip32::DerivationPath::from_str("m/2/1").unwrap(),
1346                bip32::DerivationPath::from_str("m/2/42").unwrap(),
1347                bip32::DerivationPath::from_str("m/2/9854").unwrap()
1348            ],
1349        );
1350        assert_eq!(
1351            xpub,
1352            get_multipath_xpub(&DescriptorPublicKey::MultiXPub(xpub.clone()).to_string(), 4)
1353        );
1354        // Even if it's in the middle of the derivation path.
1355        let xpub = get_multipath_xpub("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/<0;1;9854>/0/5/10", 3);
1356        assert_eq!(
1357            xpub.derivation_paths.paths(),
1358            &vec![
1359                bip32::DerivationPath::from_str("m/2/0/0/5/10").unwrap(),
1360                bip32::DerivationPath::from_str("m/2/1/0/5/10").unwrap(),
1361                bip32::DerivationPath::from_str("m/2/9854/0/5/10").unwrap()
1362            ],
1363        );
1364        assert_eq!(
1365            xpub,
1366            get_multipath_xpub(&DescriptorPublicKey::MultiXPub(xpub.clone()).to_string(), 3)
1367        );
1368        // Even if it is a wildcard extended key.
1369        let xpub = get_multipath_xpub("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/<0;1;9854>/3456/9876/*", 3);
1370        assert_eq!(xpub.wildcard, Wildcard::Unhardened);
1371        assert_eq!(
1372            xpub.derivation_paths.paths(),
1373            &vec![
1374                bip32::DerivationPath::from_str("m/2/0/3456/9876").unwrap(),
1375                bip32::DerivationPath::from_str("m/2/1/3456/9876").unwrap(),
1376                bip32::DerivationPath::from_str("m/2/9854/3456/9876").unwrap()
1377            ],
1378        );
1379        assert_eq!(
1380            xpub,
1381            get_multipath_xpub(&DescriptorPublicKey::MultiXPub(xpub.clone()).to_string(), 3)
1382        );
1383        // Also even if it has an origin.
1384        let xpub = get_multipath_xpub("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/<0;1>/*", 2);
1385        assert_eq!(xpub.wildcard, Wildcard::Unhardened);
1386        assert_eq!(
1387            xpub.derivation_paths.paths(),
1388            &vec![
1389                bip32::DerivationPath::from_str("m/0").unwrap(),
1390                bip32::DerivationPath::from_str("m/1").unwrap(),
1391            ],
1392        );
1393        assert_eq!(
1394            xpub,
1395            get_multipath_xpub(&DescriptorPublicKey::MultiXPub(xpub.clone()).to_string(), 2)
1396        );
1397        // Also if it has hardened steps in the derivation path. In fact, it can also have hardened
1398        // indexes even at the step with multiple indexes!
1399        let xpub = get_multipath_xpub("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/9478'/<0';1h>/8h/*'", 2);
1400        assert_eq!(xpub.wildcard, Wildcard::Hardened);
1401        assert_eq!(
1402            xpub.derivation_paths.paths(),
1403            &vec![
1404                bip32::DerivationPath::from_str("m/9478'/0'/8'").unwrap(),
1405                bip32::DerivationPath::from_str("m/9478h/1h/8h").unwrap(),
1406            ],
1407        );
1408        assert_eq!(
1409            xpub,
1410            get_multipath_xpub(&DescriptorPublicKey::MultiXPub(xpub.clone()).to_string(), 2)
1411        );
1412        // You can't get the "full derivation path" for a multipath extended public key.
1413        let desc_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/9478'/<0';1>/8h/*'").unwrap();
1414        assert!(desc_key.full_derivation_path().is_none());
1415        assert!(desc_key.is_multipath());
1416        // But you can get all the derivation paths
1417        assert_eq!(
1418            desc_key.full_derivation_paths(),
1419            vec![
1420                bip32::DerivationPath::from_str("m/0'/1'/9478'/0'/8'").unwrap(),
1421                bip32::DerivationPath::from_str("m/0'/1'/9478'/1/8'").unwrap(),
1422            ],
1423        );
1424        assert_eq!(desc_key.into_single_keys(), vec![DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/9478'/0'/8h/*'").unwrap(), DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/9478'/1/8h/*'").unwrap()]);
1425
1426        // All the same but with extended private keys instead of xpubs.
1427        let xprv = get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/2/<0;1;42;9854>");
1428        assert_eq!(
1429            xprv.derivation_paths.paths(),
1430            &vec![
1431                bip32::DerivationPath::from_str("m/2/0").unwrap(),
1432                bip32::DerivationPath::from_str("m/2/1").unwrap(),
1433                bip32::DerivationPath::from_str("m/2/42").unwrap(),
1434                bip32::DerivationPath::from_str("m/2/9854").unwrap()
1435            ],
1436        );
1437        assert_eq!(
1438            xprv,
1439            get_multipath_xprv(&DescriptorSecretKey::MultiXPrv(xprv.clone()).to_string())
1440        );
1441        let xprv = get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/2/<0;1;9854>/0/5/10");
1442        assert_eq!(
1443            xprv.derivation_paths.paths(),
1444            &vec![
1445                bip32::DerivationPath::from_str("m/2/0/0/5/10").unwrap(),
1446                bip32::DerivationPath::from_str("m/2/1/0/5/10").unwrap(),
1447                bip32::DerivationPath::from_str("m/2/9854/0/5/10").unwrap()
1448            ],
1449        );
1450        assert_eq!(
1451            xprv,
1452            get_multipath_xprv(&DescriptorSecretKey::MultiXPrv(xprv.clone()).to_string())
1453        );
1454        let xprv = get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/2/<0;1;9854>/3456/9876/*");
1455        assert_eq!(xprv.wildcard, Wildcard::Unhardened);
1456        assert_eq!(
1457            xprv.derivation_paths.paths(),
1458            &vec![
1459                bip32::DerivationPath::from_str("m/2/0/3456/9876").unwrap(),
1460                bip32::DerivationPath::from_str("m/2/1/3456/9876").unwrap(),
1461                bip32::DerivationPath::from_str("m/2/9854/3456/9876").unwrap()
1462            ],
1463        );
1464        assert_eq!(
1465            xprv,
1466            get_multipath_xprv(&DescriptorSecretKey::MultiXPrv(xprv.clone()).to_string())
1467        );
1468        let xprv = get_multipath_xprv("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/<0;1>/*");
1469        assert_eq!(xprv.wildcard, Wildcard::Unhardened);
1470        assert_eq!(
1471            xprv.derivation_paths.paths(),
1472            &vec![
1473                bip32::DerivationPath::from_str("m/0").unwrap(),
1474                bip32::DerivationPath::from_str("m/1").unwrap(),
1475            ],
1476        );
1477        assert_eq!(
1478            xprv,
1479            get_multipath_xprv(&DescriptorSecretKey::MultiXPrv(xprv.clone()).to_string())
1480        );
1481        let xprv = get_multipath_xprv("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/9478'/<0';1h>/8h/*'");
1482        assert_eq!(xprv.wildcard, Wildcard::Hardened);
1483        assert_eq!(
1484            xprv.derivation_paths.paths(),
1485            &vec![
1486                bip32::DerivationPath::from_str("m/9478'/0'/8'").unwrap(),
1487                bip32::DerivationPath::from_str("m/9478h/1h/8h").unwrap(),
1488            ],
1489        );
1490        assert_eq!(
1491            xprv,
1492            get_multipath_xprv(&DescriptorSecretKey::MultiXPrv(xprv.clone()).to_string())
1493        );
1494        let desc_key = DescriptorSecretKey::from_str("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/9478'/<0';1>/8h/*'").unwrap();
1495        assert!(desc_key.to_public(&secp).is_err());
1496        assert!(desc_key.is_multipath());
1497        assert_eq!(desc_key.into_single_keys(), vec![DescriptorSecretKey::from_str("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/9478'/0'/8h/*'").unwrap(), DescriptorSecretKey::from_str("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/9478'/1/8h/*'").unwrap()]);
1498
1499        // It's invalid to:
1500        // - Not have opening or closing brackets
1501        // - Have multiple steps with different indexes
1502        // - Only have one index within the brackets
1503        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/<0;1;42;9854").unwrap_err();
1504        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/0;1;42;9854>").unwrap_err();
1505        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<0;1>/96/<0;1>").unwrap_err();
1506        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<0>").unwrap_err();
1507        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<0;>").unwrap_err();
1508        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<;1>").unwrap_err();
1509        DescriptorPublicKey::from_str("tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2/4/<0;1;>").unwrap_err();
1510    }
1511
1512    #[test]
1513    #[cfg(feature = "serde")]
1514    fn test_descriptor_public_key_serde() {
1515        let desc = "[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2";
1516        let public_key = DescriptorPublicKey::from_str(desc).unwrap();
1517        assert_tokens(&public_key, &[Token::String(desc)]);
1518    }
1519
1520    #[test]
1521    fn definite_keys() {
1522        // basic xpub
1523        let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
1524            .parse::<DescriptorPublicKey>()
1525            .unwrap();
1526        assert!(DefiniteDescriptorKey::new(desc).is_some());
1527        // xpub with wildcard
1528        let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/*"
1529            .parse::<DescriptorPublicKey>()
1530            .unwrap();
1531        assert!(DefiniteDescriptorKey::new(desc).is_none());
1532        // multipath xpub
1533        let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/<0;1>"
1534            .parse::<DescriptorPublicKey>()
1535            .unwrap();
1536        assert!(DefiniteDescriptorKey::new(desc).is_none());
1537        // xpub with hardened path
1538        let desc = "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/1'/2"
1539            .parse::<DescriptorPublicKey>()
1540            .unwrap();
1541        assert!(DefiniteDescriptorKey::new(desc).is_none());
1542    }
1543}