From 9c3096a7f749e0b8bd5532e30c5a6edac7a1c5cb Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 31 Jul 2023 00:44:19 -0700 Subject: [PATCH 1/4] Move only: Move specialized impl block into a module There are more specialized key types coming --- src/descriptor/descriptor_impl.rs | 364 ++++++++++++++++++++++++++++++ src/descriptor/mod.rs | 360 +---------------------------- 2 files changed, 371 insertions(+), 353 deletions(-) create mode 100644 src/descriptor/descriptor_impl.rs diff --git a/src/descriptor/descriptor_impl.rs b/src/descriptor/descriptor_impl.rs new file mode 100644 index 000000000..a9318b3c3 --- /dev/null +++ b/src/descriptor/descriptor_impl.rs @@ -0,0 +1,364 @@ +//! Module of specialized impl blocks for certain MiniscriptKeys. +//! Contains common APIs for specific types of keys like `DescriptorPublicKey`, +//! `DefinitePublicKey` and more. + +use core::ops::Range; +use core::str::{self, FromStr}; + +use bitcoin::hashes::{hash160, ripemd160, sha256}; +use bitcoin::{secp256k1, Script}; + +use super::ConversionError; +use crate::descriptor::DescriptorSecretKey; +use crate::prelude::*; +use crate::{ + hash256, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, Error, ForEachKey, + TranslatePk, Translator, +}; + +/// Alias type for a map of public key to secret key +/// +/// This map is returned whenever a descriptor that contains secrets is parsed using +/// [`Descriptor::parse_descriptor`], since the descriptor will always only contain +/// public keys. This map allows looking up the corresponding secret key given a +/// public key from the descriptor. +pub type KeyMap = HashMap; + +impl Descriptor { + /// Whether or not the descriptor has any wildcards + #[deprecated(note = "use has_wildcards instead")] + pub fn is_deriveable(&self) -> bool { + self.has_wildcard() + } + + /// Whether or not the descriptor has any wildcards i.e. `/*`. + pub fn has_wildcard(&self) -> bool { + self.for_any_key(|key| key.has_wildcard()) + } + + /// Replaces all wildcards (i.e. `/*`) in the descriptor with a particular derivation index, + /// turning it into a *definite* descriptor. + /// + /// # Errors + /// - If index ≥ 2^31 + pub fn at_derivation_index( + &self, + index: u32, + ) -> Result, ConversionError> { + struct Derivator(u32); + + impl Translator for Derivator { + fn pk( + &mut self, + pk: &DescriptorPublicKey, + ) -> Result { + pk.clone().at_derivation_index(self.0) + } + + translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, ConversionError); + } + self.translate_pk(&mut Derivator(index)) + .map_err(|e| e.expect_translator_err("No Context errors while translating")) + } + + #[deprecated(note = "use at_derivation_index instead")] + /// Deprecated name for [`Self::at_derivation_index`]. + pub fn derive(&self, index: u32) -> Result, ConversionError> { + self.at_derivation_index(index) + } + + /// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or + /// otherwise converting them. All [`bitcoin::secp256k1::XOnlyPublicKey`]s are converted to by adding a + /// default(0x02) y-coordinate. + /// + /// This is a shorthand for: + /// + /// ``` + /// # use miniscript::{Descriptor, DescriptorPublicKey, bitcoin::secp256k1::Secp256k1}; + /// # use core::str::FromStr; + /// # let descriptor = Descriptor::::from_str("tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)") + /// .expect("Valid ranged descriptor"); + /// # let index = 42; + /// # let secp = Secp256k1::verification_only(); + /// let derived_descriptor = descriptor.at_derivation_index(index).unwrap().derived_descriptor(&secp).unwrap(); + /// # assert_eq!(descriptor.derived_descriptor(&secp, index).unwrap(), derived_descriptor); + /// ``` + /// + /// and is only here really here for backwards compatbility. + /// See [`at_derivation_index`] and `[derived_descriptor`] for more documentation. + /// + /// [`at_derivation_index`]: Self::at_derivation_index + /// [`derived_descriptor`]: crate::DerivedDescriptor::derived_descriptor + /// + /// # Errors + /// + /// This function will return an error if hardened derivation is attempted. + pub fn derived_descriptor( + &self, + secp: &secp256k1::Secp256k1, + index: u32, + ) -> Result, ConversionError> { + self.at_derivation_index(index)?.derived_descriptor(secp) + } + + /// Parse a descriptor that may contain secret keys + /// + /// Internally turns every secret key found into the corresponding public key and then returns a + /// a descriptor that only contains public keys and a map to lookup the secret key given a public key. + pub fn parse_descriptor( + secp: &secp256k1::Secp256k1, + s: &str, + ) -> Result<(Descriptor, KeyMap), Error> { + fn parse_key( + s: &str, + key_map: &mut KeyMap, + secp: &secp256k1::Secp256k1, + ) -> Result { + let (public_key, secret_key) = match DescriptorSecretKey::from_str(s) { + Ok(sk) => ( + sk.to_public(secp) + .map_err(|e| Error::Unexpected(e.to_string()))?, + Some(sk), + ), + Err(_) => ( + DescriptorPublicKey::from_str(s) + .map_err(|e| Error::Unexpected(e.to_string()))?, + None, + ), + }; + + if let Some(secret_key) = secret_key { + key_map.insert(public_key.clone(), secret_key); + } + + Ok(public_key) + } + + let mut keymap_pk = KeyMapWrapper(HashMap::new(), secp); + + struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1); + + impl<'a, C: secp256k1::Signing> Translator + for KeyMapWrapper<'a, C> + { + fn pk(&mut self, pk: &String) -> Result { + parse_key(pk, &mut self.0, self.1) + } + + fn sha256(&mut self, sha256: &String) -> Result { + let hash = + sha256::Hash::from_str(sha256).map_err(|e| Error::Unexpected(e.to_string()))?; + Ok(hash) + } + + fn hash256(&mut self, hash256: &String) -> Result { + let hash = hash256::Hash::from_str(hash256) + .map_err(|e| Error::Unexpected(e.to_string()))?; + Ok(hash) + } + + fn ripemd160(&mut self, ripemd160: &String) -> Result { + let hash = ripemd160::Hash::from_str(ripemd160) + .map_err(|e| Error::Unexpected(e.to_string()))?; + Ok(hash) + } + + fn hash160(&mut self, hash160: &String) -> Result { + let hash = hash160::Hash::from_str(hash160) + .map_err(|e| Error::Unexpected(e.to_string()))?; + Ok(hash) + } + } + + let descriptor = Descriptor::::from_str(s)?; + let descriptor = descriptor.translate_pk(&mut keymap_pk).map_err(|e| { + Error::Unexpected( + e.expect_translator_err("No Outer context errors") + .to_string(), + ) + })?; + + Ok((descriptor, keymap_pk.0)) + } + + /// Serialize a descriptor to string with its secret keys + pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String { + struct KeyMapLookUp<'a>(&'a KeyMap); + + impl<'a> Translator for KeyMapLookUp<'a> { + fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { + key_to_string(pk, self.0) + } + + fn sha256(&mut self, sha256: &sha256::Hash) -> Result { + Ok(sha256.to_string()) + } + + fn hash256(&mut self, hash256: &hash256::Hash) -> Result { + Ok(hash256.to_string()) + } + + fn ripemd160(&mut self, ripemd160: &ripemd160::Hash) -> Result { + Ok(ripemd160.to_string()) + } + + fn hash160(&mut self, hash160: &hash160::Hash) -> Result { + Ok(hash160.to_string()) + } + } + + fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result { + Ok(match key_map.get(pk) { + Some(secret) => secret.to_string(), + None => pk.to_string(), + }) + } + + let descriptor = self + .translate_pk(&mut KeyMapLookUp(key_map)) + .expect("Translation to string cannot fail"); + + descriptor.to_string() + } + + /// Utility method for deriving the descriptor at each index in a range to find one matching + /// `script_pubkey`. + /// + /// If it finds a match then it returns the index it was derived at and the concrete + /// descriptor at that index. If the descriptor is non-derivable then it will simply check the + /// script pubkey against the descriptor and return it if it matches (in this case the index + /// returned will be meaningless). + pub fn find_derivation_index_for_spk( + &self, + secp: &secp256k1::Secp256k1, + script_pubkey: &Script, + range: Range, + ) -> Result)>, ConversionError> { + let range = if self.has_wildcard() { range } else { 0..1 }; + + for i in range { + let concrete = self.derived_descriptor(secp, i)?; + if &concrete.script_pubkey() == script_pubkey { + return Ok(Some((i, concrete))); + } + } + + Ok(None) + } + + /// Whether this descriptor contains a key that has multiple derivation paths. + pub fn is_multipath(&self) -> bool { + self.for_any_key(DescriptorPublicKey::is_multipath) + } + + /// Get as many descriptors as different paths in this descriptor. + /// + /// For multipath descriptors it will return as many descriptors as there is + /// "parallel" paths. For regular descriptors it will just return itself. + #[allow(clippy::blocks_in_if_conditions)] + pub fn into_single_descriptors(self) -> Result>, Error> { + // All single-path descriptors contained in this descriptor. + let mut descriptors = Vec::new(); + // We (ab)use `for_any_key` to gather the number of separate descriptors. + if !self.for_any_key(|key| { + // All multipath keys must have the same number of indexes at the "multi-index" + // step. So we can return early if we already populated the vector. + if !descriptors.is_empty() { + return true; + } + + match key { + DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => false, + DescriptorPublicKey::MultiXPub(xpub) => { + for _ in 0..xpub.derivation_paths.paths().len() { + descriptors.push(self.clone()); + } + true + } + } + }) { + // If there is no multipath key, return early. + return Ok(vec![self]); + } + assert!(!descriptors.is_empty()); + + // Now, transform the multipath key of each descriptor into a single-key using each index. + struct IndexChoser(usize); + impl Translator for IndexChoser { + fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { + match pk { + DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => { + Ok(pk.clone()) + } + DescriptorPublicKey::MultiXPub(_) => pk + .clone() + .into_single_keys() + .get(self.0) + .cloned() + .ok_or(Error::MultipathDescLenMismatch), + } + } + translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, Error); + } + + for (i, desc) in descriptors.iter_mut().enumerate() { + let mut index_choser = IndexChoser(i); + *desc = desc + .translate_pk(&mut index_choser) + .map_err(|e| e.expect_translator_err("No Context errors possible"))?; + } + + Ok(descriptors) + } +} + +impl Descriptor { + /// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or + /// otherwise converting them. All [`bitcoin::secp256k1::XOnlyPublicKey`]s are converted to by adding a + /// default(0x02) y-coordinate. + /// + /// # Examples + /// + /// ``` + /// use miniscript::descriptor::{Descriptor, DescriptorPublicKey}; + /// use miniscript::bitcoin::secp256k1; + /// use std::str::FromStr; + /// + /// // test from bip 86 + /// let secp = secp256k1::Secp256k1::verification_only(); + /// let descriptor = Descriptor::::from_str("tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)") + /// .expect("Valid ranged descriptor"); + /// let result = descriptor.at_derivation_index(0).unwrap().derived_descriptor(&secp).expect("Non-hardened derivation"); + /// assert_eq!(result.to_string(), "tr(03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115)#6qm9h8ym"); + /// ``` + /// + /// # Errors + /// + /// This function will return an error if hardened derivation is attempted. + pub fn derived_descriptor( + &self, + secp: &secp256k1::Secp256k1, + ) -> Result, ConversionError> { + struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1); + + impl<'a, C: secp256k1::Verification> + Translator + for Derivator<'a, C> + { + fn pk( + &mut self, + pk: &DefiniteDescriptorKey, + ) -> Result { + pk.derive_public_key(self.0) + } + + translate_hash_clone!(DefiniteDescriptorKey, bitcoin::PublicKey, ConversionError); + } + + let derived = self.translate_pk(&mut Derivator(secp)); + match derived { + Ok(derived) => Ok(derived), + Err(e) => Err(e.expect_translator_err("No Context errors when deriving keys")), + } + } +} diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 7551e1ad4..f32b3fe73 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -12,29 +12,30 @@ //! use core::fmt; -use core::ops::Range; -use core::str::{self, FromStr}; +use core::str::{self}; use bitcoin::address::WitnessVersion; -use bitcoin::hashes::{hash160, ripemd160, sha256}; -use bitcoin::{secp256k1, Address, Network, Script, ScriptBuf, TxIn, Witness}; +use bitcoin::{Address, Network, ScriptBuf, TxIn, Witness}; use sync::Arc; use self::checksum::verify_checksum; use crate::miniscript::{Legacy, Miniscript, Segwitv0}; use crate::prelude::*; use crate::{ - expression, hash256, miniscript, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier, - ToPublicKey, TranslateErr, TranslatePk, Translator, + expression, miniscript, BareCtx, Error, ForEachKey, MiniscriptKey, Satisfier, ToPublicKey, + TranslateErr, TranslatePk, Translator, }; mod bare; +mod descriptor_impl; mod segwitv0; mod sh; mod sortedmulti; mod tr; // Descriptor Exports +pub use descriptor_impl::KeyMap; + pub use self::bare::{Bare, Pkh}; pub use self::segwitv0::{Wpkh, Wsh, WshInner}; pub use self::sh::{Sh, ShInner}; @@ -50,14 +51,6 @@ pub use self::key::{ SinglePriv, SinglePub, SinglePubKey, Wildcard, }; -/// Alias type for a map of public key to secret key -/// -/// This map is returned whenever a descriptor that contains secrets is parsed using -/// [`Descriptor::parse_descriptor`], since the descriptor will always only contain -/// public keys. This map allows looking up the corresponding secret key given a -/// public key from the descriptor. -pub type KeyMap = HashMap; - /// Script descriptor #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Descriptor { @@ -547,345 +540,6 @@ impl ForEachKey for Descriptor { } } -impl Descriptor { - /// Whether or not the descriptor has any wildcards - #[deprecated(note = "use has_wildcards instead")] - pub fn is_deriveable(&self) -> bool { - self.has_wildcard() - } - - /// Whether or not the descriptor has any wildcards i.e. `/*`. - pub fn has_wildcard(&self) -> bool { - self.for_any_key(|key| key.has_wildcard()) - } - - /// Replaces all wildcards (i.e. `/*`) in the descriptor with a particular derivation index, - /// turning it into a *definite* descriptor. - /// - /// # Errors - /// - If index ≥ 2^31 - pub fn at_derivation_index( - &self, - index: u32, - ) -> Result, ConversionError> { - struct Derivator(u32); - - impl Translator for Derivator { - fn pk( - &mut self, - pk: &DescriptorPublicKey, - ) -> Result { - pk.clone().at_derivation_index(self.0) - } - - translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, ConversionError); - } - self.translate_pk(&mut Derivator(index)) - .map_err(|e| e.expect_translator_err("No Context errors while translating")) - } - - #[deprecated(note = "use at_derivation_index instead")] - /// Deprecated name for [`Self::at_derivation_index`]. - pub fn derive(&self, index: u32) -> Result, ConversionError> { - self.at_derivation_index(index) - } - - /// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or - /// otherwise converting them. All [`bitcoin::secp256k1::XOnlyPublicKey`]s are converted to by adding a - /// default(0x02) y-coordinate. - /// - /// This is a shorthand for: - /// - /// ``` - /// # use miniscript::{Descriptor, DescriptorPublicKey, bitcoin::secp256k1::Secp256k1}; - /// # use core::str::FromStr; - /// # let descriptor = Descriptor::::from_str("tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)") - /// .expect("Valid ranged descriptor"); - /// # let index = 42; - /// # let secp = Secp256k1::verification_only(); - /// let derived_descriptor = descriptor.at_derivation_index(index).unwrap().derived_descriptor(&secp).unwrap(); - /// # assert_eq!(descriptor.derived_descriptor(&secp, index).unwrap(), derived_descriptor); - /// ``` - /// - /// and is only here really here for backwards compatbility. - /// See [`at_derivation_index`] and `[derived_descriptor`] for more documentation. - /// - /// [`at_derivation_index`]: Self::at_derivation_index - /// [`derived_descriptor`]: crate::DerivedDescriptor::derived_descriptor - /// - /// # Errors - /// - /// This function will return an error if hardened derivation is attempted. - pub fn derived_descriptor( - &self, - secp: &secp256k1::Secp256k1, - index: u32, - ) -> Result, ConversionError> { - self.at_derivation_index(index)?.derived_descriptor(secp) - } - - /// Parse a descriptor that may contain secret keys - /// - /// Internally turns every secret key found into the corresponding public key and then returns a - /// a descriptor that only contains public keys and a map to lookup the secret key given a public key. - pub fn parse_descriptor( - secp: &secp256k1::Secp256k1, - s: &str, - ) -> Result<(Descriptor, KeyMap), Error> { - fn parse_key( - s: &str, - key_map: &mut KeyMap, - secp: &secp256k1::Secp256k1, - ) -> Result { - let (public_key, secret_key) = match DescriptorSecretKey::from_str(s) { - Ok(sk) => ( - sk.to_public(secp) - .map_err(|e| Error::Unexpected(e.to_string()))?, - Some(sk), - ), - Err(_) => ( - DescriptorPublicKey::from_str(s) - .map_err(|e| Error::Unexpected(e.to_string()))?, - None, - ), - }; - - if let Some(secret_key) = secret_key { - key_map.insert(public_key.clone(), secret_key); - } - - Ok(public_key) - } - - let mut keymap_pk = KeyMapWrapper(HashMap::new(), secp); - - struct KeyMapWrapper<'a, C: secp256k1::Signing>(KeyMap, &'a secp256k1::Secp256k1); - - impl<'a, C: secp256k1::Signing> Translator - for KeyMapWrapper<'a, C> - { - fn pk(&mut self, pk: &String) -> Result { - parse_key(pk, &mut self.0, self.1) - } - - fn sha256(&mut self, sha256: &String) -> Result { - let hash = - sha256::Hash::from_str(sha256).map_err(|e| Error::Unexpected(e.to_string()))?; - Ok(hash) - } - - fn hash256(&mut self, hash256: &String) -> Result { - let hash = hash256::Hash::from_str(hash256) - .map_err(|e| Error::Unexpected(e.to_string()))?; - Ok(hash) - } - - fn ripemd160(&mut self, ripemd160: &String) -> Result { - let hash = ripemd160::Hash::from_str(ripemd160) - .map_err(|e| Error::Unexpected(e.to_string()))?; - Ok(hash) - } - - fn hash160(&mut self, hash160: &String) -> Result { - let hash = hash160::Hash::from_str(hash160) - .map_err(|e| Error::Unexpected(e.to_string()))?; - Ok(hash) - } - } - - let descriptor = Descriptor::::from_str(s)?; - let descriptor = descriptor.translate_pk(&mut keymap_pk).map_err(|e| { - Error::Unexpected( - e.expect_translator_err("No Outer context errors") - .to_string(), - ) - })?; - - Ok((descriptor, keymap_pk.0)) - } - - /// Serialize a descriptor to string with its secret keys - pub fn to_string_with_secret(&self, key_map: &KeyMap) -> String { - struct KeyMapLookUp<'a>(&'a KeyMap); - - impl<'a> Translator for KeyMapLookUp<'a> { - fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { - key_to_string(pk, self.0) - } - - fn sha256(&mut self, sha256: &sha256::Hash) -> Result { - Ok(sha256.to_string()) - } - - fn hash256(&mut self, hash256: &hash256::Hash) -> Result { - Ok(hash256.to_string()) - } - - fn ripemd160(&mut self, ripemd160: &ripemd160::Hash) -> Result { - Ok(ripemd160.to_string()) - } - - fn hash160(&mut self, hash160: &hash160::Hash) -> Result { - Ok(hash160.to_string()) - } - } - - fn key_to_string(pk: &DescriptorPublicKey, key_map: &KeyMap) -> Result { - Ok(match key_map.get(pk) { - Some(secret) => secret.to_string(), - None => pk.to_string(), - }) - } - - let descriptor = self - .translate_pk(&mut KeyMapLookUp(key_map)) - .expect("Translation to string cannot fail"); - - descriptor.to_string() - } - - /// Utility method for deriving the descriptor at each index in a range to find one matching - /// `script_pubkey`. - /// - /// If it finds a match then it returns the index it was derived at and the concrete - /// descriptor at that index. If the descriptor is non-derivable then it will simply check the - /// script pubkey against the descriptor and return it if it matches (in this case the index - /// returned will be meaningless). - pub fn find_derivation_index_for_spk( - &self, - secp: &secp256k1::Secp256k1, - script_pubkey: &Script, - range: Range, - ) -> Result)>, ConversionError> { - let range = if self.has_wildcard() { range } else { 0..1 }; - - for i in range { - let concrete = self.derived_descriptor(secp, i)?; - if &concrete.script_pubkey() == script_pubkey { - return Ok(Some((i, concrete))); - } - } - - Ok(None) - } - - /// Whether this descriptor contains a key that has multiple derivation paths. - pub fn is_multipath(&self) -> bool { - self.for_any_key(DescriptorPublicKey::is_multipath) - } - - /// Get as many descriptors as different paths in this descriptor. - /// - /// For multipath descriptors it will return as many descriptors as there is - /// "parallel" paths. For regular descriptors it will just return itself. - #[allow(clippy::blocks_in_if_conditions)] - pub fn into_single_descriptors(self) -> Result>, Error> { - // All single-path descriptors contained in this descriptor. - let mut descriptors = Vec::new(); - // We (ab)use `for_any_key` to gather the number of separate descriptors. - if !self.for_any_key(|key| { - // All multipath keys must have the same number of indexes at the "multi-index" - // step. So we can return early if we already populated the vector. - if !descriptors.is_empty() { - return true; - } - - match key { - DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => false, - DescriptorPublicKey::MultiXPub(xpub) => { - for _ in 0..xpub.derivation_paths.paths().len() { - descriptors.push(self.clone()); - } - true - } - } - }) { - // If there is no multipath key, return early. - return Ok(vec![self]); - } - assert!(!descriptors.is_empty()); - - // Now, transform the multipath key of each descriptor into a single-key using each index. - struct IndexChoser(usize); - impl Translator for IndexChoser { - fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { - match pk { - DescriptorPublicKey::Single(..) | DescriptorPublicKey::XPub(..) => { - Ok(pk.clone()) - } - DescriptorPublicKey::MultiXPub(_) => pk - .clone() - .into_single_keys() - .get(self.0) - .cloned() - .ok_or(Error::MultipathDescLenMismatch), - } - } - translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, Error); - } - - for (i, desc) in descriptors.iter_mut().enumerate() { - let mut index_choser = IndexChoser(i); - *desc = desc - .translate_pk(&mut index_choser) - .map_err(|e| e.expect_translator_err("No Context errors possible"))?; - } - - Ok(descriptors) - } -} - -impl Descriptor { - /// Convert all the public keys in the descriptor to [`bitcoin::PublicKey`] by deriving them or - /// otherwise converting them. All [`bitcoin::secp256k1::XOnlyPublicKey`]s are converted to by adding a - /// default(0x02) y-coordinate. - /// - /// # Examples - /// - /// ``` - /// use miniscript::descriptor::{Descriptor, DescriptorPublicKey}; - /// use miniscript::bitcoin::secp256k1; - /// use std::str::FromStr; - /// - /// // test from bip 86 - /// let secp = secp256k1::Secp256k1::verification_only(); - /// let descriptor = Descriptor::::from_str("tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)") - /// .expect("Valid ranged descriptor"); - /// let result = descriptor.at_derivation_index(0).unwrap().derived_descriptor(&secp).expect("Non-hardened derivation"); - /// assert_eq!(result.to_string(), "tr(03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115)#6qm9h8ym"); - /// ``` - /// - /// # Errors - /// - /// This function will return an error if hardened derivation is attempted. - pub fn derived_descriptor( - &self, - secp: &secp256k1::Secp256k1, - ) -> Result, ConversionError> { - struct Derivator<'a, C: secp256k1::Verification>(&'a secp256k1::Secp256k1); - - impl<'a, C: secp256k1::Verification> - Translator - for Derivator<'a, C> - { - fn pk( - &mut self, - pk: &DefiniteDescriptorKey, - ) -> Result { - pk.derive_public_key(self.0) - } - - translate_hash_clone!(DefiniteDescriptorKey, bitcoin::PublicKey, ConversionError); - } - - let derived = self.translate_pk(&mut Derivator(secp)); - match derived { - Ok(derived) => Ok(derived), - Err(e) => Err(e.expect_translator_err("No Context errors when deriving keys")), - } - } -} - impl_from_tree!( Descriptor, /// Parse an expression tree into a descriptor. From 0e2cc5ae6b9ea0e97e5d5be416c1e05134392695 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 31 Jul 2023 01:12:26 -0700 Subject: [PATCH 2/4] Introduce a new Miniscriptkey type for ExtendedPublicKey Unlike descriptor publickey which supports multi xkeys as well as single keys, this only supports extended keys. Makes the ergonimics much simpler for most common wallet implementations co-authored-by: Scott Robinson --- src/descriptor/key.rs | 176 +++++++++++++++++++++++++++++------------- 1 file changed, 122 insertions(+), 54 deletions(-) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 83308e0dc..424e28b8c 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -294,17 +294,7 @@ impl fmt::Display for DescriptorPublicKey { }?; Ok(()) } - DescriptorPublicKey::XPub(ref xpub) => { - maybe_fmt_master_id(f, &xpub.origin)?; - xpub.xkey.fmt(f)?; - fmt_derivation_path(f, &xpub.derivation_path)?; - match xpub.wildcard { - Wildcard::None => {} - Wildcard::Unhardened => write!(f, "/*")?, - Wildcard::Hardened => write!(f, "/*h")?, - } - Ok(()) - } + DescriptorPublicKey::XPub(ref xpub) => xpub.fmt(f), DescriptorPublicKey::MultiXPub(ref xpub) => { maybe_fmt_master_id(f, &xpub.origin)?; xpub.xkey.fmt(f)?; @@ -456,12 +446,7 @@ impl FromStr for DescriptorPublicKey { wildcard, })) } else { - Ok(DescriptorPublicKey::XPub(DescriptorXKey { - origin, - xkey: xpub, - derivation_path: derivation_paths.into_iter().next().unwrap_or_default(), - wildcard, - })) + DescriptorXKey::::from_str(s).map(Self::XPub) } } else { let key = match key_part.len() { @@ -525,17 +510,127 @@ impl error::Error for ConversionError { } } +impl MiniscriptKey for DescriptorXKey { + type Sha256 = sha256::Hash; + type Hash256 = hash256::Hash; + type Ripemd160 = ripemd160::Hash; + type Hash160 = hash160::Hash; + + fn num_der_paths(&self) -> usize { + 1 + } +} + +impl DescriptorXKey { + /// The fingerprint of the master key associated with this key, `0x00000000` if none. + pub fn master_fingerprint(&self) -> bip32::Fingerprint { + if let Some((fingerprint, _)) = self.origin { + fingerprint + } else { + self.xkey.fingerprint() + } + } + + /// Full path, from the master key + /// + /// For wildcard keys this will return the path up to the wildcard, so you + /// can get full paths by appending one additional derivation step, according + /// to the wildcard type (hardened or normal). + pub fn full_derivation_path(&self) -> bip32::DerivationPath { + let origin_path = if let Some((_, ref path)) = self.origin { + path.clone() + } else { + bip32::DerivationPath::from(vec![]) + }; + origin_path.extend(&self.derivation_path) + } + + /// Whether or not the key has a wildcard + pub fn has_wildcard(&self) -> bool { + self.wildcard != Wildcard::None + } + + /// Replaces any wildcard (i.e. `/*`) in the key with a particular derivation index, turning it into a + /// *definite* key (i.e. one where all the derivation paths are set). + /// + /// # Returns + /// + /// - If this key is not an xpub, returns `self`. + /// - If this key is an xpub but does not have a wildcard, returns `self`. + /// - Otherwise, returns the xpub at derivation `index` (removing the wildcard). + /// + /// # Errors + /// + /// - If `index` is hardened. + pub fn at_derivation_index(self, index: u32) -> Result { + let derivation_path = match self.wildcard { + Wildcard::None => self.derivation_path, + Wildcard::Unhardened => self.derivation_path.into_child( + bip32::ChildNumber::from_normal_idx(index) + .ok() + .ok_or(ConversionError::HardenedChild)?, + ), + Wildcard::Hardened => self.derivation_path.into_child( + bip32::ChildNumber::from_hardened_idx(index) + .ok() + .ok_or(ConversionError::HardenedChild)?, + ), + }; + let definite = DescriptorPublicKey::XPub(DescriptorXKey { + origin: self.origin, + xkey: self.xkey, + derivation_path, + wildcard: Wildcard::None, + }); + + Ok(DefiniteDescriptorKey::new(definite) + .expect("The key should not contain any wildcards at this point")) + } +} + +impl fmt::Display for DescriptorXKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + maybe_fmt_master_id(f, &self.origin)?; + self.xkey.fmt(f)?; + fmt_derivation_path(f, &self.derivation_path)?; + match self.wildcard { + Wildcard::None => {} + Wildcard::Unhardened => write!(f, "/*")?, + Wildcard::Hardened => write!(f, "/*h")?, + } + Ok(()) + } +} + +impl FromStr for DescriptorXKey { + type Err = DescriptorKeyParseError; + + fn from_str(s: &str) -> Result { + let (key_part, origin) = parse_key_origin(s)?; + + let (xpub, derivation_paths, wildcard) = + parse_xkey_deriv::(key_part)?; + + if derivation_paths.len() > 1 { + return Err(DescriptorKeyParseError( + "Multiple derivation paths are not allowed for single extended keys", + )); + } + + Ok(Self { + origin, + xkey: xpub, + derivation_path: derivation_paths.into_iter().next().unwrap_or_default(), + wildcard, + }) + } +} + impl DescriptorPublicKey { /// The fingerprint of the master key associated with this key, `0x00000000` if none. pub fn master_fingerprint(&self) -> bip32::Fingerprint { match *self { - DescriptorPublicKey::XPub(ref xpub) => { - if let Some((fingerprint, _)) = xpub.origin { - fingerprint - } else { - xpub.xkey.fingerprint() - } - } + DescriptorPublicKey::XPub(ref xpub) => xpub.master_fingerprint(), DescriptorPublicKey::MultiXPub(ref xpub) => { if let Some((fingerprint, _)) = xpub.origin { fingerprint @@ -573,14 +668,7 @@ impl DescriptorPublicKey { /// For multipath extended keys, this returns `None`. pub fn full_derivation_path(&self) -> Option { match *self { - DescriptorPublicKey::XPub(ref xpub) => { - let origin_path = if let Some((_, ref path)) = xpub.origin { - path.clone() - } else { - bip32::DerivationPath::from(vec![]) - }; - Some(origin_path.extend(&xpub.derivation_path)) - } + DescriptorPublicKey::XPub(ref xpub) => Some(xpub.full_derivation_path()), DescriptorPublicKey::Single(ref single) => { Some(if let Some((_, ref path)) = single.origin { path.clone() @@ -602,7 +690,7 @@ impl DescriptorPublicKey { pub fn has_wildcard(&self) -> bool { match *self { DescriptorPublicKey::Single(..) => false, - DescriptorPublicKey::XPub(ref xpub) => xpub.wildcard != Wildcard::None, + DescriptorPublicKey::XPub(ref xpub) => xpub.has_wildcard(), DescriptorPublicKey::MultiXPub(ref xpub) => xpub.wildcard != Wildcard::None, } } @@ -628,27 +716,7 @@ impl DescriptorPublicKey { pub fn at_derivation_index(self, index: u32) -> Result { let definite = match self { DescriptorPublicKey::Single(_) => self, - DescriptorPublicKey::XPub(xpub) => { - let derivation_path = match xpub.wildcard { - Wildcard::None => xpub.derivation_path, - Wildcard::Unhardened => xpub.derivation_path.into_child( - bip32::ChildNumber::from_normal_idx(index) - .ok() - .ok_or(ConversionError::HardenedChild)?, - ), - Wildcard::Hardened => xpub.derivation_path.into_child( - bip32::ChildNumber::from_hardened_idx(index) - .ok() - .ok_or(ConversionError::HardenedChild)?, - ), - }; - DescriptorPublicKey::XPub(DescriptorXKey { - origin: xpub.origin, - xkey: xpub.xkey, - derivation_path, - wildcard: Wildcard::None, - }) - } + DescriptorPublicKey::XPub(xpub) => return xpub.at_derivation_index(index), DescriptorPublicKey::MultiXPub(_) => return Err(ConversionError::MultiKey), }; From 458206c81a2a1941e31918cd3ffefa57be4df559 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 31 Jul 2023 01:20:27 -0700 Subject: [PATCH 3/4] Expose common descriptor types used by downstream users --- src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7a51f1405..e8aaa38b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,6 +118,8 @@ mod macros; #[macro_use] mod pub_macros; +use bitcoin::bip32; +use descriptor::DescriptorXKey; use internals::hex::exts::DisplayHex; pub use pub_macros::*; @@ -150,6 +152,21 @@ pub use crate::miniscript::satisfy::{Preimage32, Satisfier}; pub use crate::miniscript::{hash256, Miniscript}; use crate::prelude::*; +/// Output descriptor supporting full capabilities of Miniscript as well as all BIPs +/// from BIP380-386. This is the recommended descriptor type to use if you want to +/// support all the features of Miniscript as well full descriptor support. +pub type MsDescriptor = Descriptor; + +/// Output descriptor supporting all the features of Miniscript, but only supports +/// extended public keys. +/// +/// In particular, as of 0.11.0, this descriptor type does not support the following: +/// - Multi-path descriptors +/// - Single keys in descriptor without derivation paths +/// +/// Wallet developers might want to use this descriptor type for ergonomic reasons +/// if they are **only** interested in supporting extended public keys. +pub type MsDescriptorXPubOnly = Descriptor>; ///Public key trait which can be converted to Hash type pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash { /// Returns true if the pubkey is uncompressed. Defaults to `false`. From c3e618e4d93ab524d420f47e753318aca48a7aa5 Mon Sep 17 00:00:00 2001 From: sanket1729 Date: Mon, 31 Jul 2023 02:03:44 -0700 Subject: [PATCH 4/4] Add additional methods to XPubOnly Descriptors I am not happy with the names or the code duplication, but looking for feedback on whether this solves the desired issue --- src/descriptor/descriptor_impl.rs | 90 ++++++++++++++++++++++++++++++- src/descriptor/mod.rs | 37 ++++++++++++- src/lib.rs | 4 +- 3 files changed, 128 insertions(+), 3 deletions(-) diff --git a/src/descriptor/descriptor_impl.rs b/src/descriptor/descriptor_impl.rs index a9318b3c3..35d600d6c 100644 --- a/src/descriptor/descriptor_impl.rs +++ b/src/descriptor/descriptor_impl.rs @@ -2,6 +2,7 @@ //! Contains common APIs for specific types of keys like `DescriptorPublicKey`, //! `DefinitePublicKey` and more. +use core::convert::Infallible; use core::ops::Range; use core::str::{self, FromStr}; @@ -13,7 +14,7 @@ use crate::descriptor::DescriptorSecretKey; use crate::prelude::*; use crate::{ hash256, DefiniteDescriptorKey, Descriptor, DescriptorPublicKey, Error, ForEachKey, - TranslatePk, Translator, + MsDescriptor, MsDescriptorXPubOnly, TranslatePk, Translator, XPubOnly, }; /// Alias type for a map of public key to secret key @@ -24,6 +25,93 @@ use crate::{ /// public key from the descriptor. pub type KeyMap = HashMap; +impl MsDescriptorXPubOnly { + /// Whether or not the descriptor has any wildcards i.e. `/*`. + pub fn has_wildcard(&self) -> bool { + self.for_any_key(|key| key.has_wildcard()) + } + + /// Replaces all wildcards (i.e. `/*`) in the descriptor with a particular derivation index, + /// turning it into a *definite* descriptor. + /// + /// # Errors + /// - If index ≥ 2^31 + pub fn at_derivation_index( + &self, + index: u32, + ) -> Result, ConversionError> { + struct Derivator(u32); + + impl Translator for Derivator { + fn pk(&mut self, pk: &XPubOnly) -> Result { + pk.clone().at_derivation_index(self.0) + } + + translate_hash_clone!(XPubOnly, XPubOnly, ConversionError); + } + + self.translate_pk(&mut Derivator(index)) + .map_err(|e| e.expect_translator_err("No Context errors while translating")) + } + + /// Same as [`Descriptor::derived_descriptor`], but for + /// descriptors with Extended keys only(xpubs only). + pub fn derived_descriptor( + &self, + secp: &secp256k1::Secp256k1, + index: u32, + ) -> Result, ConversionError> { + self.at_derivation_index(index)?.derived_descriptor(secp) + } + + /// Returns the ms descriptor of this [`MsDescriptorXPubOnly`]. + pub fn ms_descriptor(&self) -> MsDescriptor { + struct MsDescriptorTranslator; + + impl Translator for MsDescriptorTranslator { + fn pk(&mut self, pk: &XPubOnly) -> Result { + Ok(DescriptorPublicKey::XPub(pk.clone())) + } + + translate_hash_clone!(XPubOnly, XPubOnly, Infallible); + } + + let res = self + .translate_pk(&mut MsDescriptorTranslator) + .map_err(|e| e.expect_translator_err("No Context errors while translating")); + // Indirect way to unwrap the infallible error type + match res { + Ok(desc) => desc, + Err(e) => match e {}, + } + } + + /// Constructs a new [`MsDescriptorXPubOnly`] from a [`MsDescriptor`]. + /// + /// # Returns None if: + /// + /// - If the descriptor contains any non-xpub keys. + pub fn from_ms_descriptor(desc: &MsDescriptor) -> Option { + struct XOnlyKeyTranslator; + + impl Translator for XOnlyKeyTranslator { + fn pk(&mut self, pk: &DescriptorPublicKey) -> Result { + if let DescriptorPublicKey::XPub(xpub) = pk { + Ok(xpub.clone()) + } else { + Err(()) + } + } + + translate_hash_clone!(DescriptorPublicKey, XPubOnly, ()); + } + + desc.translate_pk(&mut XOnlyKeyTranslator) + .map_err(|e| e.expect_translator_err("No Context errors while translating")) + .ok() + } +} + impl Descriptor { /// Whether or not the descriptor has any wildcards #[deprecated(note = "use has_wildcards instead")] diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index f32b3fe73..f19eb454b 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -623,7 +623,9 @@ mod tests { use crate::descriptor::{DescriptorPublicKey, DescriptorXKey, SinglePub}; #[cfg(feature = "compiler")] use crate::policy; - use crate::{hex_script, Descriptor, Error, Miniscript, Satisfier}; + use crate::{ + hex_script, Descriptor, Error, Miniscript, MsDescriptor, MsDescriptorXPubOnly, Satisfier, + }; type StdDescriptor = Descriptor; const TEST_PK: &str = "pk(020000000000000000000000000000000000000000000000000000000000000002)"; @@ -1344,6 +1346,39 @@ mod tests { assert_eq!(format!("{}", expected), desc); } + #[test] + fn test_extended_only_descriptor() { + fn _test_xpub_desc(raw_desc: &str, raw_addr_expected: &str) { + let secp = secp256k1::Secp256k1::verification_only(); + let index = 5; + + // Parse descriptor + let desc = MsDescriptor::from_str(raw_desc).unwrap(); + let desc_xpub_only = MsDescriptorXPubOnly::from_str(raw_desc).unwrap(); + + // Same string formatting + assert_eq!(desc.to_string(), raw_desc); + assert_eq!(desc_xpub_only.to_string(), raw_desc); + + // Same address + let addr_one = desc + .derived_descriptor(&secp, index) + .unwrap() + .address(bitcoin::Network::Bitcoin) + .unwrap(); + let addr_two = desc_xpub_only + .derived_descriptor(&secp, index) + .unwrap() + .address(bitcoin::Network::Bitcoin) + .unwrap(); + let addr_expected = bitcoin::Address::from_str(raw_addr_expected) + .unwrap() + .assume_checked(); + assert_eq!(addr_one, addr_expected); + assert_eq!(addr_two, addr_expected); + } + } + #[test] fn test_sortedmulti() { fn _test_sortedmulti(raw_desc_one: &str, raw_desc_two: &str, raw_addr_expected: &str) { diff --git a/src/lib.rs b/src/lib.rs index e8aaa38b9..5db44244f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,6 +157,8 @@ use crate::prelude::*; /// support all the features of Miniscript as well full descriptor support. pub type MsDescriptor = Descriptor; +/// They key type that supports parsing only extended public keys. +pub type XPubOnly = DescriptorXKey; /// Output descriptor supporting all the features of Miniscript, but only supports /// extended public keys. /// @@ -166,7 +168,7 @@ pub type MsDescriptor = Descriptor; /// /// Wallet developers might want to use this descriptor type for ergonomic reasons /// if they are **only** interested in supporting extended public keys. -pub type MsDescriptorXPubOnly = Descriptor>; +pub type MsDescriptorXPubOnly = Descriptor; ///Public key trait which can be converted to Hash type pub trait MiniscriptKey: Clone + Eq + Ord + fmt::Debug + fmt::Display + hash::Hash { /// Returns true if the pubkey is uncompressed. Defaults to `false`.