]> Untitled Git - bdk/commitdiff
descriptor: Use `DescriptorError` instead of `Error` when reasonable
authorAlekos Filini <alekos.filini@gmail.com>
Mon, 11 Jan 2021 12:12:01 +0000 (13:12 +0100)
committerAlekos Filini <alekos.filini@gmail.com>
Tue, 12 Jan 2021 11:21:22 +0000 (12:21 +0100)
Change the return type of the `descriptor!()` macro and `ToWalletDescriptor` to
avoid having to map errors.

Also introduce more checks to validate descriptors built using the macro.

CHANGELOG.md
src/database/any.rs
src/descriptor/checksum.rs
src/descriptor/dsl.rs
src/descriptor/error.rs
src/descriptor/mod.rs
src/descriptor/policy.rs
src/descriptor/template.rs
src/keys/mod.rs

index d410360c6970073d413cd20003af2f4d8530464e..6a3f63f793f0535acee59447f170c877294c882b 100644 (file)
@@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ## [Unreleased]
 
+### Descriptor
+#### Changed
+- Added an alias `DescriptorError` for `descriptor::error::Error`
+- Changed the error returned by `descriptor!()` and `fragment!()` to `DescriptorError`
+- Changed the error type in `ToWalletDescriptor` to `DescriptorError`
+- Improved checks on descriptors built using the macros
+
 ### Blockchain
 #### Changed
 - Remove `BlockchainMarker`, `OfflineClient` and `OfflineWallet` in favor of just using the unit
index 64dd47722dbaf3461dc196d2620457f41a8c2a34..021ab7d679effbb938baf14144210c5d6b47bff4 100644 (file)
 //! # use bdk::database::{AnyDatabase, MemoryDatabase};
 //! # use bdk::{Wallet};
 //! let memory = MemoryDatabase::default();
-//! let wallet_memory =
-//!     Wallet::new_offline("...", None, Network::Testnet, memory)?;
+//! let wallet_memory = Wallet::new_offline("...", None, Network::Testnet, memory)?;
 //!
 //! # #[cfg(feature = "key-value-db")]
 //! # {
 //! let sled = sled::open("my-database")?.open_tree("default_tree")?;
-//! let wallet_sled =
-//!     Wallet::new_offline("...", None, Network::Testnet, sled)?;
+//! let wallet_sled = Wallet::new_offline("...", None, Network::Testnet, sled)?;
 //! # }
 //! # Ok::<(), bdk::Error>(())
 //! ```
index 04b226013fd3962b09f68a1c528981a06ebf3b50..08ebf3bfcdf94be01f4ec622017db00d5eb1e091 100644 (file)
@@ -29,7 +29,7 @@
 
 use std::iter::FromIterator;
 
-use crate::descriptor::Error;
+use crate::descriptor::DescriptorError;
 
 const INPUT_CHARSET: &str =  "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
 const CHECKSUM_CHARSET: &str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
@@ -57,14 +57,14 @@ fn poly_mod(mut c: u64, val: u64) -> u64 {
 }
 
 /// Compute the checksum of a descriptor
