Merge branch 'release/0.4.0'
This commit is contained in:
commit
5e352489a0
10
CHANGELOG.md
10
CHANGELOG.md
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user