diff --git a/CHANGELOG.md b/CHANGELOG.md index d410360c..6a3f63f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/database/any.rs b/src/database/any.rs index 64dd4772..021ab7d6 100644 --- a/src/database/any.rs +++ b/src/database/any.rs @@ -36,14 +36,12 @@ //! # 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>(()) //! ``` diff --git a/src/descriptor/checksum.rs b/src/descriptor/checksum.rs index 04b22601..08ebf3bf 100644 --- a/src/descriptor/checksum.rs +++ b/src/descriptor/checksum.rs @@ -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 { +pub fn get_checksum(desc: &str) -> Result { 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 )); } } diff --git a/src/descriptor/dsl.rs b/src/descriptor/dsl.rs index 7cf83b62..caf2eaa0 100644 --- a/src/descriptor/dsl.rs +++ b/src/descriptor/dsl.rs @@ -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::, _>>() + .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, KeyMap, ValidNetworks), Error>`. It allows writing +/// This macro will be expanded to an object of type `Result<(Miniscript, 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::, _>>() + .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, KeyMap, ValidNetworks), KeyError>, + desc: Result<(Descriptor, 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(); + // } } diff --git a/src/descriptor/error.rs b/src/descriptor/error.rs index e470fea9..495f999c 100644 --- a/src/descriptor/error.rs +++ b/src/descriptor/error.rs @@ -27,26 +27,19 @@ /// Errors related to the parsing and usage of descriptors #[derive(Debug)] pub enum Error { - //InternalError, - //InvalidPrefix(Vec), - //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 diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 97c22839..cffb1cb4 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -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 { + 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`] @@ -195,7 +211,7 @@ pub trait ExtractPolicy { &self, signers: &SignersContainer, secp: &SecpCtx, - ) -> Result, Error>; + ) -> Result, DescriptorError>; } pub(crate) trait XKeyUtils { @@ -235,8 +251,8 @@ impl XKeyUtils for DescriptorXKey { pub(crate) trait DescriptorMeta: Sized { fn is_witness(&self) -> bool; - fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result; - fn get_extended_keys(&self) -> Result>, Error>; + fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result; + fn get_extended_keys(&self) -> Result>, DescriptorError>; fn is_fixed(&self) -> bool; fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option; fn derive_from_psbt_input( @@ -297,11 +313,11 @@ impl DescriptorMeta for Descriptor { } } - fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result { + fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result { let translate_key = |key: &DescriptorPublicKey, index: u32, paths: &mut HDKeyPaths| - -> Result { + -> Result { match key { DescriptorPublicKey::SinglePub(_) => {} DescriptorPublicKey::XPub(xpub) => { @@ -344,10 +360,10 @@ impl DescriptorMeta for Descriptor { Ok(answer_pk) } - fn get_extended_keys(&self) -> Result>, Error> { + fn get_extended_keys(&self) -> Result>, DescriptorError> { let get_key = |key: &DescriptorPublicKey, keys: &mut Vec>| - -> Result { + -> Result { if let DescriptorPublicKey::XPub(xpub) = key { keys.push(xpub.clone()) } @@ -369,7 +385,10 @@ impl DescriptorMeta for Descriptor { } fn is_fixed(&self) -> bool { - fn check_key(key: &DescriptorPublicKey, flag: &mut bool) -> Result { + fn check_key( + key: &DescriptorPublicKey, + flag: &mut bool, + ) -> Result { match key { DescriptorPublicKey::SinglePub(_) => {} DescriptorPublicKey::XPub(xpub) => { @@ -398,7 +417,7 @@ impl DescriptorMeta for Descriptor { let try_key = |key: &DescriptorPublicKey, index: &HashMap, found_path: &mut Option| - -> Result { + -> Result { 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 { 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 diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 789e69e9..d7380907 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -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(); diff --git a/src/descriptor/template.rs b/src/descriptor/template.rs index 6a14e919..bc34e707 100644 --- a/src/descriptor/template.rs +++ b/src/descriptor/template.rs @@ -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); /// /// impl> DescriptorTemplate for MyP2PKH { -/// fn build(self) -> Result { +/// fn build(self) -> Result { /// Ok(bdk::descriptor!(pkh(self.0))?) /// } /// } /// ``` pub trait DescriptorTemplate { /// Build the complete descriptor - fn build(self) -> Result; + fn build(self) -> Result; } /// Turns a [`DescriptorTemplate`] into a valid wallet descriptor by calling its @@ -70,7 +72,7 @@ impl 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 ToWalletDescriptor for T { pub struct P2PKH>(pub K); impl> DescriptorTemplate for P2PKH { - fn build(self) -> Result { + fn build(self) -> Result { Ok(descriptor!(pkh(self.0))?) } } @@ -137,7 +139,7 @@ impl> DescriptorTemplate for P2PKH { pub struct P2WPKH_P2SH>(pub K); impl> DescriptorTemplate for P2WPKH_P2SH { - fn build(self) -> Result { + fn build(self) -> Result { Ok(descriptor!(sh(wpkh(self.0)))?) } } @@ -170,7 +172,7 @@ impl> DescriptorTemplate for P2WPKH_P2SH { pub struct P2WPKH>(pub K); impl> DescriptorTemplate for P2WPKH { - fn build(self) -> Result { + fn build(self) -> Result { Ok(descriptor!(wpkh(self.0))?) } } @@ -205,7 +207,7 @@ impl> DescriptorTemplate for P2WPKH { pub struct BIP44>(pub K, pub KeychainKind); impl> DescriptorTemplate for BIP44 { - fn build(self) -> Result { + fn build(self) -> Result { Ok(P2PKH(legacy::make_bipxx_private(44, self.0, self.1)?).build()?) } } @@ -244,7 +246,7 @@ impl> DescriptorTemplate for BIP44 { pub struct BIP44Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for BIP44Public { - fn build(self) -> Result { + fn build(self) -> Result { Ok(P2PKH(legacy::make_bipxx_public(44, self.0, self.1, self.2)?).build()?) } } @@ -279,7 +281,7 @@ impl> DescriptorTemplate for BIP44Public { pub struct BIP49>(pub K, pub KeychainKind); impl> DescriptorTemplate for BIP49 { - fn build(self) -> Result { + fn build(self) -> Result { Ok(P2WPKH_P2SH(segwit_v0::make_bipxx_private(49, self.0, self.1)?).build()?) } } @@ -318,7 +320,7 @@ impl> DescriptorTemplate for BIP49 { pub struct BIP49Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for BIP49Public { - fn build(self) -> Result { + fn build(self) -> Result { Ok(P2WPKH_P2SH(segwit_v0::make_bipxx_public(49, self.0, self.1, self.2)?).build()?) } } @@ -353,7 +355,7 @@ impl> DescriptorTemplate for BIP49Public { pub struct BIP84>(pub K, pub KeychainKind); impl> DescriptorTemplate for BIP84 { - fn build(self) -> Result { + fn build(self) -> Result { Ok(P2WPKH(segwit_v0::make_bipxx_private(84, self.0, self.1)?).build()?) } } @@ -392,7 +394,7 @@ impl> DescriptorTemplate for BIP84 { pub struct BIP84Public>(pub K, pub bip32::Fingerprint, pub KeychainKind); impl> DescriptorTemplate for BIP84Public { - fn build(self) -> Result { + fn build(self) -> Result { 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, KeyError> { + ) -> Result, 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, KeyError> { + ) -> Result, 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, KeyMap, ValidNetworks), KeyError>, + desc: Result<(Descriptor, KeyMap, ValidNetworks), DescriptorError>, is_witness: bool, is_fixed: bool, expected: &[&str], diff --git a/src/keys/mod.rs b/src/keys/mod.rs index 7a7fb7f0..68872800 100644 --- a/src/keys/mod.rs +++ b/src/keys/mod.rs @@ -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, Ctx: ScriptContext>( pub fn make_pk, Ctx: ScriptContext>( descriptor_key: Pk, secp: &SecpCtx, -) -> Result<(Miniscript, KeyMap, ValidNetworks), KeyError> { +) -> Result<(Miniscript, 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, Ctx: ScriptContext>( thresh: usize, pks: Vec, secp: &SecpCtx, -) -> Result<(Miniscript, KeyMap, ValidNetworks), KeyError> { +) -> Result<(Miniscript, KeyMap, ValidNetworks), DescriptorError> { let (pks, key_map, valid_networks) = expand_multi_keys(pks, secp)?; + let minisc = Miniscript::from_ast(Terminal::Multi(thresh, pks))?; - Ok(( - Miniscript::from_ast(Terminal::Multi(thresh, pks))?, - key_map, - valid_networks, - )) + minisc.check_minsicript()?; + + Ok((minisc, key_map, valid_networks)) } // Used internally by `bdk::descriptor!` to build `sortedmulti()` fragments @@ -610,11 +609,14 @@ pub fn make_sortedmulti_inner, Ctx: ScriptContext>( KeyMap, ValidNetworks, ), - KeyError, + DescriptorError, > { let (pks, key_map, valid_networks) = expand_multi_keys(pks, secp)?; + let minisc = SortedMultiVec::new(thresh, pks)?; - Ok((SortedMultiVec::new(thresh, pks)?, key_map, valid_networks)) + // TODO: should we apply the checks here as well? + + Ok((minisc, key_map, valid_networks)) } /// The "identity" conversion is used internally by some `bdk::fragment`s