Skip to content

Commit d6d81a0

Browse files
committed
desc keys: add a method to get single-path keys from multipath keys
1 parent 9c95b5e commit d6d81a0

File tree

1 file changed

+80
-0
lines changed

1 file changed

+80
-0
lines changed

src/descriptor/key.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,44 @@ impl DescriptorSecretKey {
314314

315315
Ok(pk)
316316
}
317+
318+
/// Whether or not this key has multiple derivation paths.
319+
pub fn is_multipath(&self) -> bool {
320+
match *self {
321+
DescriptorSecretKey::Single(..) | DescriptorSecretKey::XPrv(..) => false,
322+
DescriptorSecretKey::MultiXPrv(_) => true,
323+
}
324+
}
325+
326+
/// Get as many keys as derivation paths in this key.
327+
///
328+
/// For raw keys and single-path extended keys it will return the key itself.
329+
/// For multipath extended keys it will return a single-path extended key per derivation
330+
/// path.
331+
pub fn into_single_keys(self) -> Vec<DescriptorSecretKey> {
332+
match self {
333+
DescriptorSecretKey::Single(..) | DescriptorSecretKey::XPrv(..) => vec![self],
334+
DescriptorSecretKey::MultiXPrv(xpub) => {
335+
let DescriptorMultiXKey {
336+
origin,
337+
xkey,
338+
derivation_paths,
339+
wildcard,
340+
} = xpub;
341+
derivation_paths
342+
.into_iter()
343+
.map(|derivation_path| {
344+
DescriptorSecretKey::XPrv(DescriptorXKey {
345+
origin: origin.clone(),
346+
xkey,
347+
derivation_path,
348+
wildcard,
349+
})
350+
})
351+
.collect()
352+
}
353+
}
354+
}
317355
}
318356

319357
/// Writes the fingerprint of the origin, if there is one.
@@ -584,6 +622,44 @@ impl DescriptorPublicKey {
584622
Ok(DefiniteDescriptorKey::new(definite)
585623
.expect("The key should not contain any wildcards at this point"))
586624
}
625+
626+
/// Whether or not this key has multiple derivation paths.
627+
pub fn is_multipath(&self) -> bool {
628+
match *self {
629+
DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => false,
630+
DescriptorPublicKey::MultiXPub(_) => true,
631+
}
632+
}
633+
634+
/// Get as many keys as derivation paths in this key.
635+
///
636+
/// For raw public key and single-path extended keys it will return the key itself.
637+
/// For multipath extended keys it will return a single-path extended key per derivation
638+
/// path.
639+
pub fn into_single_keys(self) -> Vec<DescriptorPublicKey> {
640+
match self {
641+
DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => vec![self],
642+
DescriptorPublicKey::MultiXPub(xpub) => {
643+
let DescriptorMultiXKey {
644+
origin,
645+
xkey,
646+
derivation_paths,
647+
wildcard,
648+
} = xpub;
649+
derivation_paths
650+
.into_iter()
651+
.map(|derivation_path| {
652+
DescriptorPublicKey::XPub(DescriptorXKey {
653+
origin: origin.clone(),
654+
xkey,
655+
derivation_path,
656+
wildcard,
657+
})
658+
})
659+
.collect()
660+
}
661+
}
662+
}
587663
}
588664

589665
impl FromStr for DescriptorSecretKey {
@@ -1317,6 +1393,8 @@ mod test {
13171393
// You can't get the "full derivation path" for a multipath extended public key.
13181394
let desc_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/9478'/<0';1>/8h/*'").unwrap();
13191395
assert!(desc_key.full_derivation_path().is_none());
1396+
assert!(desc_key.is_multipath());
1397+
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()]);
13201398

13211399
// All the same but with extended private keys instead of xpubs.
13221400
let xprv = get_multipath_xprv("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/2/<0;1;42;9854>");
@@ -1388,6 +1466,8 @@ mod test {
13881466
);
13891467
let desc_key = DescriptorSecretKey::from_str("[abcdef00/0'/1']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/9478'/<0';1>/8h/*'").unwrap();
13901468
assert!(desc_key.to_public(&secp).is_err());
1469+
assert!(desc_key.is_multipath());
1470+
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()]);
13911471

13921472
// It's invalid to:
13931473
// - Not have opening or closing brackets

0 commit comments

Comments
 (0)