// Bitcoin Dev Kit // Written in 2020 by Alekos Filini // // Copyright (c) 2020-2021 Bitcoin Dev Kit Developers // // This file is licensed under the Apache License, Version 2.0 or the MIT license // , at your option. // You may not use this file except in accordance with one or both of these // licenses. //! Derived descriptor keys //! //! The [`DerivedDescriptorKey`] type is a wrapper over the standard [`DescriptorPublicKey`] which //! guarantees that all the extended keys have a fixed derivation path, i.e. all the wildcards have //! been replaced by actual derivation indexes. //! //! The [`AsDerived`] trait provides a quick way to derive descriptors to obtain a //! `Descriptor` type. This, in turn, can be used to derive public //! keys for arbitrary derivation indexes. //! //! Combining this with [`Wallet::get_signers`], secret keys can also be derived. //! //! # Example //! //! ``` //! # use std::str::FromStr; //! # use bitcoin::secp256k1::Secp256k1; //! use bdk::descriptor::{AsDerived, DescriptorPublicKey}; //! use bdk::miniscript::{ToPublicKey, TranslatePk, MiniscriptKey}; //! //! let secp = Secp256k1::gen_new(); //! //! let key = DescriptorPublicKey::from_str("[aa600a45/84'/0'/0']tpubDCbDXFKoLTQp44wQuC12JgSn5g9CWGjZdpBHeTqyypZ4VvgYjTJmK9CkyR5bFvG9f4PutvwmvpYCLkFx2rpx25hiMs4sUgxJveW8ZzSAVAc/0/*")?; //! let (descriptor, _, _) = bdk::descriptor!(wpkh(key))?; //! //! // derived: wpkh([aa600a45/84'/0'/0']tpubDCbDXFKoLTQp44wQuC12JgSn5g9CWGjZdpBHeTqyypZ4VvgYjTJmK9CkyR5bFvG9f4PutvwmvpYCLkFx2rpx25hiMs4sUgxJveW8ZzSAVAc/0/42)#3ladd0t2 //! let derived = descriptor.as_derived(42, &secp); //! println!("derived: {}", derived); //! //! // with_pks: wpkh(02373ecb54c5e83bd7e0d40adf78b65efaf12fafb13571f0261fc90364eee22e1e)#p4jjgvll //! let with_pks = derived.translate_pk_infallible(|pk| pk.to_public_key(), |pkh| pkh.to_public_key().to_pubkeyhash()); //! println!("with_pks: {}", with_pks); //! # Ok::<(), Box>(()) //! ``` //! //! [`Wallet::get_signers`]: crate::wallet::Wallet::get_signers use std::cmp::Ordering; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops::Deref; use bitcoin::hashes::hash160; use bitcoin::{PublicKey, XOnlyPublicKey}; use miniscript::descriptor::{DescriptorSinglePub, SinglePubKey, Wildcard}; use miniscript::{Descriptor, DescriptorPublicKey, MiniscriptKey, ToPublicKey, TranslatePk}; use crate::wallet::utils::SecpCtx; /// Extended [`DescriptorPublicKey`] that has been derived /// /// Derived keys are guaranteed to never contain wildcards of any kind #[derive(Debug, Clone)] pub struct DerivedDescriptorKey<'s>(DescriptorPublicKey, &'s SecpCtx); impl<'s> DerivedDescriptorKey<'s> { /// Construct a new derived key /// /// Panics if the key is wildcard pub fn new(key: DescriptorPublicKey, secp: &'s SecpCtx) -> DerivedDescriptorKey<'s> { if let DescriptorPublicKey::XPub(xpub) = &key { assert!(xpub.wildcard == Wildcard::None) } DerivedDescriptorKey(key, secp) } } impl<'s> Deref for DerivedDescriptorKey<'s> { type Target = DescriptorPublicKey; fn deref(&self) -> &Self::Target { &self.0 } } impl<'s> PartialEq for DerivedDescriptorKey<'s> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl<'s> Eq for DerivedDescriptorKey<'s> {} impl<'s> PartialOrd for DerivedDescriptorKey<'s> { fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } } impl<'s> Ord for DerivedDescriptorKey<'s> { fn cmp(&self, other: &Self) -> Ordering { self.0.cmp(&other.0) } } impl<'s> fmt::Display for DerivedDescriptorKey<'s> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl<'s> Hash for DerivedDescriptorKey<'s> { fn hash(&self, state: &mut H) { self.0.hash(state); } } impl<'s> MiniscriptKey for DerivedDescriptorKey<'s> { type Hash = Self; fn to_pubkeyhash(&self) -> Self::Hash { DerivedDescriptorKey(self.0.to_pubkeyhash(), self.1) } fn is_uncompressed(&self) -> bool { self.0.is_uncompressed() } } impl<'s> ToPublicKey for DerivedDescriptorKey<'s> { fn to_public_key(&self) -> PublicKey { match &self.0 { DescriptorPublicKey::SinglePub(DescriptorSinglePub { key: SinglePubKey::XOnly(_), .. }) => panic!("Found x-only public key in non-tr descriptor"), DescriptorPublicKey::SinglePub(DescriptorSinglePub { key: SinglePubKey::FullKey(ref pk), .. }) => *pk, DescriptorPublicKey::XPub(ref xpub) => PublicKey::new( xpub.xkey .derive_pub(self.1, &xpub.derivation_path) .expect("Shouldn't fail, only normal derivations") .public_key, ), } } fn to_x_only_pubkey(&self) -> XOnlyPublicKey { match &self.0 { DescriptorPublicKey::SinglePub(DescriptorSinglePub { key: SinglePubKey::XOnly(ref pk), .. }) => *pk, DescriptorPublicKey::SinglePub(DescriptorSinglePub { key: SinglePubKey::FullKey(ref pk), .. }) => XOnlyPublicKey::from(pk.inner), DescriptorPublicKey::XPub(ref xpub) => XOnlyPublicKey::from( xpub.xkey .derive_pub(self.1, &xpub.derivation_path) .expect("Shouldn't fail, only normal derivations") .public_key, ), } } fn hash_to_hash160(hash: &Self::Hash) -> hash160::Hash { hash.to_public_key().to_pubkeyhash() } } /// Utilities to derive descriptors /// /// Check out the [module level] documentation for more. /// /// [module level]: crate::descriptor::derived pub trait AsDerived { /// Derive a descriptor and transform all of its keys to `DerivedDescriptorKey` fn as_derived<'s>(&self, index: u32, secp: &'s SecpCtx) -> Descriptor>; /// Transform the keys into `DerivedDescriptorKey`. /// /// Panics if the descriptor is not "fixed", i.e. if it's derivable fn as_derived_fixed<'s>(&self, secp: &'s SecpCtx) -> Descriptor>; } impl AsDerived for Descriptor { fn as_derived<'s>( &self, index: u32, secp: &'s SecpCtx, ) -> Descriptor> { self.derive(index).translate_pk_infallible( |key| DerivedDescriptorKey::new(key.clone(), secp), |key| DerivedDescriptorKey::new(key.clone(), secp), ) } fn as_derived_fixed<'s>(&self, secp: &'s SecpCtx) -> Descriptor> { assert!(!self.is_deriveable()); self.as_derived(0, secp) } }