// 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. //! Descriptors //! //! This module contains generic utilities to work with descriptors, plus some re-exported types //! from [`miniscript`]. use std::collections::{BTreeMap, HashMap, HashSet}; use std::ops::Deref; use bitcoin::util::bip32::{ ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint, KeySource, }; use bitcoin::util::psbt; use bitcoin::{Network, PublicKey, Script, TxOut}; use miniscript::descriptor::{DescriptorPublicKey, DescriptorType, DescriptorXKey, Wildcard}; pub use miniscript::{descriptor::KeyMap, Descriptor, Legacy, Miniscript, ScriptContext, Segwitv0}; use miniscript::{DescriptorTrait, ForEachKey, TranslatePk}; use crate::descriptor::policy::BuildSatisfaction; pub mod checksum; pub(crate) mod derived; #[doc(hidden)] pub mod dsl; pub mod error; pub mod policy; pub mod template; pub use self::checksum::get_checksum; use self::derived::AsDerived; pub use self::derived::DerivedDescriptorKey; pub use self::error::Error as DescriptorError; pub use self::policy::Policy; use self::template::DescriptorTemplateOut; use crate::keys::{IntoDescriptorKey, KeyError}; use crate::wallet::signer::SignersContainer; use crate::wallet::utils::SecpCtx; /// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`] pub type ExtendedDescriptor = Descriptor; /// Alias for a [`Descriptor`] that contains extended **derived** keys pub type DerivedDescriptor<'s> = Descriptor>; /// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or /// [`psbt::Output`] /// /// [`psbt::Input`]: bitcoin::util::psbt::Input /// [`psbt::Output`]: bitcoin::util::psbt::Output pub type HdKeyPaths = BTreeMap; /// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`] pub trait IntoWalletDescriptor { /// Convert to wallet descriptor fn into_wallet_descriptor( self, secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>; } impl IntoWalletDescriptor for &str { fn into_wallet_descriptor( self, secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { let descriptor = if self.contains('#') { let parts: Vec<&str> = self.splitn(2, '#').collect(); if !get_checksum(parts[0]) .ok() .map(|computed| computed == parts[1]) .unwrap_or(false) { return Err(DescriptorError::InvalidDescriptorChecksum); } parts[0] } else { self }; ExtendedDescriptor::parse_descriptor(secp, descriptor)? .into_wallet_descriptor(secp, network) } } impl IntoWalletDescriptor for &String { fn into_wallet_descriptor( self, secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { self.as_str().into_wallet_descriptor(secp, network) } } impl IntoWalletDescriptor for ExtendedDescriptor { fn into_wallet_descriptor( self, secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { (self, KeyMap::default()).into_wallet_descriptor(secp, network) } } impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) { fn into_wallet_descriptor( self, secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { use crate::keys::DescriptorKey; let check_key = |pk: &DescriptorPublicKey| { let (pk, _, networks) = if self.0.is_witness() { let desciptor_key: DescriptorKey = pk.clone().into_descriptor_key()?; desciptor_key.extract(secp)? } else { let desciptor_key: DescriptorKey = pk.clone().into_descriptor_key()?; desciptor_key.extract(secp)? }; if networks.contains(&network) { Ok(pk) } else { Err(DescriptorError::Key(KeyError::InvalidNetwork)) } }; // check the network for the keys let translated = self.0.translate_pk(check_key, check_key)?; Ok((translated, self.1)) } } impl IntoWalletDescriptor for DescriptorTemplateOut { fn into_wallet_descriptor( self, _secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { let valid_networks = &self.2; let fix_key = |pk: &DescriptorPublicKey| { if valid_networks.contains(&network) { // workaround for xpubs generated by other key types, like bip39: since when the // conversion is made one network has to be chosen, what we generally choose // "mainnet", but then override the set of valid networks to specify that all of // them are valid. here we reset the network to make sure the wallet struct gets a // descriptor with the right network everywhere. let pk = match pk { DescriptorPublicKey::XPub(ref xpub) => { let mut xpub = xpub.clone(); xpub.xkey.network = network; DescriptorPublicKey::XPub(xpub) } other => other.clone(), }; Ok(pk) } else { Err(DescriptorError::Key(KeyError::InvalidNetwork)) } }; // fixup the network for keys that need it let translated = self.0.translate_pk(fix_key, fix_key)?; Ok((translated, self.1)) } } /// Wrapper for `IntoWalletDescriptor` that performs additional checks on the keys contained in the /// descriptor pub(crate) fn into_wallet_descriptor_checked( inner: T, secp: &SecpCtx, network: Network, ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> { let (descriptor, keymap) = inner.into_wallet_descriptor(secp, network)?; // Ensure the keys don't contain any hardened derivation steps or hardened wildcards let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| { if let DescriptorPublicKey::XPub(DescriptorXKey { derivation_path, wildcard, .. }) = k.as_key() { return *wildcard == Wildcard::Hardened || derivation_path.into_iter().any(ChildNumber::is_hardened); } false }); if descriptor_contains_hardened_steps { return Err(DescriptorError::HardenedDerivationXpub); } // Ensure that there are no duplicated keys let mut found_keys = HashSet::new(); let descriptor_contains_duplicated_keys = descriptor.for_any_key(|k| { if let DescriptorPublicKey::XPub(xkey) = k.as_key() { let fingerprint = xkey.root_fingerprint(secp); if found_keys.contains(&fingerprint) { return true; } found_keys.insert(fingerprint); } false }); if descriptor_contains_duplicated_keys { return Err(DescriptorError::DuplicatedKeys); } Ok((descriptor, keymap)) } #[doc(hidden)] /// Used internally mainly by the `descriptor!()` and `fragment!()` macros pub trait CheckMiniscript { fn check_minsicript(&self) -> Result<(), miniscript::Error>; } impl CheckMiniscript for miniscript::Miniscript { fn check_minsicript(&self) -> Result<(), miniscript::Error> { Ctx::check_global_validity(self)?; Ok(()) } } /// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`] pub trait ExtractPolicy { /// Extract the spending [`policy`] fn extract_policy( &self, signers: &SignersContainer, psbt: BuildSatisfaction, secp: &SecpCtx, ) -> Result, DescriptorError>; } pub(crate) trait XKeyUtils { fn full_path(&self, append: &[ChildNumber]) -> DerivationPath; fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint; } // FIXME: `InnerXKey` was made private in rust-miniscript, so we have to implement this manually on // both `ExtendedPubKey` and `ExtendedPrivKey`. // // Revert back to using the trait once https://github.com/rust-bitcoin/rust-miniscript/pull/230 is // released impl XKeyUtils for DescriptorXKey { fn full_path(&self, append: &[ChildNumber]) -> DerivationPath { let full_path = match self.origin { Some((_, ref path)) => path .into_iter() .chain(self.derivation_path.into_iter()) .cloned() .collect(), None => self.derivation_path.clone(), }; if self.wildcard != Wildcard::None { full_path .into_iter() .chain(append.iter()) .cloned() .collect() } else { full_path } } fn root_fingerprint(&self, _: &SecpCtx) -> Fingerprint { match self.origin { Some((fingerprint, _)) => fingerprint, None => self.xkey.fingerprint(), } } } impl XKeyUtils for DescriptorXKey { fn full_path(&self, append: &[ChildNumber]) -> DerivationPath { let full_path = match self.origin { Some((_, ref path)) => path .into_iter() .chain(self.derivation_path.into_iter()) .cloned() .collect(), None => self.derivation_path.clone(), }; if self.wildcard != Wildcard::None { full_path .into_iter() .chain(append.iter()) .cloned() .collect() } else { full_path } } fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint { match self.origin { Some((fingerprint, _)) => fingerprint, None => self.xkey.fingerprint(secp), } } } pub(crate) trait DerivedDescriptorMeta { fn get_hd_keypaths(&self, secp: &SecpCtx) -> Result; } pub(crate) trait DescriptorMeta { fn is_witness(&self) -> bool; fn get_extended_keys(&self) -> Result>, DescriptorError>; fn derive_from_hd_keypaths<'s>( &self, hd_keypaths: &HdKeyPaths, secp: &'s SecpCtx, ) -> Option>; fn derive_from_psbt_input<'s>( &self, psbt_input: &psbt::Input, utxo: Option, secp: &'s SecpCtx, ) -> Option>; } pub(crate) trait DescriptorScripts { fn psbt_redeem_script(&self) -> Option