Skip to content

Commit cf401b6

Browse files
committed
desc keys: add a method to get single-path keys from multipath keys
1 parent b5eb317 commit cf401b6

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

src/descriptor/key.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ impl DerivPaths {
8383
pub fn paths(&self) -> &Vec<bip32::DerivationPath> {
8484
&self.0
8585
}
86+
87+
/// Get the list of derivation paths.
88+
pub fn into_paths(self) -> Vec<bip32::DerivationPath> {
89+
self.0
90+
}
8691
}
8792

8893
/// Instance of one or more extended keys, as specified in BIP 389.
@@ -334,6 +339,45 @@ impl DescriptorSecretKey {
334339

335340
Ok(pk)
336341
}
342+
343+
/// Whether or not this key has multiple derivation paths.
344+
pub fn is_multipath(&self) -> bool {
345+
match *self {
346+
DescriptorSecretKey::Single(..) | DescriptorSecretKey::XPrv(..) => false,
347+
DescriptorSecretKey::MultiXPrv(_) => true,
348+
}
349+
}
350+
351+
/// Get as many keys as derivation paths in this key.
352+
///
353+
/// For raw keys and single-path extended keys it will return the key itself.
354+
/// For multipath extended keys it will return a single-path extended key per derivation
355+
/// path.
356+
pub fn into_single_keys(self) -> Vec<DescriptorSecretKey> {
357+
match self {
358+
DescriptorSecretKey::Single(..) | DescriptorSecretKey::XPrv(..) => vec![self],
359+
DescriptorSecretKey::MultiXPrv(xpub) => {
360+
let DescriptorMultiXKey {
361+
origin,
362+
xkey,
363+
derivation_paths,
364+
wildcard,
365+
} = xpub;
366+
derivation_paths
367+
.into_paths()
368+
.into_iter()
369+
.map(|derivation_path| {
370+
DescriptorSecretKey::XPrv(DescriptorXKey {
371+
origin: origin.clone(),
372+
xkey,
373+
derivation_path,
374+
wildcard,
375+
})
376+
})
377+
.collect()
378+
}
379+
}
380+
}
337381
}
338382

339383
/// Writes the fingerprint of the origin, if there is one.
@@ -604,6 +648,45 @@ impl DescriptorPublicKey {
604648
Ok(DefiniteDescriptorKey::new(definite)
605649
.expect("The key should not contain any wildcards at this point"))
606650
}
651+
652+
/// Whether or not this key has multiple derivation paths.
653+
pub fn is_multipath(&self) -> bool {
654+
match *self {
655+
DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => false,
656+
DescriptorPublicKey::MultiXPub(_) => true,
657+
}
658+
}
659+
660+
/// Get as many keys as derivation paths in this key.
661+
///
662+
/// For raw public key and single-path extended keys it will return the key itself.
663+
/// For multipath extended keys it will return a single-path extended key per derivation
664+
/// path.
665+
pub fn into_single_keys(self) -> Vec<DescriptorPublicKey> {
666+
match self {
667+
DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => vec![self],
668+
DescriptorPublicKey::MultiXPub(xpub) => {
669+
let DescriptorMultiXKey {
670+
origin,
671+
xkey,
672+
derivation_paths,
673+
wildcard,
674+
} = xpub;
675+
derivation_paths
676+
.into_paths()
677+
.into_iter()
678+
.map(|derivation_path| {
679+
DescriptorPublicKey::XPub(DescriptorXKey {
680+
origin: origin.clone(),
681+
xkey,
682+
derivation_path,
683+
wildcard,
684+
})
685+
})
686+
.collect()
687+
}
688+
}
689+
}
607690
}
608691

609692
impl FromStr for DescriptorSecretKey {
@@ -1337,6 +1420,8 @@ mod test {
13371420
// You can't get the "full derivation path" for a multipath extended public key.
13381421
let desc_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/9478'/<0';1>/8h/*'").unwrap();
13391422
assert!(desc_key.full_derivation_path().is_none());
1423+
assert!(desc_key.is_multipath());
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()]);
13401425

13411426
// All the same but with extended private keys instead of xpubs.
13421427
let xprv = get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/2/<0;1;42;9854>");
@@ -1408,6 +1493,8 @@ mod test {
14081493
);
14091494
let desc_key = DescriptorSecretKey::from_str("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/9478'/<0';1>/8h/*'").unwrap();
14101495
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()]);
14111498

14121499
// It's invalid to:
14131500
// - Not have opening or closing brackets

0 commit comments

Comments
 (0)