Merge branch 'release/0.4.0'

This commit is contained in:
Steve Myers 2021-02-17 18:33:11 -08:00
commit 5e352489a0
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
10 changed files with 128 additions and 32 deletions

View File

@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [v0.4.0] - [v0.3.0]
### Keys
#### Changed
- Renamed `DerivableKey::add_metadata()` to `DerivableKey::into_descriptor_key()`
@ -16,13 +18,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Misc
#### Removed
- Removed the `parse_descriptor` example, since it wasn't demostrating any bdk-specific API anymore.
- Removed the `parse_descriptor` example, since it wasn't demonstrating any bdk-specific API anymore.
#### Changed
- Updated `bitcoin` to `0.26`, `miniscript` to `5.1` and `electrum-client` to `0.6`
#### Added
- Added support for the `signet` network (issue #62)
#### Added
- Added a function to get the version of BDK at runtime
### Wallet
@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Policy
#### Changed
- Removed unneeded `Result<(), PolicyError>` return type for `Satisfaction::finalize()`
- Removed the `TooManyItemsSelected` policy error (see commit message for more details)
## [v0.3.0] - [v0.2.0]
@ -269,7 +270,8 @@ final transaction is created by calling `finish` on the builder.
- Use `MemoryDatabase` in the compiler example
- Make the REPL return JSON
[unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.2.0...HEAD
[unreleased]: https://github.com/bitcoindevkit/bdk/compare/v0.4.0...HEAD
[0.1.0-beta.1]: https://github.com/bitcoindevkit/bdk/compare/96c87ea5...0.1.0-beta.1
[v0.2.0]: https://github.com/bitcoindevkit/bdk/compare/0.1.0-beta.1...v0.2.0
[v0.3.0]: https://github.com/bitcoindevkit/bdk/compare/v0.2.0...v0.3.0
[v0.4.0]: https://github.com/bitcoindevkit/bdk/compare/v0.3.0...v0.4.0

View File

@ -1,6 +1,6 @@
[package]
name = "bdk"
version = "0.3.1-dev"
version = "0.4.1-dev"
edition = "2018"
authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccardo@casatta.it>"]
homepage = "https://bitcoindevkit.org"
@ -12,7 +12,7 @@ readme = "README.md"
license = "MIT"
[dependencies]
bdk-macros = { path = "./macros" }
bdk-macros = "^0.3"
log = "^0.4"
miniscript = "5.1"
bitcoin = { version = "^0.26", features = ["use-serde"] }
@ -59,8 +59,8 @@ test-electrum = ["electrum"]
test-md-docs = ["electrum"]
[dev-dependencies]
bdk-testutils = { path = "./testutils" }
bdk-testutils-macros = { path = "./testutils-macros" }
bdk-testutils = "^0.3"
bdk-testutils-macros = "^0.3"
serial_test = "0.4"
lazy_static = "1.4"
env_logger = "0.7"

View File

@ -1,6 +1,6 @@
[package]
name = "bdk-macros"
version = "0.2.0"
version = "0.3.0"
authors = ["Alekos Filini <alekos.filini@gmail.com>"]
edition = "2018"
homepage = "https://bitcoindevkit.org"

View File

@ -31,6 +31,8 @@ pub enum Error {
InvalidHDKeyPath,
/// The provided descriptor doesn't match its checksum
InvalidDescriptorChecksum,
/// The descriptor contains hardened derivation steps on public extended keys
HardenedDerivationXpub,
/// Error thrown while working with [`keys`](crate::keys)
Key(crate::keys::KeyError),

View File

@ -198,6 +198,36 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
}
}
/// Wrapper for `IntoWalletDescriptor` that performs additional checks on the keys contained in the
/// descriptor
pub(crate) fn into_wallet_descriptor_checked<T: IntoWalletDescriptor>(
inner: T,
secp: &SecpCtx,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
let (descriptor, keymap) = inner.into_wallet_descriptor(secp, network)?;
// Ensure the keys don't contain any hardened derivation steps or hardened wildcards
let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| {
if let DescriptorPublicKey::XPub(DescriptorXKey {
derivation_path,
wildcard,
..
}) = k.as_key()
{
return *wildcard == Wildcard::Hardened
|| derivation_path.into_iter().any(ChildNumber::is_hardened);
}
false
});
if descriptor_contains_hardened_steps {
return Err(DescriptorError::HardenedDerivationXpub);
}
Ok((descriptor, keymap))
}
#[doc(hidden)]
/// Used internally mainly by the `descriptor!()` and `fragment!()` macros
pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
@ -740,4 +770,18 @@ mod test {
.unwrap();
assert_eq!(wallet_desc, wallet_desc2)
}
#[test]
fn test_into_wallet_descriptor_checked() {
let secp = Secp256k1::new();
let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
assert!(result.is_err());
assert!(matches!(
result.unwrap_err(),
DescriptorError::HardenedDerivationXpub
));
}
}

View File

