Merge commit 'refs/pull/264/head' of github.com:bitcoindevkit/bdk

This commit is contained in:
Alekos Filini 2021-01-12 14:02:41 +01:00
commit 6955a7776d
No known key found for this signature in database
GPG Key ID: 431401E4A4530061
9 changed files with 190 additions and 106 deletions

View 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

View File

@ -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>(())
//! ```

View 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
));
}
}

View 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();
// }
}

View File

@ -27,26 +27,19 @@
/// 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

View 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

View 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();

View 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],

View 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))?;
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<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)?;
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