From 6611ef0e5fdb85a6889468cee441838a8da0eeea Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Thu, 11 Feb 2021 09:27:34 -0500 Subject: [PATCH 01/15] Bump version to 0.4.0-rc.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5abcc0f0..27dfc19f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk" -version = "0.3.1-dev" +version = "0.4.0-rc.1" edition = "2018" authors = ["Alekos Filini ", "Riccardo Casatta "] homepage = "https://bitcoindevkit.org" From 02b9eda6fa30aede5866edf55addd93982f74bde Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Thu, 11 Feb 2021 09:29:27 -0500 Subject: [PATCH 02/15] Update CHANGELOG for release v0.4.0 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3717708a..ae94a8b9 100644 --- a/CHANGELOG.md +++ b/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()` @@ -267,3 +269,4 @@ final transaction is created by calling `finish` on the builder. [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 From dccf09861c1a4a5f039b24ab088e85fd6e9789b0 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Thu, 11 Feb 2021 09:29:44 -0500 Subject: [PATCH 03/15] Update version in the examples --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 197613af..df6cf7c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 From fa2610538f9fd964f57afe000af0264b40c7b2e8 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Sat, 13 Feb 2021 10:58:26 -0500 Subject: [PATCH 04/15] [policy] Remove the `TooManyItemsSelected` error The `TooManyItemsSelected` error has been removed, since it's not technically an error but potentailly more of an "over-constraint" over a tx: for instance, given a `thresh(3,pk(a),pk(b),older(10),older(20))` descriptor one could create a spending tx with the `[0,1,2]` items that would only be spendable after `10` blocks, or a tx with the `[0,2,3]` items that would be spendable after `20`. In this case specifying more items than the threshold would create a tx with the maximum constraint possible, in this case the `20` blocks. This is not necessarily an error, so we should allow it without failing. --- CHANGELOG.md | 1 + src/descriptor/policy.rs | 14 +++----------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b958d62..b683d1cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,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] diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 54ba808b..40d8efb3 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -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; @@ -510,8 +510,6 @@ impl Condition { pub enum PolicyError { /// Not enough items are selected to satisfy a [`SatisfiableItem::Thresh`] 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`] IndexOutOfRange(usize), /// Can not add to an item that is [`Satisfaction::None`] or [`Satisfaction::Complete`] @@ -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 From b61427c07bd5c85f865213be4c38224a3898ccdf Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Sat, 13 Feb 2021 11:00:03 -0500 Subject: [PATCH 05/15] [policy] Allow specifying a policy path for `Multisig` While technically it's not required since there are no timelocks inside, it's still less confusing for the end user if we allow this instead of failing like we do currently. --- src/descriptor/policy.rs | 65 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/descriptor/policy.rs b/src/descriptor/policy.rs index 40d8efb3..8929e085 100644 --- a/src/descriptor/policy.rs +++ b/src/descriptor/policy.rs @@ -506,11 +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), - /// 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, @@ -642,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, }; @@ -682,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), @@ -1249,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))); + } } From 6092c6e7890438117bc065b0220bbaf872ba04f0 Mon Sep 17 00:00:00 2001 From: Lloyd Fournier Date: Tue, 16 Feb 2021 16:31:55 +1100 Subject: [PATCH 06/15] Don't fix tokio minor version This is also what they give as an example in their docs: https://docs.rs/tokio/1.2.0/tokio/ --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 27dfc19f..65f60adf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ tiny-bip39 = { version = "^0.8", optional = true } # Platform-specific dependencies [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -tokio = { version = "1.0", features = ["rt"] } +tokio = { version = "1", features = ["rt"] } [target.'cfg(target_arch = "wasm32")'.dependencies] async-trait = "0.1" From 7843732e17296bb048cafe2aa1146bad70eaab52 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Mon, 15 Feb 2021 11:33:47 -0500 Subject: [PATCH 07/15] [descriptor] Perform additional checks before using a descriptor Fixes #287 --- src/descriptor/error.rs | 2 ++ src/descriptor/mod.rs | 44 +++++++++++++++++++++++++++++++++++++++++ src/wallet/mod.rs | 9 +++++---- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/descriptor/error.rs b/src/descriptor/error.rs index 495f999c..64605061 100644 --- a/src/descriptor/error.rs +++ b/src/descriptor/error.rs @@ -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), diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 10000786..ee4f0629 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -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( + 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 { @@ -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 + )); + } } diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index b2729ec7..8bb254c4 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -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 { 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(), From 3a2b8bdb8568f507eba7a7b8ce5553157b1145db Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 12:04:59 -0800 Subject: [PATCH 08/15] Small CHANGELOG cleanup --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b683d1cf..80bcda66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,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 From 7bbff79d4bf399a3569c6205afb6ff3d0e84509e Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 11:42:59 -0800 Subject: [PATCH 09/15] Bump 'bdk-testutils-macros' version to 0.3.0 --- testutils-macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutils-macros/Cargo.toml b/testutils-macros/Cargo.toml index 5bd8a3db..3766e5bd 100644 --- a/testutils-macros/Cargo.toml +++ b/testutils-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk-testutils-macros" -version = "0.2.0" +version = "0.3.0" authors = ["Alekos Filini "] edition = "2018" homepage = "https://bitcoindevkit.org" From cdf7b33104e7ca1daf2ab2716672214d091fca99 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 11:45:46 -0800 Subject: [PATCH 10/15] Bump 'bdk-testutils' version to 0.3.0 --- testutils/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testutils/Cargo.toml b/testutils/Cargo.toml index 8ce0035d..ee6efe60 100644 --- a/testutils/Cargo.toml +++ b/testutils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk-testutils" -version = "0.2.0" +version = "0.3.0" authors = ["Alekos Filini "] edition = "2018" homepage = "https://bitcoindevkit.org" From fcf5e971a6c31ce117a281d56cd9590cbac5973a Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 11:48:44 -0800 Subject: [PATCH 11/15] Bump 'bdk-macros' version to 0.3.0 --- macros/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/Cargo.toml b/macros/Cargo.toml index c8338cdf..9123bb1f 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk-macros" -version = "0.2.0" +version = "0.3.0" authors = ["Alekos Filini "] edition = "2018" homepage = "https://bitcoindevkit.org" From 3f5513a2d664976f0aed58f1ead733b31af2f6b7 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 11:53:38 -0800 Subject: [PATCH 12/15] Update 'bdk-macros', 'bdk-testutils', 'bdk-testutils-macros' dep versions --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 65f60adf..f34fe3da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" From e3f893dbd1c569709423f6f8d97882dff7677de4 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 11:54:22 -0800 Subject: [PATCH 13/15] Bump version to 0.4.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f34fe3da..62c345b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk" -version = "0.4.0-rc.1" +version = "0.4.0" edition = "2018" authors = ["Alekos Filini ", "Riccardo Casatta "] homepage = "https://bitcoindevkit.org" From 2759231f7b0128d1f09bdcb8e2da8f16c8d8953b Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 16:32:23 -0800 Subject: [PATCH 14/15] Bump version to 0.4.1-dev --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 62c345b9..1624da25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk" -version = "0.4.0" +version = "0.4.1-dev" edition = "2018" authors = ["Alekos Filini ", "Riccardo Casatta "] homepage = "https://bitcoindevkit.org" From 7ee262ef4b5a8492a544a739d5f9d9e8c4d24ec0 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Wed, 17 Feb 2021 16:49:22 -0800 Subject: [PATCH 15/15] Fix CHANGELOG 'Unreleased' link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80bcda66..b2528d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -270,7 +270,7 @@ 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