-pub fn get_checksum(desc: &str) -> Result<String, Error> {
+pub fn get_checksum(desc: &str) -> Result<String, DescriptorError> {
     let mut c = 1;
     let mut cls = 0;
     let mut clscount = 0;
     for ch in desc.chars() {
         let pos = INPUT_CHARSET
             .find(ch)
-            .ok_or(Error::InvalidDescriptorCharacter(ch))? as u64;
+            .ok_or(DescriptorError::InvalidDescriptorCharacter(ch))? as u64;
         c = poly_mod(c, pos & 31);
         cls = cls * 3 + (pos >> 5);
         clscount += 1;
@@ -120,7 +120,7 @@ mod test {
 
         assert!(matches!(
             get_checksum(&invalid_desc).err(),
-            Some(Error::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart
+            Some(DescriptorError::InvalidDescriptorCharacter(invalid_char)) if invalid_char == sparkle_heart
         ));
     }
 }
index 7cf83b62087496cbd668c046913121a59898049a..caf2eaa0a8c00ed1a24bd0373ac9b02b6f029f89 100644 (file)
@@ -60,6 +60,7 @@ macro_rules! impl_top_level_pk {
 
         $key.to_descriptor_key()
             .and_then(|key: DescriptorKey<$ctx>| key.extract(&secp))
+            .map_err($crate::descriptor::DescriptorError::Key)
             .map(|(pk, key_map, valid_networks)| {
                 (
                     $crate::miniscript::Descriptor::<
@@ -75,11 +76,17 @@ macro_rules! impl_top_level_pk {
 #[doc(hidden)]
 #[macro_export]
 macro_rules! impl_leaf_opcode {
-    ( $terminal_variant:ident ) => {
+    ( $terminal_variant:ident ) => {{
+        use $crate::descriptor::CheckMiniscript;
+
         $crate::miniscript::Miniscript::from_ast(
             $crate::miniscript::miniscript::decode::Terminal::$terminal_variant,
         )
-        .map_err($crate::Error::Miniscript)
+        .map_err($crate::descriptor::DescriptorError::Miniscript)
+        .and_then(|minisc| {
+            minisc.check_minsicript()?;
+            Ok(minisc)
+        })
         .map(|minisc| {
             (
                 minisc,
@@ -87,17 +94,23 @@ macro_rules! impl_leaf_opcode {
                 $crate::keys::any_network(),
             )
         })
-    };
+    }};
 }
 
 #[doc(hidden)]
 #[macro_export]
 macro_rules! impl_leaf_opcode_value {
-    ( $terminal_variant:ident, $value:expr ) => {
+    ( $terminal_variant:ident, $value:expr ) => {{
+        use $crate::descriptor::CheckMiniscript;
+
         $crate::miniscript::Miniscript::from_ast(
             $crate::miniscript::miniscript::decode::Terminal::$terminal_variant($value),
         )
-        .map_err($crate::Error::Miniscript)
+        .map_err($crate::descriptor::DescriptorError::Miniscript)
+        .and_then(|minisc| {
+            minisc.check_minsicript()?;
+            Ok(minisc)
+        })
         .map(|minisc| {
             (
                 minisc,
@@ -105,17 +118,23 @@ macro_rules! impl_leaf_opcode_value {
                 $crate::keys::any_network(),
             )
         })
-    };
+    }};
 }
 
 #[doc(hidden)]
 #[macro_export]
 macro_rules! impl_leaf_opcode_value_two {
-    ( $terminal_variant:ident, $one:expr, $two:expr ) => {
+    ( $terminal_variant:ident, $one:expr, $two:expr ) => {{
+        use $crate::descriptor::CheckMiniscript;
+
         $crate::miniscript::Miniscript::from_ast(
             $crate::miniscript::miniscript::decode::Terminal::$terminal_variant($one, $two),
         )
-        .map_err($crate::Error::Miniscript)
+        .map_err($crate::descriptor::DescriptorError::Miniscript)
+        .and_then(|minisc| {
+            minisc.check_minsicript()?;
+            Ok(minisc)
+        })
         .map(|minisc| {
             (
                 minisc,
@@ -123,13 +142,15 @@ macro_rules! impl_leaf_opcode_value_two {
                 $crate::keys::any_network(),
             )
         })
-    };
+    }};
 }
 
 #[doc(hidden)]
 #[macro_export]
 macro_rules! impl_node_opcode_two {
     ( $terminal_variant:ident, $( $inner:tt )* ) => ({
+        use $crate::descriptor::CheckMiniscript;
+
         let inner = $crate::fragment_internal!( @t $( $inner )* );
         let (a, b) = $crate::descriptor::dsl::TupleTwo::from(inner).flattened();
 
@@ -139,10 +160,14 @@ macro_rules! impl_node_opcode_two {
                 // join key_maps
                 a_keymap.extend(b_keymap.into_iter());
 
-                Ok(($crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
+                let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
                     std::sync::Arc::new(a_minisc),
                     std::sync::Arc::new(b_minisc),
-                ))?, a_keymap, $crate::keys::merge_networks(&a_networks, &b_networks)))
+                ))?;
+
+                minisc.check_minsicript()?;
+
+                Ok((minisc, a_keymap, $crate::keys::merge_networks(&a_networks, &b_networks)))
             })
     });
 }
@@ -151,6 +176,8 @@ macro_rules! impl_node_opcode_two {
 #[macro_export]
 macro_rules! impl_node_opcode_three {
     ( $terminal_variant:ident, $( $inner:tt )* ) => {
+        use $crate::descriptor::CheckMiniscript;
+
         let inner = $crate::fragment_internal!( @t $( $inner )* );
         let (a, b, c) = $crate::descriptor::dsl::TupleThree::from(inner).flattened();
 
@@ -164,11 +191,15 @@ macro_rules! impl_node_opcode_three {
                 let networks = $crate::keys::merge_networks(&a_networks, &b_networks);
                 let networks = $crate::keys::merge_networks(&networks, &c_networks);
 
-                Ok(($crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
+                let minisc = $crate::miniscript::Miniscript::from_ast($crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
                     std::sync::Arc::new(a_minisc),
                     std::sync::Arc::new(b_minisc),
                     std::sync::Arc::new(c_minisc),
-                ))?, a_keymap, networks))
+                ))?;
+
+                minisc.check_minsicript()?;
+
+                Ok((minisc, a_keymap, networks))
             })
     };
 }
@@ -190,6 +221,7 @@ macro_rules! impl_sortedmulti {
         )*
 
         keys.into_iter().collect::<Result<Vec<_>, _>>()
+            .map_err($crate::descriptor::DescriptorError::Key)
             .and_then(|keys| $crate::keys::make_sortedmulti_inner($thresh, keys, &secp))
     });
 
@@ -199,18 +231,20 @@ macro_rules! impl_sortedmulti {
 #[macro_export]
 macro_rules! apply_modifier {
     ( $terminal_variant:ident, $inner:expr ) => {{
+        use $crate::descriptor::CheckMiniscript;
+
         $inner
-            .map_err(|e| -> $crate::Error { e.into() })
+            .map_err(|e| -> $crate::descriptor::DescriptorError { e.into() })
             .and_then(|(minisc, keymap, networks)| {
-                Ok((
-                    $crate::miniscript::Miniscript::from_ast(
-                        $crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
-                            std::sync::Arc::new(minisc),
-                        ),
-                    )?,
-                    keymap,
-                    networks,
-                ))
+                let minisc = $crate::miniscript::Miniscript::from_ast(
+                    $crate::miniscript::miniscript::decode::Terminal::$terminal_variant(
+                        std::sync::Arc::new(minisc),
+                    ),
+                )?;
+
+                minisc.check_minsicript()?;
+
+                Ok((minisc, keymap, networks))
             })
     }};
 
@@ -272,7 +306,7 @@ macro_rules! apply_modifier {
 /// Macro to write full descriptors with code
 ///
 /// This macro expands to a `Result` of
-/// [`DescriptorTemplateOut`](super::template::DescriptorTemplateOut) and [`Error`](crate::Error)
+/// [`DescriptorTemplateOut`](super::template::DescriptorTemplateOut) and [`DescriptorError`](crate::descriptor::DescriptorError)
 ///
 /// The syntax is very similar to the normal descriptor syntax, with the exception that modifiers
 /// cannot be grouped together. For instance, a descriptor fragment like `sdv:older(144)` has to be
@@ -305,8 +339,11 @@ macro_rules! apply_modifier {
 ///
 /// ```
 /// # use std::str::FromStr;
-/// let my_key_1 = bitcoin::PublicKey::from_str("02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c")?;
-/// let my_key_2 = bitcoin::PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy")?;
+/// let my_key_1 = bitcoin::PublicKey::from_str(
+///     "02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c",
+/// )?;
+/// let my_key_2 =
+///     bitcoin::PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy")?;
 /// let my_timelock = 50;
 ///
 /// let (descriptor_a, key_map_a, networks) = bdk::descriptor! {
@@ -315,12 +352,13 @@ macro_rules! apply_modifier {
 ///     )
 /// }?;
 ///
+/// #[rustfmt::skip]
 /// let b_items = vec![
 ///     bdk::fragment!(pk(my_key_1))?,
 ///     bdk::fragment!(s:pk(my_key_2))?,
 ///     bdk::fragment!(s:d:v:older(my_timelock))?,
 /// ];
-/// let (descriptor_b, mut key_map_b, networks) = bdk::descriptor!(wsh(thresh_vec(2,b_items)))?;
+/// let (descriptor_b, mut key_map_b, networks) = bdk::descriptor!(wsh(thresh_vec(2, b_items)))?;
 ///
 /// assert_eq!(descriptor_a, descriptor_b);
 /// assert_eq!(key_map_a.len(), key_map_b.len());
@@ -496,7 +534,7 @@ macro_rules! fragment_internal {
 
 /// Macro to write descriptor fragments with code
 ///
-/// This macro will be expanded to an object of type `Result<(Miniscript<DescriptorPublicKey, _>, KeyMap, ValidNetworks), Error>`. It allows writing
+/// This macro will be expanded to an object of type `Result<(Miniscript<DescriptorPublicKey, _>, KeyMap, ValidNetworks), DescriptorError>`. It allows writing
 /// fragments of larger descriptors that can be pieced together using `fragment!(thresh_vec(m, ...))`.
 ///
 /// The syntax to write macro fragment is the same as documented for the [`descriptor`] macro.
@@ -599,6 +637,7 @@ macro_rules! fragment {
         )*
 
         keys.into_iter().collect::<Result<Vec<_>, _>>()
+            .map_err($crate::descriptor::DescriptorError::Key)
             .and_then(|keys| $crate::keys::make_multi($thresh, keys, &secp))
     });
 
@@ -620,8 +659,8 @@ mod test {
 
     use std::str::FromStr;
 
-    use crate::descriptor::DescriptorMeta;
-    use crate::keys::{DescriptorKey, KeyError, ToDescriptorKey, ValidNetworks};
+    use crate::descriptor::{DescriptorError, DescriptorMeta};
+    use crate::keys::{DescriptorKey, ToDescriptorKey, ValidNetworks};
     use bitcoin::network::constants::Network::{Bitcoin, Regtest, Testnet};
     use bitcoin::util::bip32;
     use bitcoin::util::bip32::ChildNumber;
@@ -631,7 +670,7 @@ mod test {
 
     // verify descriptor generates expected script(s) (if bare or pk) or address(es)
     fn check(
-        desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), KeyError>,
+        desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
         is_witness: bool,
         is_fixed: bool,
         expected: &[&str],
@@ -978,4 +1017,15 @@ mod test {
 
         assert_eq!(descriptor.to_string(), "wsh(thresh(2,dv:older(1),s:pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c),s:pk(02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c)))")
     }
+
+    // TODO: uncomment once https://github.com/rust-bitcoin/rust-miniscript/pull/221 is released
+    //
+    // #[test]
+    // #[should_panic(expected = "Miniscript(ContextError(CompressedOnly))")]
+    // fn test_dsl_miniscript_checks() {
+    //     let mut uncompressed_pk = PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap();
+    //     uncompressed_pk.compressed = false;
+
+    //     descriptor!(wsh(v:pk(uncompressed_pk))).unwrap();
+    // }
 }
index e470fea9c205738764e4212c09ab16423481592f..495f999c91cf17b0cb7fb6d01b8b59f8c837b1e4 100644 (file)
 /// Errors related to the parsing and usage of descriptors
 #[derive(Debug)]
 pub enum Error {
-    //InternalError,
-    //InvalidPrefix(Vec<u8>),
-    //HardenedDerivationOnXpub,
-    //MalformedInput,
     /// Invalid HD Key path, such as having a wildcard but a length != 1
     InvalidHDKeyPath,
+    /// The provided descriptor doesn't match its checksum
+    InvalidDescriptorChecksum,
 
-    //KeyParsingError(String),
     /// Error thrown while working with [`keys`](crate::keys)
     Key(crate::keys::KeyError),
     /// Error while extracting and manipulating policies
     Policy(crate::descriptor::policy::PolicyError),
 
-    //InputIndexDoesntExist,
-    //MissingPublicKey,
-    //MissingDetails,
     /// Invalid character found in the descriptor checksum
     InvalidDescriptorCharacter(char),
 
-    //CantDeriveWithMiniscript,
     /// BIP32 error
     BIP32(bitcoin::util::bip32::Error),
     /// Error during base58 decoding
index 97c22839897943ba07ed65565534cccf562e8d19..cffb1cb476dd09c8d79f61a39427136bbce888fd 100644 (file)
@@ -49,7 +49,7 @@ pub mod policy;
 pub mod template;
 
 pub use self::checksum::get_checksum;
-use self::error::Error;
+pub use self::error::Error as DescriptorError;
 pub use self::policy::Policy;
 use self::template::DescriptorTemplateOut;
 use crate::keys::{KeyError, ToDescriptorKey};
@@ -72,14 +72,14 @@ pub trait ToWalletDescriptor {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError>;
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>;
 }
 
 impl ToWalletDescriptor for &str {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         let descriptor = if self.contains('#') {
             let parts: Vec<&str> = self.splitn(2, '#').collect();
             if !get_checksum(parts[0])
@@ -87,7 +87,7 @@ impl ToWalletDescriptor for &str {
                 .map(|computed| computed == parts[1])
                 .unwrap_or(false)
             {
-                return Err(KeyError::InvalidChecksum);
+                return Err(DescriptorError::InvalidDescriptorChecksum);
             }
 
             parts[0]
@@ -103,7 +103,7 @@ impl ToWalletDescriptor for &String {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         self.as_str().to_wallet_descriptor(network)
     }
 }
@@ -112,7 +112,7 @@ impl ToWalletDescriptor for ExtendedDescriptor {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         (self, KeyMap::default()).to_wallet_descriptor(network)
     }
 }
@@ -121,7 +121,7 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap) {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         use crate::keys::DescriptorKey;
 
         let secp = Secp256k1::new();
@@ -140,7 +140,7 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap) {
             if networks.contains(&network) {
                 Ok(pk)
             } else {
-                Err(KeyError::InvalidNetwork)
+                Err(DescriptorError::Key(KeyError::InvalidNetwork))
             }
         };
 
@@ -155,7 +155,7 @@ impl ToWalletDescriptor for DescriptorTemplateOut {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         let valid_networks = &self.2;
 
         let fix_key = |pk: &DescriptorPublicKey| {
@@ -177,7 +177,7 @@ impl ToWalletDescriptor for DescriptorTemplateOut {
 
                 Ok(pk)
             } else {
-                Err(KeyError::InvalidNetwork)
+                Err(DescriptorError::Key(KeyError::InvalidNetwork))
             }
         };
 
@@ -188,6 +188,22 @@ impl ToWalletDescriptor for DescriptorTemplateOut {
     }
 }
 
+#[doc(hidden)]
+/// Used internally mainly by the `descriptor!()` and `fragment!()` macros
+pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
+    fn check_minsicript(&self) -> Result<(), miniscript::Error>;
+}
+
+impl<Ctx: miniscript::ScriptContext, Pk: miniscript::MiniscriptKey> CheckMiniscript<Ctx>
+    for miniscript::Miniscript<Pk, Ctx>
+{
+    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`]
@@ -195,7 +211,7 @@ pub trait ExtractPolicy {
         &self,
         signers: &SignersContainer,
         secp: &SecpCtx,
-    ) -> Result<Option<Policy>, Error>;
+    ) -> Result<Option<Policy>, DescriptorError>;
 }
 
 pub(crate) trait XKeyUtils {
@@ -235,8 +251,8 @@ impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
 
 pub(crate) trait DescriptorMeta: Sized {
     fn is_witness(&self) -> bool;
-    fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error>;
-    fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, Error>;
+    fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, DescriptorError>;
+    fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError>;
     fn is_fixed(&self) -> bool;
     fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self>;
     fn derive_from_psbt_input(
@@ -297,11 +313,11 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
         }
     }
 
-    fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error> {
+    fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, DescriptorError> {
         let translate_key = |key: &DescriptorPublicKey,
                              index: u32,
                              paths: &mut HDKeyPaths|
-         -> Result<DummyKey, Error> {
+         -> Result<DummyKey, DescriptorError> {
             match key {
                 DescriptorPublicKey::SinglePub(_) => {}
                 DescriptorPublicKey::XPub(xpub) => {
@@ -344,10 +360,10 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
         Ok(answer_pk)
     }
 
-    fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, Error> {
+    fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError> {
         let get_key = |key: &DescriptorPublicKey,
                        keys: &mut Vec<DescriptorXKey<ExtendedPubKey>>|
-         -> Result<DummyKey, Error> {
+         -> Result<DummyKey, DescriptorError> {
             if let DescriptorPublicKey::XPub(xpub) = key {
                 keys.push(xpub.clone())
             }
@@ -369,7 +385,10 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
     }
 
     fn is_fixed(&self) -> bool {
-        fn check_key(key: &DescriptorPublicKey, flag: &mut bool) -> Result<DummyKey, Error> {
+        fn check_key(
+            key: &DescriptorPublicKey,
+            flag: &mut bool,
+        ) -> Result<DummyKey, DescriptorError> {
             match key {
                 DescriptorPublicKey::SinglePub(_) => {}
                 DescriptorPublicKey::XPub(xpub) => {
@@ -398,7 +417,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
         let try_key = |key: &DescriptorPublicKey,
                        index: &HashMap<Fingerprint, DerivationPath>,
                        found_path: &mut Option<ChildNumber>|
-         -> Result<DummyKey, Error> {
+         -> Result<DummyKey, DescriptorError> {
             if found_path.is_some() {
                 // already found a matching path, we are done
                 return Ok(DummyKey::default());
@@ -432,7 +451,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
                     Some(path) if !xpub.is_wildcard && path.is_empty() => {
                         *found_path = Some(ChildNumber::Normal { index: 0 })
                     }
-                    Some(_) => return Err(Error::InvalidHDKeyPath),
+                    Some(_) => return Err(DescriptorError::InvalidHDKeyPath),
                     _ => {}
                 }
             }
@@ -713,11 +732,17 @@ mod test {
 
         let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
             .to_wallet_descriptor(Network::Testnet);
-        assert!(matches!(desc.err(), Some(KeyError::InvalidChecksum)));
+        assert!(matches!(
+            desc.err(),
+            Some(DescriptorError::InvalidDescriptorChecksum)
+        ));
 
         let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
             .to_wallet_descriptor(Network::Testnet);
-        assert!(matches!(desc.err(), Some(KeyError::InvalidChecksum)));
+        assert!(matches!(
+            desc.err(),
+            Some(DescriptorError::InvalidDescriptorChecksum)
+        ));
     }
 
     // test ToWalletDescriptor trait from &str with keys from right and wrong network
@@ -749,11 +774,17 @@ mod test {
 
         let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
             .to_wallet_descriptor(Network::Bitcoin);
-        assert!(matches!(desc.err(), Some(KeyError::InvalidNetwork)));
+        assert!(matches!(
+            desc.err(),
+            Some(DescriptorError::Key(KeyError::InvalidNetwork))
+        ));
 
         let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
             .to_wallet_descriptor(Network::Bitcoin);
-        assert!(matches!(desc.err(), Some(KeyError::InvalidNetwork)));
+        assert!(matches!(
+            desc.err(),
+            Some(DescriptorError::Key(KeyError::InvalidNetwork))
+        ));
     }
 
     // test ToWalletDescriptor trait from the output of the descriptor!() macro
index 789e69e920fa8544f11e465714b88257ecc0e37c..d7380907d9159f88eeeb316eebadba24a61bed7c 100644 (file)
@@ -1145,11 +1145,12 @@ mod test {
         let (prvkey0, _pubkey0, _fingerprint0) = setup_keys(TPRV0_STR);
         let (_prvkey1, pubkey1, _fingerprint1) = setup_keys(TPRV1_STR);
         let sequence = 50;
+        #[rustfmt::skip]
         let desc = descriptor!(wsh(thresh(
             2,
             pk(prvkey0),
-            s: pk(pubkey1),
-            s: d: v: older(sequence)
+            s:pk(pubkey1),
+            s:d:v:older(sequence)
         )))
         .unwrap();
 
index 6a14e919b28adfa31c9b764fe2f3c3872655a019..bc34e7075129fa18957d93aec9a055538fb4651b 100644 (file)
@@ -33,7 +33,8 @@ use bitcoin::Network;
 use miniscript::{Legacy, Segwitv0};
 
 use super::{ExtendedDescriptor, KeyMap, ToWalletDescriptor};
-use crate::keys::{DerivableKey, KeyError, ToDescriptorKey, ValidNetworks};
+use crate::descriptor::DescriptorError;
+use crate::keys::{DerivableKey, ToDescriptorKey, ValidNetworks};
 use crate::{descriptor, KeychainKind};
 
 /// Type alias for the return type of [`DescriptorTemplate`], [`descriptor!`](crate::descriptor!) and others
@@ -47,6 +48,7 @@ pub type DescriptorTemplateOut = (ExtendedDescriptor, KeyMap, ValidNetworks);
 /// ## Example
 ///
 /// ```
+/// use bdk::descriptor::error::Error as DescriptorError;
 /// use bdk::keys::{KeyError, ToDescriptorKey};
 /// use bdk::miniscript::Legacy;
 /// use bdk::template::{DescriptorTemplate, DescriptorTemplateOut};
@@ -54,14 +56,14 @@ pub type DescriptorTemplateOut = (ExtendedDescriptor, KeyMap, ValidNetworks);
 /// struct MyP2PKH<K: ToDescriptorKey<Legacy>>(K);
 ///
 /// impl<K: ToDescriptorKey<Legacy>> DescriptorTemplate for MyP2PKH<K> {
-///     fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+///     fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
 ///         Ok(bdk::descriptor!(pkh(self.0))?)
 ///     }
 /// }
 /// ```
 pub trait DescriptorTemplate {
     /// Build the complete descriptor
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError>;
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError>;
 }
 
 /// Turns a [`DescriptorTemplate`] into a valid wallet descriptor by calling its
@@ -70,7 +72,7 @@ impl<T: DescriptorTemplate> ToWalletDescriptor for T {
     fn to_wallet_descriptor(
         self,
         network: Network,
-    ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
+    ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         Ok(self.build()?.to_wallet_descriptor(network)?)
     }
 }
@@ -103,7 +105,7 @@ impl<T: DescriptorTemplate> ToWalletDescriptor for T {
 pub struct P2PKH<K: ToDescriptorKey<Legacy>>(pub K);
 
 impl<K: ToDescriptorKey<Legacy>> DescriptorTemplate for P2PKH<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(descriptor!(pkh(self.0))?)
     }
 }
@@ -137,7 +139,7 @@ impl<K: ToDescriptorKey<Legacy>> DescriptorTemplate for P2PKH<K> {
 pub struct P2WPKH_P2SH<K: ToDescriptorKey<Segwitv0>>(pub K);
 
 impl<K: ToDescriptorKey<Segwitv0>> DescriptorTemplate for P2WPKH_P2SH<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(descriptor!(sh(wpkh(self.0)))?)
     }
 }
@@ -170,7 +172,7 @@ impl<K: ToDescriptorKey<Segwitv0>> DescriptorTemplate for P2WPKH_P2SH<K> {
 pub struct P2WPKH<K: ToDescriptorKey<Segwitv0>>(pub K);
 
 impl<K: ToDescriptorKey<Segwitv0>> DescriptorTemplate for P2WPKH<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(descriptor!(wpkh(self.0))?)
     }
 }
@@ -205,7 +207,7 @@ impl<K: ToDescriptorKey<Segwitv0>> DescriptorTemplate for P2WPKH<K> {
 pub struct BIP44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
 
 impl<K: DerivableKey<Legacy>> DescriptorTemplate for BIP44<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(P2PKH(legacy::make_bipxx_private(44, self.0, self.1)?).build()?)
     }
 }
@@ -244,7 +246,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for BIP44<K> {
 pub struct BIP44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
 
 impl<K: DerivableKey<Legacy>> DescriptorTemplate for BIP44Public<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(P2PKH(legacy::make_bipxx_public(44, self.0, self.1, self.2)?).build()?)
     }
 }
@@ -279,7 +281,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for BIP44Public<K> {
 pub struct BIP49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
 
 impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP49<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(P2WPKH_P2SH(segwit_v0::make_bipxx_private(49, self.0, self.1)?).build()?)
     }
 }