@ -47,7 +47,7 @@
//! # Ok::<(), bdk::Error>(())
//! ```
use std::cmp::{max, Ordering};
use std::cmp::max;
use std::collections::{BTreeMap, HashSet, VecDeque};
use std::fmt;
@ -506,13 +506,11 @@ impl Condition {
}
/// Errors that can happen while extracting and manipulating policies
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq)]
pub enum PolicyError {
/// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`]
/// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`] or a [`SatisfiableItem::Multisig`]
NotEnoughItemsSelected(String),
/// Too many items are selected to satisfy a [`SatisfiableItem::Thresh`]
TooManyItemsSelected(String),
/// Index out of range for an item to satisfy a [`SatisfiableItem::Thresh`]
/// Index out of range for an item to satisfy a [`SatisfiableItem::Thresh`] or a [`SatisfiableItem::Multisig`]
IndexOutOfRange(usize),
/// Can not add to an item that is [`Satisfaction::None`] or [`Satisfaction::Complete`]
AddOnLeaf,
@ -644,10 +642,10 @@ impl Policy {
SatisfiableItem::Thresh { items, threshold } if items.len() == *threshold => {
(0..*threshold).collect()
}
SatisfiableItem::Multisig { keys, .. } => (0..keys.len()).collect(),
_ => vec![],
};
let selected = match path.get(&self.id) {
_ if !default.is_empty() => &default,
Some(arr) => arr,
_ => &default,
};
@ -668,14 +666,8 @@ impl Policy {
// if we have something, make sure we have enough items. note that the user can set
// an empty value for this step in case of n-of-n, because `selected` is set to all
// the elements above
match selected.len().cmp(threshold) {
Ordering::Less => {
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()))
}
Ordering::Greater => {
return Err(PolicyError::TooManyItemsSelected(self.id.clone()))
}
Ordering::Equal => (),
if selected.len() < *threshold {
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
}
// check the selected items, see if there are conflicting requirements
@ -690,7 +682,16 @@ impl Policy {
Ok(requirements)
}
_ if !selected.is_empty() => Err(PolicyError::TooManyItemsSelected(self.id.clone())),
SatisfiableItem::Multisig { keys, threshold } => {
if selected.len() < *threshold {
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
}
if let Some(item) = selected.iter().find(|i| **i >= keys.len()) {
return Err(PolicyError::IndexOutOfRange(*item));
}
Ok(Condition::default())
}
SatisfiableItem::AbsoluteTimelock { value } => Ok(Condition {
csv: None,
timelock: Some(*value),
@ -1257,4 +1258,50 @@ mod test {
//
// // TODO how should this merge timelocks?
// }
#[test]
fn test_get_condition_multisig() {
let secp = Secp256k1::gen_new();
let (_, pk0, _) = setup_keys(TPRV0_STR);
let (_, pk1, _) = setup_keys(TPRV1_STR);
let desc = descriptor!(wsh(multi(1, pk0, pk1))).unwrap();
let (wallet_desc, keymap) = desc
.into_wallet_descriptor(&secp, Network::Testnet)
.unwrap();
let signers = keymap.into();
let policy = wallet_desc
.extract_policy(&signers, &secp)
.unwrap()
.unwrap();
// no args, choose the default
let no_args = policy.get_condition(&vec![].into_iter().collect());
assert_eq!(no_args, Ok(Condition::default()));
// enough args
let eq_thresh =
policy.get_condition(&vec![(policy.id.clone(), vec![0])].into_iter().collect());
assert_eq!(eq_thresh, Ok(Condition::default()));
// more args, it doesn't really change anything
let gt_thresh =
policy.get_condition(&vec![(policy.id.clone(), vec![0, 1])].into_iter().collect());
assert_eq!(gt_thresh, Ok(Condition::default()));
// not enough args, error
let lt_thresh =
policy.get_condition(&vec![(policy.id.clone(), vec![])].into_iter().collect());
assert_eq!(
lt_thresh,
Err(PolicyError::NotEnoughItemsSelected(policy.id.clone()))
);
// index out of range
let out_of_range =
policy.get_condition(&vec![(policy.id.clone(), vec![5])].into_iter().collect());
assert_eq!(out_of_range, Err(PolicyError::IndexOutOfRange(5)));
}
}

View File

@ -56,7 +56,7 @@
//! interact with the bitcoin P2P network.
//!
//! ```toml
//! bdk = "0.3.0"
//! bdk = "0.4.0"
//! ```
//!
//! ## Sync the balance of a descriptor

View File

@ -66,8 +66,9 @@ use crate::blockchain::{Blockchain, Progress};
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::descriptor::derived::AsDerived;
use crate::descriptor::{
get_checksum, DerivedDescriptor, DerivedDescriptorMeta, DescriptorMeta, DescriptorScripts,
ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
get_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DerivedDescriptorMeta,
DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor,
Policy, XKeyUtils,
};
use crate::error::Error;
use crate::psbt::PSBTUtils;
@ -134,7 +135,7 @@ where
) -> Result<Self, Error> {
let secp = Secp256k1::new();
let (descriptor, keymap) = descriptor.into_wallet_descriptor(&secp, network)?;
let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)?;
database.check_descriptor_checksum(
KeychainKind::External,
get_checksum(&descriptor.to_string())?.as_bytes(),
@ -143,7 +144,7 @@ where
let (change_descriptor, change_signers) = match change_descriptor {
Some(desc) => {
let (change_descriptor, change_keymap) =
desc.into_wallet_descriptor(&secp, network)?;
into_wallet_descriptor_checked(desc, &secp, network)?;
database.check_descriptor_checksum(
KeychainKind::Internal,
get_checksum(&change_descriptor.to_string())?.as_bytes(),

View File

@ -1,6 +1,6 @@
[package]
name = "bdk-testutils-macros"
version = "0.2.0"
version = "0.3.0"
authors = ["Alekos Filini <alekos.filini@gmail.com>"]
edition = "2018"
homepage = "https://bitcoindevkit.org"

View File

@ -1,6 +1,6 @@
[package]
name = "bdk-testutils"
version = "0.2.0"
version = "0.3.0"
authors = ["Alekos Filini <alekos.filini@gmail.com>"]
edition = "2018"
homepage = "https://bitcoindevkit.org"