@@ -318,7 +320,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP49<K> {
 pub struct BIP49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
 
 impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP49Public<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(P2WPKH_P2SH(segwit_v0::make_bipxx_public(49, self.0, self.1, self.2)?).build()?)
     }
 }
@@ -353,7 +355,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP49Public<K> {
 pub struct BIP84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
 
 impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP84<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(P2WPKH(segwit_v0::make_bipxx_private(84, self.0, self.1)?).build()?)
     }
 }
@@ -392,7 +394,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP84<K> {
 pub struct BIP84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
 
 impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for BIP84Public<K> {
-    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
+    fn build(self) -> Result<DescriptorTemplateOut, DescriptorError> {
         Ok(P2WPKH(segwit_v0::make_bipxx_public(84, self.0, self.1, self.2)?).build()?)
     }
 }
@@ -406,7 +408,7 @@ macro_rules! expand_make_bipxx {
                 bip: u32,
                 key: K,
                 keychain: KeychainKind,
-            ) -> Result<impl ToDescriptorKey<$ctx>, KeyError> {
+            ) -> Result<impl ToDescriptorKey<$ctx>, DescriptorError> {
                 let mut derivation_path = Vec::with_capacity(4);
                 derivation_path.push(bip32::ChildNumber::from_hardened_idx(bip)?);
                 derivation_path.push(bip32::ChildNumber::from_hardened_idx(0)?);
@@ -430,7 +432,7 @@ macro_rules! expand_make_bipxx {
                 key: K,
                 parent_fingerprint: bip32::Fingerprint,
                 keychain: KeychainKind,
-            ) -> Result<impl ToDescriptorKey<$ctx>, KeyError> {
+            ) -> Result<impl ToDescriptorKey<$ctx>, DescriptorError> {
                 let derivation_path: bip32::DerivationPath = match keychain {
                     KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
                     KeychainKind::Internal => vec![bip32::ChildNumber::from_normal_idx(1)?].into(),
@@ -456,8 +458,8 @@ mod test {
     // test existing descriptor templates, make sure they are expanded to the right descriptors
 
     use super::*;
-    use crate::descriptor::DescriptorMeta;
-    use crate::keys::{KeyError, ValidNetworks};
+    use crate::descriptor::{DescriptorError, DescriptorMeta};
+    use crate::keys::ValidNetworks;
     use bitcoin::hashes::core::str::FromStr;
     use bitcoin::network::constants::Network::Regtest;
     use bitcoin::secp256k1::Secp256k1;
@@ -467,7 +469,7 @@ mod test {
 
     // verify template descriptor generates expected address(es)
     fn check(
-        desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), KeyError>,
+        desc: Result<(Descriptor<DescriptorPublicKey>, KeyMap, ValidNetworks), DescriptorError>,
         is_witness: bool,
         is_fixed: bool,
         expected: &[&str],
index 7a7fb7f0bb23c5bec7d8cfd6fbfd7e6768ae2a78..68872800cdc5c033c519cb021f396fd16b4d281f 100644 (file)
@@ -43,6 +43,7 @@ use miniscript::descriptor::{DescriptorXKey, KeyMap};
 pub use miniscript::ScriptContext;
 use miniscript::{Miniscript, Terminal};
 
+use crate::descriptor::{CheckMiniscript, DescriptorError};
 use crate::wallet::utils::SecpCtx;
 
 #[cfg(feature = "keys-bip39")]
@@ -572,14 +573,13 @@ fn expand_multi_keys<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
 pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
     descriptor_key: Pk,
     secp: &SecpCtx,
-) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
+) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), DescriptorError> {
     let (key, key_map, valid_networks) = descriptor_key.to_descriptor_key()?.extract(secp)?;
+    let minisc = Miniscript::from_ast(Terminal::PkK(key))?;
 
-    Ok((
-        Miniscript::from_ast(Terminal::PkK(key))?,
-        key_map,
-        valid_networks,
-    ))
+    minisc.check_minsicript()?;
+
+    Ok((minisc, key_map, valid_networks))
 }
 
 // Used internally by `bdk::fragment!` to build `multi()` fragments
@@ -588,14 +588,13 @@ pub fn make_multi<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
     thresh: usize,
     pks: Vec<Pk>,
     secp: &SecpCtx,
-) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
+) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), DescriptorError> {
     let (pks, key_map, valid_networks) = expand_multi_keys(pks, secp)?;
+    let minisc = Miniscript::from_ast(Terminal::Multi(thresh, pks))?;
+
+    minisc.check_minsicript()?;
 
-    Ok((
-        Miniscript::from_ast(Terminal::Multi(thresh, pks))?,
-        key_map,
-        valid_networks,
-    ))
+    Ok((minisc, key_map, valid_networks))
 }
 
 // Used internally by `bdk::descriptor!` to build `sortedmulti()` fragments
@@ -610,11 +609,14 @@ pub fn make_sortedmulti_inner<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
         KeyMap,
         ValidNetworks,
     ),
-    KeyError,
+    DescriptorError,
 > {
     let (pks, key_map, valid_networks) = expand_multi_keys(pks, secp)?;
+    let minisc = SortedMultiVec::new(thresh, pks)?;
+
+    // TODO: should we apply the checks here as well?
 
-    Ok((SortedMultiVec::new(thresh, pks)?, key_map, valid_networks))
+    Ok((minisc, key_map, valid_networks))
 }
 
 /// The "identity" conversion is used internally by some `bdk::fragment`s