]> Untitled Git - bdk/commitdiff
Switch to "mainline" rust-miniscript
authorAlekos Filini <alekos.filini@gmail.com>
Mon, 16 Nov 2020 21:07:38 +0000 (22:07 +0100)
committerAlekos Filini <alekos.filini@gmail.com>
Tue, 17 Nov 2020 22:57:28 +0000 (23:57 +0100)
12 files changed:
Cargo.toml
examples/parse_descriptor.rs
src/descriptor/dsl.rs
src/descriptor/mod.rs
src/descriptor/policy.rs
src/descriptor/template.rs
src/keys/mod.rs
src/wallet/export.rs
src/wallet/mod.rs
src/wallet/signer.rs
src/wallet/utils.rs
testutils/src/lib.rs

index ae590c50dadd3d1cbcd3588d21cea6d4c513aaaf..1b36a80ab85a88b681ef4b8b86b5daa0459a1d4e 100644 (file)
@@ -8,7 +8,7 @@ authors = ["Alekos Filini <alekos.filini@gmail.com>", "Riccardo Casatta <riccard
 bdk-macros = { version = "0.1.0-beta.1", path = "./macros" }
 log = "^0.4"
 bitcoin = { version = "0.25", features = ["use-serde"] }
-miniscript = { git = "https://github.com/MagicalBitcoin/rust-miniscript", rev = "dfc53201aa5796b6bf3b5a40581d192abaa84133" }
+miniscript = { git = "https://github.com/rust-bitcoin/rust-miniscript.git", rev = "ca60da2" }
 serde = { version = "^1.0", features = ["derive"] }
 serde_json = { version = "^1.0" }
 rand = "^0.7"
index 7728a82e570b827ba230949a83076d3c7e8172eb..176077d1a08ba0bff220838f2f6a4ed69dce1b2c 100644 (file)
@@ -27,11 +27,15 @@ extern crate serde_json;
 
 use std::sync::Arc;
 
+use bdk::bitcoin::secp256k1::Secp256k1;
 use bdk::bitcoin::util::bip32::ChildNumber;
 use bdk::bitcoin::*;
 use bdk::descriptor::*;
+use bdk::miniscript::DescriptorPublicKeyCtx;
 
 fn main() {
+    let secp = Secp256k1::new();
+
     let desc = "wsh(or_d(\
                     multi(\
                       2,[d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*,tprv8ZgxMBicQKsPduL5QnGihpprdHyypMGi4DhimjtzYemu7se5YQNcZfAPLqXRuGHb5ZX2eTQj62oNqMnyxJ7B7wz54Uzswqw8fFqMVdcmVF7/1/*\
@@ -39,19 +43,18 @@ fn main() {
                     and_v(vc:pk_h(cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy),older(1000))\
                    ))";
 
-    let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc).unwrap();
+    let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(desc).unwrap();
     println!("{:?}", extended_desc);
 
+    let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, ChildNumber::from_normal_idx(42).unwrap());
+
     let signers = Arc::new(key_map.into());
-    let policy = extended_desc.extract_policy(signers).unwrap();
+    let policy = extended_desc.extract_policy(signers, &secp).unwrap();
     println!("policy: {}", serde_json::to_string(&policy).unwrap());
 
-    let derived_desc = extended_desc.derive(ChildNumber::from_normal_idx(42).unwrap());
-    println!("{:?}", derived_desc);
-
-    let addr = derived_desc.address(Network::Testnet).unwrap();
+    let addr = extended_desc.address(Network::Testnet, deriv_ctx).unwrap();
     println!("{}", addr);
 
-    let script = derived_desc.witness_script();
+    let script = extended_desc.witness_script(deriv_ctx);
     println!("{:?}", script);
 }
index 9b7ef1e20a890c4726db1ebabd4970c4a9e2e005..adda9e7b6a1e35e98620fa1a34f9ba1bda3457a9 100644 (file)
@@ -39,9 +39,10 @@ macro_rules! impl_top_level_pk {
     ( $descriptor_variant:ident, $ctx:ty, $key:expr ) => {{
         #[allow(unused_imports)]
         use $crate::keys::{DescriptorKey, ToDescriptorKey};
+        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
 
         $key.to_descriptor_key()
-            .and_then(|key: DescriptorKey<$ctx>| key.extract())
+            .and_then(|key: DescriptorKey<$ctx>| key.extract(&secp))
             .map(|(pk, key_map, valid_networks)| {
                 (
                     $crate::miniscript::Descriptor::<
@@ -314,7 +315,8 @@ macro_rules! fragment {
         $crate::impl_leaf_opcode!(False)
     });
     ( pk_k $key:expr ) => ({
-        $crate::keys::make_pk($key)
+        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
+        $crate::keys::make_pk($key, &secp)
     });
     ( pk $key:expr ) => ({
         $crate::fragment!(+c pk_k $key)
@@ -391,6 +393,7 @@ macro_rules! fragment {
     });
     ( multi $thresh:expr $(, $key:expr )+ ) => ({
         use $crate::keys::ToDescriptorKey;
+        let secp = $crate::bitcoin::secp256k1::Secp256k1::new();
 
         let mut keys = vec![];
         $(
@@ -398,7 +401,7 @@ macro_rules! fragment {
         )*
 
         keys.into_iter().collect::<Result<Vec<_>, _>>()
-            .and_then(|keys| $crate::keys::make_multi($thresh, keys))
+            .and_then(|keys| $crate::keys::make_multi($thresh, keys, &secp))
     });
 
 }
@@ -406,7 +409,8 @@ macro_rules! fragment {
 #[cfg(test)]
 mod test {
     use bitcoin::hashes::hex::ToHex;
-    use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
+    use bitcoin::secp256k1::Secp256k1;
+    use miniscript::descriptor::{DescriptorPublicKey, DescriptorPublicKeyCtx, KeyMap};
     use miniscript::{Descriptor, Legacy, Segwitv0};
 
     use std::str::FromStr;
@@ -426,6 +430,9 @@ mod test {
         is_fixed: bool,
         expected: &[&str],
     ) {
+        let secp = Secp256k1::new();
+        let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, ChildNumber::Normal { index: 0 });
+
         let (desc, _key_map, _networks) = desc.unwrap();
         assert_eq!(desc.is_witness(), is_witness);
         assert_eq!(desc.is_fixed(), is_fixed);
@@ -436,11 +443,11 @@ mod test {
             } else {
                 desc.derive(ChildNumber::from_normal_idx(index).unwrap())
             };
-            let address = child_desc.address(Regtest);
+            let address = child_desc.address(Regtest, deriv_ctx);
             if address.is_some() {
                 assert_eq!(address.unwrap().to_string(), *expected.get(i).unwrap());
             } else {
-                let script = child_desc.script_pubkey();
+                let script = child_desc.script_pubkey(deriv_ctx);
                 assert_eq!(script.to_hex().as_str(), *expected.get(i).unwrap());
             }
         }
@@ -649,6 +656,8 @@ mod test {
     // - verify the key_maps are correctly merged together
     #[test]
     fn test_key_maps_merged() {
+        let secp = Secp256k1::new();
+
         let xprv1 = bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
         let path1 = bip32::DerivationPath::from_str("m/0").unwrap();
         let desc_key1 = (xprv1, path1.clone()).to_descriptor_key().unwrap();
@@ -672,9 +681,9 @@ mod test {
         let desc_key3: DescriptorKey<Segwitv0> =
             (xprv3, path3.clone()).to_descriptor_key().unwrap();
 
-        let (key1, _key_map, _valid_networks) = desc_key1.extract().unwrap();
-        let (key2, _key_map, _valid_networks) = desc_key2.extract().unwrap();
-        let (key3, _key_map, _valid_networks) = desc_key3.extract().unwrap();
+        let (key1, _key_map, _valid_networks) = desc_key1.extract(&secp).unwrap();
+        let (key2, _key_map, _valid_networks) = desc_key2.extract(&secp).unwrap();
+        let (key3, _key_map, _valid_networks) = desc_key3.extract(&secp).unwrap();
         assert_eq!(key_map.get(&key1).unwrap().to_string(), "tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/0/*");
         assert_eq!(key_map.get(&key2).unwrap().to_string(), "tprv8ZgxMBicQKsPegBHHnq7YEgM815dG24M2Jk5RVqipgDxF1HJ1tsnT815X5Fd5FRfMVUs8NZs9XCb6y9an8hRPThnhfwfXJ36intaekySHGF/2147483647'/0/*");
         assert_eq!(key_map.get(&key3).unwrap().to_string(), "tprv8ZgxMBicQKsPdZXrcHNLf5JAJWFAoJ2TrstMRdSKtEggz6PddbuSkvHKM9oKJyFgZV1B7rw8oChspxyYbtmEXYyg1AjfWbL3ho3XHDpHRZf/10/20/30/40/*");
index 430b38773a0f8be2cdb591c3183fb986f1d5c23b..b87124bda459b1c8fe6481457fee4f4eb392d1a1 100644 (file)
@@ -53,6 +53,7 @@ use self::error::Error;
 pub use self::policy::Policy;
 use crate::keys::{KeyError, ToDescriptorKey, ValidNetworks};
 use crate::wallet::signer::SignersContainer;
+use crate::wallet::utils::{descriptor_to_pk_ctx, SecpCtx};
 
 /// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
 pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
@@ -92,7 +93,7 @@ impl ToWalletDescriptor for &str {
             self
         };
 
-        ExtendedDescriptor::parse_secret(descriptor)?.to_wallet_descriptor(network)
+        ExtendedDescriptor::parse_descriptor(descriptor)?.to_wallet_descriptor(network)
     }
 }
 
@@ -121,15 +122,17 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap) {
     ) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
         use crate::keys::DescriptorKey;
 
+        let secp = Secp256k1::new();
+
         let check_key = |pk: &DescriptorPublicKey| {
             let (pk, _, networks) = if self.0.is_witness() {
                 let desciptor_key: DescriptorKey<miniscript::Segwitv0> =
                     pk.clone().to_descriptor_key()?;
-                desciptor_key.extract()?
+                desciptor_key.extract(&secp)?
             } else {
                 let desciptor_key: DescriptorKey<miniscript::Legacy> =
                     pk.clone().to_descriptor_key()?;
-                desciptor_key.extract()?
+                desciptor_key.extract(&secp)?
             };
 
             if networks.contains(&network) {
@@ -185,12 +188,16 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap, ValidNetworks) {
 
 /// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
 pub trait ExtractPolicy {
-    fn extract_policy(&self, signers: Arc<SignersContainer>) -> Result<Option<Policy>, Error>;
+    fn extract_policy(
+        &self,
+        signers: Arc<SignersContainer>,
+        secp: &SecpCtx,
+    ) -> Result<Option<Policy>, Error>;
 }
 
 pub(crate) trait XKeyUtils {
     fn full_path(&self, append: &[ChildNumber]) -> DerivationPath;
-    fn root_fingerprint(&self) -> Fingerprint;
+    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
 }
 
 impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
@@ -215,46 +222,55 @@ impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
         }
     }
 
-    fn root_fingerprint(&self) -> Fingerprint {
+    fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
         match self.origin {
             Some((fingerprint, _)) => fingerprint,
-            None => self.xkey.xkey_fingerprint(),
+            None => self.xkey.xkey_fingerprint(secp),
         }
     }
 }
 
 pub(crate) trait DescriptorMeta: Sized {
     fn is_witness(&self) -> bool;
-    fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error>;
+    fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error>;
     fn is_fixed(&self) -> bool;
-    fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option<Self>;
-    fn derive_from_psbt_input(&self, psbt_input: &psbt::Input, utxo: Option<TxOut>)
-        -> Option<Self>;
+    fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self>;
+    fn derive_from_psbt_input(
+        &self,
+        psbt_input: &psbt::Input,
+        utxo: Option<TxOut>,
+        secp: &SecpCtx,
+    ) -> Option<Self>;
 }
 
 pub(crate) trait DescriptorScripts {
-    fn psbt_redeem_script(&self) -> Option<Script>;
-    fn psbt_witness_script(&self) -> Option<Script>;
+    fn psbt_redeem_script(&self, secp: &SecpCtx) -> Option<Script>;
+    fn psbt_witness_script(&self, secp: &SecpCtx) -> Option<Script>;
 }
 
-impl<T> DescriptorScripts for Descriptor<T>
-where
-    T: miniscript::MiniscriptKey + miniscript::ToPublicKey,
-{
-    fn psbt_redeem_script(&self) -> Option<Script> {
+impl DescriptorScripts for Descriptor<DescriptorPublicKey> {
+    fn psbt_redeem_script(&self, secp: &SecpCtx) -> Option<Script> {
+        let deriv_ctx = descriptor_to_pk_ctx(secp);
+
         match self {
-            Descriptor::ShWpkh(_) => Some(self.witness_script()),
-            Descriptor::ShWsh(ref script) => Some(script.encode().to_v0_p2wsh()),
-            Descriptor::Sh(ref script) => Some(script.encode()),
-            Descriptor::Bare(ref script) => Some(script.encode()),
+            Descriptor::ShWpkh(_) => Some(self.witness_script(deriv_ctx)),
+            Descriptor::ShWsh(ref script) => Some(script.encode(deriv_ctx).to_v0_p2wsh()),
+            Descriptor::Sh(ref script) => Some(script.encode(deriv_ctx)),
+            Descriptor::Bare(ref script) => Some(script.encode(deriv_ctx)),
+            Descriptor::ShSortedMulti(ref keys) => Some(keys.encode(deriv_ctx)),
             _ => None,
         }
     }
 
-    fn psbt_witness_script(&self) -> Option<Script> {
+    fn psbt_witness_script(&self, secp: &SecpCtx) -> Option<Script> {
+        let deriv_ctx = descriptor_to_pk_ctx(secp);
+
         match self {
-            Descriptor::Wsh(ref script) => Some(script.encode()),
-            Descriptor::ShWsh(ref script) => Some(script.encode()),
+            Descriptor::Wsh(ref script) => Some(script.encode(deriv_ctx)),
+            Descriptor::ShWsh(ref script) => Some(script.encode(deriv_ctx)),
+            Descriptor::WshSortedMulti(ref keys) | Descriptor::ShWshSortedMulti(ref keys) => {
+                Some(keys.encode(deriv_ctx))
+            }
             _ => None,
         }
     }
@@ -263,22 +279,25 @@ where
 impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
     fn is_witness(&self) -> bool {
         match self {
-            Descriptor::Bare(_) | Descriptor::Pk(_) | Descriptor::Pkh(_) | Descriptor::Sh(_) => {
-                false
-            }
+            Descriptor::Bare(_)
+            | Descriptor::Pk(_)
+            | Descriptor::Pkh(_)
+            | Descriptor::Sh(_)
+            | Descriptor::ShSortedMulti(_) => false,
             Descriptor::Wpkh(_)
             | Descriptor::ShWpkh(_)
             | Descriptor::Wsh(_)
-            | Descriptor::ShWsh(_) => true,
+            | Descriptor::ShWsh(_)
+            | Descriptor::ShWshSortedMulti(_)
+            | Descriptor::WshSortedMulti(_) => true,
         }
     }
 
-    fn get_hd_keypaths(&self, index: u32) -> Result<HDKeyPaths, Error> {
-        fn translate_key(
-            key: &DescriptorPublicKey,
-            index: u32,
-            paths: &mut HDKeyPaths,
-        ) -> Result<DummyKey, Error> {
+    fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error> {
+        let translate_key = |key: &DescriptorPublicKey,
+                             index: u32,
+                             paths: &mut HDKeyPaths|
+         -> Result<DummyKey, Error> {
             match key {
                 DescriptorPublicKey::SinglePub(_) => {}
                 DescriptorPublicKey::XPub(xpub) => {
@@ -298,7 +317,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
                     paths.insert(
                         derived_pubkey.public_key,
                         (
-                            xpub.root_fingerprint(),
+                            xpub.root_fingerprint(secp),
                             xpub.full_path(&[ChildNumber::from_normal_idx(index)?]),
                         ),
                     );
@@ -306,7 +325,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
             }
 
             Ok(DummyKey::default())
-        }
+        };
 
         let mut answer_pk = BTreeMap::new();
         let mut answer_pkh = BTreeMap::new();
@@ -347,12 +366,11 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
         !found_wildcard_pk && !found_wildcard_pkh
     }
 
-    fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths) -> Option<Self> {
-        fn try_key(
-            key: &DescriptorPublicKey,
-            index: &HashMap<Fingerprint, DerivationPath>,
-            found_path: &mut Option<ChildNumber>,
-        ) -> Result<DummyKey, Error> {
+    fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self> {
+        let try_key = |key: &DescriptorPublicKey,
+                       index: &HashMap<Fingerprint, DerivationPath>,
+                       found_path: &mut Option<ChildNumber>|
+         -> Result<DummyKey, Error> {
             if found_path.is_some() {
                 // already found a matching path, we are done
                 return Ok(DummyKey::default());
@@ -363,13 +381,15 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
                 // return the "prefix" that matched, so we remove that prefix from the full path
                 // found in `index` and save it in `derive_path`. We expect this to be a derivation
                 // path of length 1 if the key `is_wildcard` and an empty path otherwise.
-                let root_fingerprint = xpub.root_fingerprint();
+                let root_fingerprint = xpub.root_fingerprint(secp);
                 let derivation_path: Option<Vec<ChildNumber>> = index
                     .get_key_value(&root_fingerprint)
-                    .and_then(|(fingerprint, path)| xpub.matches(*fingerprint, path))
+                    .and_then(|(fingerprint, path)| {
+                        xpub.matches(&(*fingerprint, path.clone()), secp)
+                    })
                     .map(|prefix| {
                         index
-                            .get(&xpub.root_fingerprint())
+                            .get(&xpub.root_fingerprint(secp))
                             .unwrap()
                             .into_iter()
                             .skip(prefix.into_iter().count())
@@ -422,8 +442,9 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
         &self,
         psbt_input: &psbt::Input,
         utxo: Option<TxOut>,
+        secp: &SecpCtx,
     ) -> Option<Self> {
-        if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.hd_keypaths) {
+        if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.hd_keypaths, secp) {
             return Some(derived);
         } else if !self.is_fixed() {
             // If the descriptor is not fixed we can't brute-force the derivation address, so just
@@ -431,25 +452,38 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
             return None;
         }
 
+        let deriv_ctx = descriptor_to_pk_ctx(secp);
         match self {
             Descriptor::Pk(_)
             | Descriptor::Pkh(_)
             | Descriptor::Wpkh(_)
             | Descriptor::ShWpkh(_)
                 if utxo.is_some()
-                    && self.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
+                    && self.script_pubkey(deriv_ctx) == utxo.as_ref().unwrap().script_pubkey =>
             {
                 Some(self.clone())
             }
             Descriptor::Bare(ms) | Descriptor::Sh(ms)
                 if psbt_input.redeem_script.is_some()
-                    && &ms.encode() == psbt_input.redeem_script.as_ref().unwrap() =>
+                    && &ms.encode(deriv_ctx) == psbt_input.redeem_script.as_ref().unwrap() =>
             {
                 Some(self.clone())
             }
             Descriptor::Wsh(ms) | Descriptor::ShWsh(ms)
                 if psbt_input.witness_script.is_some()
-                    && &ms.encode() == psbt_input.witness_script.as_ref().unwrap() =>
+                    && &ms.encode(deriv_ctx) == psbt_input.witness_script.as_ref().unwrap() =>
+            {
+                Some(self.clone())
+            }
+            Descriptor::ShSortedMulti(keys)
+                if psbt_input.redeem_script.is_some()
+                    && &keys.encode(deriv_ctx) == psbt_input.redeem_script.as_ref().unwrap() =>
+            {
+                Some(self.clone())
+            }
+            Descriptor::WshSortedMulti(keys) | Descriptor::ShWshSortedMulti(keys)
+                if psbt_input.witness_script.is_some()
+                    && &keys.encode(deriv_ctx) == psbt_input.witness_script.as_ref().unwrap() =>
             {
                 Some(self.clone())
             }
@@ -489,6 +523,7 @@ mod test {
 
     use bitcoin::consensus::encode::deserialize;
     use bitcoin::hashes::hex::FromHex;
+    use bitcoin::secp256k1::Secp256k1;
     use bitcoin::util::{bip32, psbt};
 
     use super::*;
@@ -513,7 +548,7 @@ mod test {
         .unwrap();
 
         assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
+            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
             .is_some());
     }
 
@@ -544,7 +579,7 @@ mod test {
         .unwrap();
 
         assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
+            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
             .is_some());
     }
 
@@ -568,7 +603,7 @@ mod test {
         .unwrap();
 
         assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
+            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
             .is_some());
     }
 
@@ -598,7 +633,7 @@ mod test {
         .unwrap();
 
         assert!(descriptor
-            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0))
+            .derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
             .is_some());
     }
 
index fe0056cd9e3c90f9f28d90abbf1039dc4cb4375e..1af2bbe800fffe1e694c54db7fc131034634c0ef 100644 (file)
 //! ```
 //! # use std::sync::Arc;
 //! # use bdk::descriptor::*;
+//! # use bdk::bitcoin::secp256k1::Secp256k1;
+//! let secp = Secp256k1::new();
 //! let desc = "wsh(and_v(v:pk(cV3oCth6zxZ1UVsHLnGothsWNsaoxRhC6aeNi5VbSdFpwUkgkEci),or_d(pk(cVMTy7uebJgvFaSBwcgvwk8qn8xSLc97dKow4MBetjrrahZoimm2),older(12960))))";
 //!
-//! let (extended_desc, key_map) = ExtendedDescriptor::parse_secret(desc)?;
+//! let (extended_desc, key_map) = ExtendedDescriptor::parse_descriptor(desc)?;
 //! println!("{:?}", extended_desc);
 //!
 //! let signers = Arc::new(key_map.into());
-//! let policy = extended_desc.extract_policy(signers)?;
+//! let policy = extended_desc.extract_policy(signers, &secp)?;
 //! println!("policy: {}", serde_json::to_string(&policy)?);
 //! # Ok::<(), bdk::Error>(())
 //! ```
@@ -55,7 +57,7 @@ use bitcoin::hashes::*;
 use bitcoin::util::bip32::Fingerprint;
 use bitcoin::PublicKey;
 
-use miniscript::descriptor::DescriptorPublicKey;
+use miniscript::descriptor::{DescriptorPublicKey, SortedMultiVec};
 use miniscript::{Descriptor, Miniscript, MiniscriptKey, ScriptContext, Terminal, ToPublicKey};
 
 #[allow(unused_imports)]
@@ -63,6 +65,7 @@ use log::{debug, error, info, trace};
 
 use crate::descriptor::ExtractPolicy;
 use crate::wallet::signer::{SignerId, SignersContainer};
+use crate::wallet::utils::{descriptor_to_pk_ctx, SecpCtx};
 
 use super::checksum::get_checksum;
 use super::error::Error;
@@ -80,14 +83,14 @@ pub struct PKOrF {
 }
 
 impl PKOrF {
-    fn from_key(k: &DescriptorPublicKey) -> Self {
+    fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
         match k {
             DescriptorPublicKey::SinglePub(pubkey) => PKOrF {
                 pubkey: Some(pubkey.key),
                 ..Default::default()
             },
             DescriptorPublicKey::XPub(xpub) => PKOrF {
-                fingerprint: Some(xpub.root_fingerprint()),
+                fingerprint: Some(xpub.root_fingerprint(secp)),
                 ..Default::default()
             },
         }
@@ -241,6 +244,9 @@ pub enum Satisfaction {
         m: usize,
         /// The items that can be satisfied by the descriptor
         items: Vec<usize>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        /// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
+        sorted: Option<bool>,
         #[serde(skip_serializing_if = "BTreeMap::is_empty")]
         /// Extra conditions that also need to be satisfied
         conditions: ConditionMap,
@@ -253,6 +259,9 @@ pub enum Satisfaction {
         m: usize,
         /// The items that can be satisfied by the descriptor
         items: Vec<usize>,
+        #[serde(skip_serializing_if = "Option::is_none")]
+        /// Whether the items are sorted in lexicographic order (used by `sortedmulti`)
+        sorted: Option<bool>,
         #[serde(
             serialize_with = "serialize_folded_cond_map",
             skip_serializing_if = "BTreeMap::is_empty"
@@ -324,6 +333,7 @@ impl Satisfaction {
             m,
             items,
             conditions,
+            sorted,
         } = self
         {
             if items.len() >= *m {
@@ -378,6 +388,7 @@ impl Satisfaction {
                     m: *m,
                     items: items.clone(),
                     conditions: map,
+                    sorted: *sorted,
                 };
             }
         }
@@ -510,6 +521,7 @@ impl Policy {
             m: threshold,
             items: vec![],
             conditions: Default::default(),
+            sorted: None,
         };
         for (index, item) in items.iter().enumerate() {
             contribution.add(&item.contribution, index)?;
@@ -526,21 +538,24 @@ impl Policy {
         keys: &[DescriptorPublicKey],
         signers: Arc<SignersContainer>,
         threshold: usize,
+        sorted: bool,
+        secp: &SecpCtx,
     ) -> Result<Option<Policy>, PolicyError> {
         if threshold == 0 {
             return Ok(None);
         }
 
-        let parsed_keys = keys.iter().map(|k| PKOrF::from_key(k)).collect();
+        let parsed_keys = keys.iter().map(|k| PKOrF::from_key(k, secp)).collect();
 
         let mut contribution = Satisfaction::Partial {
             n: keys.len(),
             m: threshold,
             items: vec![],
             conditions: Default::default(),
+            sorted: Some(sorted),
         };
         for (index, key) in keys.iter().enumerate() {
-            if signers.find(signer_id(key)).is_some() {
+            if signers.find(signer_id(key, secp)).is_some() {
                 contribution.add(
                     &Satisfaction::Complete {
                         condition: Default::default(),
@@ -648,17 +663,17 @@ impl From<SatisfiableItem> for Policy {
     }
 }
 
-fn signer_id(key: &DescriptorPublicKey) -> SignerId {
+fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
     match key {
         DescriptorPublicKey::SinglePub(pubkey) => pubkey.key.to_pubkeyhash().into(),
-        DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint().into(),
+        DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
     }
 }
 
-fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>) -> Policy {
-    let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key(key)).into();
+fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>, secp: &SecpCtx) -> Policy {
+    let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key(key, secp)).into();
 
-    policy.contribution = if signers.find(signer_id(key)).is_some() {
+    policy.contribution = if signers.find(signer_id(key, secp)).is_some() {
         Satisfaction::Complete {
             condition: Default::default(),
         }
@@ -672,8 +687,10 @@ fn signature(key: &DescriptorPublicKey, signers: Arc<SignersContainer>) -> Polic
 fn signature_key(
     key: &<DescriptorPublicKey as MiniscriptKey>::Hash,
     signers: Arc<SignersContainer>,
+    secp: &SecpCtx,
 ) -> Policy {
-    let key_hash = key.to_public_key().to_pubkeyhash();
+    let deriv_ctx = descriptor_to_pk_ctx(secp);
+    let key_hash = key.to_public_key(deriv_ctx).to_pubkeyhash();
     let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key_hash(key_hash)).into();
 
     if signers.find(SignerId::PkHash(key_hash)).is_some() {
@@ -686,12 +703,18 @@ fn signature_key(
 }
 
 impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
-    fn extract_policy(&self, signers: Arc<SignersContainer>) -> Result<Option<Policy>, Error> {
+    fn extract_policy(
+        &self,
+        signers: Arc<SignersContainer>,
+        secp: &SecpCtx,
+    ) -> Result<Option<Policy>, Error> {
         Ok(match &self.node {
             // Leaves
             Terminal::True | Terminal::False => None,
-            Terminal::PkK(pubkey) => Some(signature(pubkey, Arc::clone(&signers))),
-            Terminal::PkH(pubkey_hash) => Some(signature_key(pubkey_hash, Arc::clone(&signers))),
+            Terminal::PkK(pubkey) => Some(signature(pubkey, Arc::clone(&signers), secp)),
+            Terminal::PkH(pubkey_hash) => {
+                Some(signature_key(pubkey_hash, Arc::clone(&signers), secp))
+            }
             Terminal::After(value) => {
                 let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into();
                 policy.contribution = Satisfaction::Complete {
@@ -724,7 +747,9 @@ impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx>
             Terminal::Hash160(hash) => {
                 Some(SatisfiableItem::HASH160Preimage { hash: *hash }.into())
             }
-            Terminal::Multi(k, pks) => Policy::make_multisig(pks, Arc::clone(&signers), *k)?,
+            Terminal::Multi(k, pks) => {
+                Policy::make_multisig(pks, Arc::clone(&signers), *k, false, secp)?
+            }
             // Identities
             Terminal::Alt(inner)
             | Terminal::Swap(inner)
@@ -732,31 +757,31 @@ impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx>
             | Terminal::DupIf(inner)
             | Terminal::Verify(inner)
             | Terminal::NonZero(inner)
-            | Terminal::ZeroNotEqual(inner) => inner.extract_policy(Arc::clone(&signers))?,
+            | Terminal::ZeroNotEqual(inner) => inner.extract_policy(Arc::clone(&signers), secp)?,
             // Complex policies
             Terminal::AndV(a, b) | Terminal::AndB(a, b) => Policy::make_and(
-                a.extract_policy(Arc::clone(&signers))?,
-                b.extract_policy(Arc::clone(&signers))?,
+                a.extract_policy(Arc::clone(&signers), secp)?,
+                b.extract_policy(Arc::clone(&signers), secp)?,
             )?,
             Terminal::AndOr(x, y, z) => Policy::make_or(
                 Policy::make_and(
-                    x.extract_policy(Arc::clone(&signers))?,
-                    y.extract_policy(Arc::clone(&signers))?,
+                    x.extract_policy(Arc::clone(&signers), secp)?,
+                    y.extract_policy(Arc::clone(&signers), secp)?,
                 )?,
-                z.extract_policy(Arc::clone(&signers))?,
+                z.extract_policy(Arc::clone(&signers), secp)?,
             )?,
             Terminal::OrB(a, b)
             | Terminal::OrD(a, b)
             | Terminal::OrC(a, b)
             | Terminal::OrI(a, b) => Policy::make_or(
-                a.extract_policy(Arc::clone(&signers))?,
-                b.extract_policy(Arc::clone(&signers))?,
+                a.extract_policy(Arc::clone(&signers), secp)?,
+                b.extract_policy(Arc::clone(&signers), secp)?,
             )?,
             Terminal::Thresh(k, nodes) => {
                 let mut threshold = *k;
                 let mapped: Vec<_> = nodes
                     .iter()
-                    .map(|n| n.extract_policy(Arc::clone(&signers)))
+                    .map(|n| n.extract_policy(Arc::clone(&signers), secp))
                     .collect::<Result<Vec<_>, _>>()?
                     .into_iter()
                     .filter_map(|x| x)
@@ -776,14 +801,42 @@ impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx>
 }
 
 impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
-    fn extract_policy(&self, signers: Arc<SignersContainer>) -> Result<Option<Policy>, Error> {
+    fn extract_policy(
+        &self,
+        signers: Arc<SignersContainer>,
+        secp: &SecpCtx,
+    ) -> Result<Option<Policy>, Error> {
+        fn make_sortedmulti<Ctx: ScriptContext>(
+            keys: &SortedMultiVec<DescriptorPublicKey, Ctx>,
+            signers: Arc<SignersContainer>,
+            secp: &SecpCtx,
+        ) -> Result<Option<Policy>, Error> {
+            Ok(Policy::make_multisig(
+                keys.pks.as_ref(),
+                signers,
+                keys.k,
+                true,
+                secp,
+            )?)
+        }
+
         match self {
             Descriptor::Pk(pubkey)
             | Descriptor::Pkh(pubkey)
             | Descriptor::Wpkh(pubkey)
-            | Descriptor::ShWpkh(pubkey) => Ok(Some(signature(pubkey, signers))),
-            Descriptor::Bare(inner) | Descriptor::Sh(inner) => Ok(inner.extract_policy(signers)?),
-            Descriptor::Wsh(inner) | Descriptor::ShWsh(inner) => Ok(inner.extract_policy(signers)?),
+            | Descriptor::ShWpkh(pubkey) => Ok(Some(signature(pubkey, signers, secp))),
+            Descriptor::Bare(inner) | Descriptor::Sh(inner) => {
+                Ok(inner.extract_policy(signers, secp)?)
+            }
+            Descriptor::Wsh(inner) | Descriptor::ShWsh(inner) => {
+                Ok(inner.extract_policy(signers, secp)?)
+            }
+
+            // `sortedmulti()` is handled separately
+            Descriptor::ShSortedMulti(keys) => make_sortedmulti(&keys, signers, secp),
+            Descriptor::ShWshSortedMulti(keys) | Descriptor::WshSortedMulti(keys) => {
+                make_sortedmulti(&keys, signers, secp)
+            }
         }
     }
 }
@@ -833,7 +886,7 @@ mod test {
         let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = wallet_desc
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -846,7 +899,7 @@ mod test {
         let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = wallet_desc
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -926,7 +979,7 @@ mod test {
         let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = wallet_desc
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -936,7 +989,7 @@ mod test {
             && &keys[1].fingerprint.unwrap() == &fingerprint1)
         );
         assert!(
-            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions} if n == &2
+            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
              && m == &1
              && items.len() == 2
              && conditions.contains_key(&vec![0])
@@ -954,7 +1007,7 @@ mod test {
         let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = wallet_desc
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -965,7 +1018,7 @@ mod test {
         );
 
         assert!(
-            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions} if n == &2
+            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
              && m == &2
              && items.len() == 2
              && conditions.contains_key(&vec![0,1])
@@ -983,7 +1036,7 @@ mod test {
         let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = single_key
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -997,7 +1050,7 @@ mod test {
         let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = single_key
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -1019,7 +1072,7 @@ mod test {
         let single_key = wallet_desc.derive(ChildNumber::from_normal_idx(0).unwrap());
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = single_key
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -1029,7 +1082,7 @@ mod test {
             && &keys[1].fingerprint.unwrap() == &fingerprint1)
         );
         assert!(
-            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions } if n == &2
+            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
              && m == &1
              && items.len() == 2
              && conditions.contains_key(&vec![0])
@@ -1053,7 +1106,7 @@ mod test {
         let (wallet_desc, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
         let signers_container = Arc::new(SignersContainer::from(keymap));
         let policy = wallet_desc
-            .extract_policy(signers_container)
+            .extract_policy(signers_container, &Secp256k1::new())
             .unwrap()
             .unwrap();
 
@@ -1062,7 +1115,7 @@ mod test {
         );
 
         assert!(
-            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions } if n == &3
+            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &3
              && m == &2
              && items.len() == 3
              && conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
index de4dd62069a66c05075551cc5760ebc355bdd7f5..9dbbf1534627e2c1e25bc6df724a9583cc8ed4a9 100644 (file)
@@ -432,8 +432,9 @@ mod test {
     use crate::keys::{KeyError, ValidNetworks};
     use bitcoin::hashes::core::str::FromStr;
     use bitcoin::network::constants::Network::Regtest;
+    use bitcoin::secp256k1::Secp256k1;
     use bitcoin::util::bip32::ChildNumber;
-    use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
+    use miniscript::descriptor::{DescriptorPublicKey, DescriptorPublicKeyCtx, KeyMap};
     use miniscript::Descriptor;
 
     // verify template descriptor generates expected address(es)
@@ -443,6 +444,10 @@ mod test {
         is_fixed: bool,
         expected: &[&str],
     ) {
+        let secp = Secp256k1::new();
+        let deriv_ctx =
+            DescriptorPublicKeyCtx::new(&secp, ChildNumber::from_normal_idx(0).unwrap());
+
         let (desc, _key_map, _networks) = desc.unwrap();
         assert_eq!(desc.is_witness(), is_witness);
         assert_eq!(desc.is_fixed(), is_fixed);
@@ -453,7 +458,7 @@ mod test {
             } else {
                 desc.derive(ChildNumber::from_normal_idx(index).unwrap())
             };
-            let address = child_desc.address(Regtest).unwrap();
+            let address = child_desc.address(Regtest, deriv_ctx).unwrap();
             assert_eq!(address.to_string(), *expected.get(i).unwrap());
         }
     }
index c66f7b175ccee94e8b2695077c6751a779e68788..1930eb02f4eccb26920ae654acd5c0f0ed7a4410 100644 (file)
@@ -41,6 +41,8 @@ use miniscript::descriptor::{DescriptorXKey, KeyMap};
 pub use miniscript::ScriptContext;
 use miniscript::{Miniscript, Terminal};
 
+use crate::wallet::utils::SecpCtx;
+
 #[cfg(feature = "keys-bip39")]
 #[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
 pub mod bip39;
@@ -101,7 +103,10 @@ impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
     // public because it is effectively called by external crates, once the macros are expanded,
     // but since it is not meant to be part of the public api we hide it from the docs.
     #[doc(hidden)]
-    pub fn extract(self) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
+    pub fn extract(
+        self,
+        secp: &SecpCtx,
+    ) -> Result<(DescriptorPublicKey, KeyMap, ValidNetworks), KeyError> {
         match self {
             DescriptorKey::Public(public, valid_networks, _) => {
                 Ok((public, KeyMap::default(), valid_networks))
@@ -110,7 +115,7 @@ impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
                 let mut key_map = KeyMap::with_capacity(1);
 
                 let public = secret
-                    .as_public()
+                    .as_public(secp)
                     .map_err(|e| miniscript::Error::Unexpected(e.to_string()))?;
                 key_map.insert(public.clone(), secret);
 
@@ -529,8 +534,9 @@ impl<Ctx: ScriptContext, T: DerivableKey<Ctx>> ToDescriptorKey<Ctx>
 #[doc(hidden)]
 pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
     descriptor_key: Pk,
+    secp: &SecpCtx,
 ) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
-    let (key, key_map, valid_networks) = descriptor_key.to_descriptor_key()?.extract()?;
+    let (key, key_map, valid_networks) = descriptor_key.to_descriptor_key()?.extract(secp)?;
 
     Ok((
         Miniscript::from_ast(Terminal::PkK(key))?,
@@ -544,10 +550,11 @@ pub fn make_pk<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
 pub fn make_multi<Pk: ToDescriptorKey<Ctx>, Ctx: ScriptContext>(
     thresh: usize,
     pks: Vec<Pk>,
+    secp: &SecpCtx,
 ) -> Result<(Miniscript<DescriptorPublicKey, Ctx>, KeyMap, ValidNetworks), KeyError> {
     let (pks, key_maps_networks): (Vec<_>, Vec<_>) = pks
         .into_iter()
-        .map(|key| Ok::<_, KeyError>(key.to_descriptor_key()?.extract()?))
+        .map(|key| Ok::<_, KeyError>(key.to_descriptor_key()?.extract(secp)?))
         .collect::<Result<Vec<_>, _>>()?
         .into_iter()
         .map(|(a, b, c)| (a, (b, c)))
index bb97ecb67f66738bc453ed60e2c08ed20ec07c97..fa0cd9e7ca9421c6ba3369887283d5969d1da0e2 100644 (file)
@@ -71,7 +71,7 @@ use std::str::FromStr;
 
 use serde::{Deserialize, Serialize};
 
-use miniscript::{Descriptor, ScriptContext, Terminal};
+use miniscript::{Descriptor, DescriptorPublicKey, ScriptContext, Terminal};
 
 use crate::blockchain::BlockchainMarker;
 use crate::database::BatchDatabase;
@@ -122,7 +122,7 @@ impl WalletExport {
     ) -> Result<Self, &'static str> {
         let descriptor = wallet
             .descriptor
-            .to_string_with_secret(&wallet.signers.as_key_map());
+            .to_string_with_secret(&wallet.signers.as_key_map(wallet.secp_ctx()));
         Self::is_compatible_with_core(&descriptor)?;
 
         let blockheight = match wallet.database.borrow().iter_txs(false) {
@@ -145,12 +145,10 @@ impl WalletExport {
             blockheight,
         };
 
-        if export.change_descriptor()
-            != wallet
-                .change_descriptor
-                .as_ref()
-                .map(|d| d.to_string_with_secret(&wallet.change_signers.as_key_map()))
-        {
+        let desc_to_string = |d: &Descriptor<DescriptorPublicKey>| {
+            d.to_string_with_secret(&wallet.change_signers.as_key_map(wallet.secp_ctx()))
+        };
+        if export.change_descriptor() != wallet.change_descriptor.as_ref().map(desc_to_string) {
             return Err("Incompatible change descriptor");
         }
 
index 34c5ac0d556ded49e4ef10d9bc00c8a01902240f..75b18e2a8218f4ed7fce4c098393ec6ce4184863 100644 (file)
@@ -32,6 +32,8 @@ use std::collections::{BTreeMap, HashSet};
 use std::ops::{Deref, DerefMut};
 use std::sync::Arc;
 
+use bitcoin::secp256k1::Secp256k1;
+
 use bitcoin::consensus::encode::serialize;
 use bitcoin::util::bip32::ChildNumber;
 use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
@@ -55,7 +57,7 @@ pub use utils::IsDust;
 use address_validator::AddressValidator;
 use signer::{Signer, SignerId, SignerOrdering, SignersContainer};
 use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxBuilderContext};
-use utils::{After, Older};
+use utils::{descriptor_to_pk_ctx, After, Older, SecpCtx};
 
 use crate::blockchain::{Blockchain, BlockchainMarker, OfflineBlockchain, Progress};
 use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
@@ -97,6 +99,8 @@ pub struct Wallet<B: BlockchainMarker, D: BatchDatabase> {
 
     client: Option<B>,
     database: RefCell<D>,
+
+    secp: SecpCtx,
 }
 
 // offline actions, always available
@@ -149,16 +153,19 @@ where
 
             client: None,
             database: RefCell::new(database),
+
+            secp: Secp256k1::new(),
         })
     }
 
     /// Return a newly generated address using the external descriptor
     pub fn get_new_address(&self) -> Result<Address, Error> {
         let index = self.fetch_and_increment_index(ScriptType::External)?;
+        let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
 
         self.descriptor
             .derive(ChildNumber::from_normal_idx(index)?)
-            .address(self.network)
+            .address(self.network, deriv_ctx)
             .ok_or(Error::ScriptDoesntHaveAddressForm)
     }
 
@@ -246,14 +253,14 @@ where
     ) -> Result<(PSBT, TransactionDetails), Error> {
         let external_policy = self
             .descriptor
-            .extract_policy(Arc::clone(&self.signers))?
+            .extract_policy(Arc::clone(&self.signers), &self.secp)?
             .unwrap();
         let internal_policy = self
             .change_descriptor
             .as_ref()
             .map(|desc| {
                 Ok::<_, Error>(
-                    desc.extract_policy(Arc::clone(&self.change_signers))?
+                    desc.extract_policy(Arc::clone(&self.change_signers), &self.secp)?
                         .unwrap(),
                 )
             })
@@ -601,14 +608,18 @@ where
             details.received -= removed_updatable_output.value;
         }
 
+        let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
         let external_weight = self
             .get_descriptor_for_script_type(ScriptType::External)
             .0
-            .max_satisfaction_weight();
+            .max_satisfaction_weight(deriv_ctx)
+            .unwrap();
         let internal_weight = self
             .get_descriptor_for_script_type(ScriptType::Internal)
             .0
-            .max_satisfaction_weight();
+            .max_satisfaction_weight(deriv_ctx)
+            .unwrap();
 
         let original_sequence = tx.input[0].sequence;
 
@@ -801,10 +812,10 @@ where
             .chain(self.change_signers.signers().iter())
         {
             if signer.sign_whole_tx() {
-                signer.sign(&mut psbt, None)?;
+                signer.sign(&mut psbt, None, &self.secp)?;
             } else {
                 for index in 0..psbt.inputs.len() {
-                    signer.sign(&mut psbt, Some(index))?;
+                    signer.sign(&mut psbt, Some(index), &self.secp)?;
                 }
             }
         }
@@ -816,12 +827,12 @@ where
     /// Return the spending policies for the wallet's descriptor
     pub fn policies(&self, script_type: ScriptType) -> Result<Option<Policy>, Error> {
         match (script_type, self.change_descriptor.as_ref()) {
-            (ScriptType::External, _) => {
-                Ok(self.descriptor.extract_policy(Arc::clone(&self.signers))?)
-            }
+            (ScriptType::External, _) => Ok(self
+                .descriptor
+                .extract_policy(Arc::clone(&self.signers), &self.secp)?),
             (ScriptType::Internal, None) => Ok(None),
             (ScriptType::Internal, Some(desc)) => {
-                Ok(desc.extract_policy(Arc::clone(&self.change_signers))?)
+                Ok(desc.extract_policy(Arc::clone(&self.change_signers), &self.secp)?)
             }
         }
     }
@@ -877,22 +888,21 @@ where
                 .flatten()
             {
                 desc
-            } else if let Some(desc) = self
-                .descriptor
-                .derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n))
+            } else if let Some(desc) =
+                self.descriptor
+                    .derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
             {
                 desc
-            } else if let Some(desc) = self
-                .change_descriptor
-                .as_ref()
-                .and_then(|desc| desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n)))
-            {
+            } else if let Some(desc) = self.change_descriptor.as_ref().and_then(|desc| {
+                desc.derive_from_psbt_input(psbt_input, psbt.get_utxo_for(n), &self.secp)
+            }) {
                 desc
             } else {
                 debug!("Couldn't find the right derived descriptor for input {}", n);
                 return Ok((psbt, false));
             };
 
+            let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
             match desc.satisfy(
                 input,
                 (
@@ -900,6 +910,7 @@ where
                     After::new(current_height, false),
                     Older::new(current_height, create_height, false),
                 ),
+                deriv_ctx,
             ) {
                 Ok(_) => continue,
                 Err(e) => {
@@ -918,6 +929,10 @@ where
         Ok((psbt, true))
     }
 
+    pub fn secp_ctx(&self) -> &SecpCtx {
+        &self.secp
+    }
+
     // Internals
 
     fn get_descriptor_for_script_type(
@@ -943,12 +958,14 @@ where
     }
 
     fn get_change_address(&self) -> Result<Script, Error> {
+        let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
         let (desc, script_type) = self.get_descriptor_for_script_type(ScriptType::Internal);
         let index = self.fetch_and_increment_index(script_type)?;
 
         Ok(desc
             .derive(ChildNumber::from_normal_idx(index)?)
-            .script_pubkey())
+            .script_pubkey(deriv_ctx))
     }
 
     fn fetch_and_increment_index(&self, script_type: ScriptType) -> Result<u32, Error> {
@@ -970,10 +987,12 @@ where
             self.cache_addresses(script_type, index, CACHE_ADDR_BATCH_SIZE)?;
         }
 
-        let hd_keypaths = descriptor.get_hd_keypaths(index)?;
+        let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
+        let hd_keypaths = descriptor.get_hd_keypaths(index, &self.secp)?;
         let script = descriptor
             .derive(ChildNumber::from_normal_idx(index)?)
-            .script_pubkey();
+            .script_pubkey(deriv_ctx);
         for validator in &self.address_validators {
             validator.validate(script_type, &hd_keypaths, &script)?;
         }
@@ -996,6 +1015,8 @@ where
             count = 1;
         }
 
+        let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
         let mut address_batch = self.database.borrow().begin_batch();
 
         let start_time = time::Instant::new();
@@ -1003,7 +1024,7 @@ where
             address_batch.set_script_pubkey(
                 &descriptor
                     .derive(ChildNumber::from_normal_idx(i)?)
-                    .script_pubkey(),
+                    .script_pubkey(deriv_ctx),
                 script_type,
                 i,
             )?;
@@ -1022,14 +1043,18 @@ where
     }
 
     fn get_available_utxos(&self) -> Result<Vec<(UTXO, usize)>, Error> {
+        let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
+
         let external_weight = self
             .get_descriptor_for_script_type(ScriptType::External)
             .0
-            .max_satisfaction_weight();
+            .max_satisfaction_weight(deriv_ctx)
+            .unwrap();
         let internal_weight = self
             .get_descriptor_for_script_type(ScriptType::Internal)
             .0
-            .max_satisfaction_weight();
+            .max_satisfaction_weight(deriv_ctx)
+            .unwrap();
 
         let add_weight = |utxo: UTXO| {
             let weight = match utxo.is_internal {
@@ -1161,11 +1186,11 @@ where
             };
 
             let (desc, _) = self.get_descriptor_for_script_type(script_type);
-            psbt_input.hd_keypaths = desc.get_hd_keypaths(child)?;
+            psbt_input.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
             let derived_descriptor = desc.derive(ChildNumber::from_normal_idx(child)?);
 
-            psbt_input.redeem_script = derived_descriptor.psbt_redeem_script();
-            psbt_input.witness_script = derived_descriptor.psbt_witness_script();
+            psbt_input.redeem_script = derived_descriptor.psbt_redeem_script(&self.secp);
+            psbt_input.witness_script = derived_descriptor.psbt_witness_script(&self.secp);
 
             let prev_output = input.previous_output;
             if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
@@ -1194,7 +1219,7 @@ where
                 .get_path_from_script_pubkey(&tx_output.script_pubkey)?
             {
                 let (desc, _) = self.get_descriptor_for_script_type(script_type);
-                psbt_output.hd_keypaths = desc.get_hd_keypaths(child)?;
+                psbt_output.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
             }
         }
 
@@ -1219,7 +1244,7 @@ where
 
                     // merge hd_keypaths
                     let (desc, _) = self.get_descriptor_for_script_type(script_type);
-                    let mut hd_keypaths = desc.get_hd_keypaths(child)?;
+                    let mut hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
                     psbt_input.hd_keypaths.append(&mut hd_keypaths);
                 }
             }
index 24f08aef2dbb05d46d5c0d5b7aa557be3e85ae05..ec08e3265760843c9c55088016e79defd1dc6c7c 100644 (file)
@@ -30,6 +30,7 @@
 //! ```
 //! # use std::sync::Arc;
 //! # use std::str::FromStr;
+//! # use bitcoin::secp256k1::{Secp256k1, All};
 //! # use bitcoin::*;
 //! # use bitcoin::util::psbt;
 //! # use bitcoin::util::bip32::Fingerprint;
@@ -62,6 +63,7 @@
 //!         &self,
 //!         psbt: &mut psbt::PartiallySignedTransaction,
 //!         input_index: Option<usize>,
+//!         _secp: &Secp256k1<All>,
 //!     ) -> Result<(), SignerError> {
 //!         let input_index = input_index.ok_or(SignerError::InputIndexOutOfRange)?;
 //!         self.device.sign_input(psbt, input_index)?;
@@ -105,6 +107,7 @@ use bitcoin::{PrivateKey, Script, SigHash, SigHashType};
 use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap};
 use miniscript::{Legacy, MiniscriptKey, Segwitv0};
 
+use super::utils::SecpCtx;
 use crate::descriptor::XKeyUtils;
 
 /// Identifier of a signer in the `SignersContainers`. Used as a key to find the right signer among
@@ -172,6 +175,7 @@ pub trait Signer: fmt::Debug + Send + Sync {
         &self,
         psbt: &mut psbt::PartiallySignedTransaction,
         input_index: Option<usize>,
+        secp: &SecpCtx,
     ) -> Result<(), SignerError>;
 
     /// Return whether or not the signer signs the whole transaction in one go instead of every
@@ -193,6 +197,7 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
         &self,
         psbt: &mut psbt::PartiallySignedTransaction,
         input_index: Option<usize>,
+        secp: &SecpCtx,
     ) -> Result<(), SignerError> {
         let input_index = input_index.unwrap();
         if input_index >= psbt.inputs.len() {
@@ -203,7 +208,7 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
             .hd_keypaths
             .iter()
             .filter_map(|(pk, &(fingerprint, ref path))| {
-                if self.matches(fingerprint, &path).is_some() {
+                if self.matches(&(fingerprint, path.clone()), &secp).is_some() {
                     Some((pk, path))
                 } else {
                     None
@@ -215,13 +220,11 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
             None => return Ok(()),
         };
 
-        let ctx = Secp256k1::signing_only();
-
-        let derived_key = self.xkey.derive_priv(&ctx, &deriv_path).unwrap();
-        if &derived_key.private_key.public_key(&ctx) != public_key {
+        let derived_key = self.xkey.derive_priv(&secp, &deriv_path).unwrap();
+        if &derived_key.private_key.public_key(&secp) != public_key {
             Err(SignerError::InvalidKey)
         } else {
-            derived_key.private_key.sign(psbt, Some(input_index))
+            derived_key.private_key.sign(psbt, Some(input_index), secp)
         }
     }
 
@@ -239,15 +242,14 @@ impl Signer for PrivateKey {
         &self,
         psbt: &mut psbt::PartiallySignedTransaction,
         input_index: Option<usize>,
+        secp: &SecpCtx,
     ) -> Result<(), SignerError> {
         let input_index = input_index.unwrap();
         if input_index >= psbt.inputs.len() {
             return Err(SignerError::InputIndexOutOfRange);
         }
 
-        let ctx = Secp256k1::signing_only();
-
-        let pubkey = self.public_key(&ctx);
+        let pubkey = self.public_key(&secp);
         if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
             return Ok(());
         }
@@ -261,7 +263,7 @@ impl Signer for PrivateKey {
             None => Legacy::sighash(psbt, input_index)?,
         };
 
-        let signature = ctx.sign(
+        let signature = secp.sign(
             &Message::from_slice(&hash.into_inner()[..]).unwrap(),
             &self.key,
         );
@@ -323,17 +325,18 @@ impl From<(SignerId, SignerOrdering)> for SignersContainerKey {
 pub struct SignersContainer(BTreeMap<SignersContainerKey, Arc<dyn Signer>>);
 
 impl SignersContainer {
-    pub fn as_key_map(&self) -> KeyMap {
+    pub fn as_key_map(&self, secp: &SecpCtx) -> KeyMap {
         self.0
             .values()
             .filter_map(|signer| signer.descriptor_secret_key())
-            .filter_map(|secret| secret.as_public().ok().map(|public| (public, secret)))
+            .filter_map(|secret| secret.as_public(secp).ok().map(|public| (public, secret)))
             .collect()
     }
 }
 
 impl From<KeyMap> for SignersContainer {
     fn from(keymap: KeyMap) -> SignersContainer {
+        let secp = Secp256k1::new();
         let mut container = SignersContainer::new();
 
         for (_, secret) in keymap {
@@ -349,7 +352,7 @@ impl From<KeyMap> for SignersContainer {
                     Arc::new(private_key.key),
                 ),
                 DescriptorSecretKey::XPrv(xprv) => container.add_external(
-                    SignerId::from(xprv.root_fingerprint()),
+                    SignerId::from(xprv.root_fingerprint(&secp)),
                     SignerOrdering::default(),
                     Arc::new(xprv),
                 ),
index fcd3b6502be1a0af572b4e1aba285f7ec708bd09..7eee7a602631bceae5d91cbe20c3e2077b3ebeb9 100644 (file)
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 // SOFTWARE.
 
-use miniscript::{MiniscriptKey, Satisfier};
+use bitcoin::secp256k1::{All, Secp256k1};
+use bitcoin::util::bip32;
+
+use miniscript::descriptor::DescriptorPublicKeyCtx;
+use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
 
 // De-facto standard "dust limit" (even though it should change based on the output type)
 const DUST_LIMIT_SATOSHI: u64 = 546;
@@ -56,7 +60,7 @@ impl After {
     }
 }
 
-impl<Pk: MiniscriptKey> Satisfier<Pk> for After {
+impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx, Pk> for After {
     fn check_after(&self, n: u32) -> bool {
         if let Some(current_height) = self.current_height {
             current_height >= n
@@ -86,7 +90,7 @@ impl Older {
     }
 }
 
-impl<Pk: MiniscriptKey> Satisfier<Pk> for Older {
+impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx, Pk> for Older {
     fn check_older(&self, n: u32) -> bool {
         if let Some(current_height) = self.current_height {
             // TODO: test >= / >
@@ -97,6 +101,14 @@ impl<Pk: MiniscriptKey> Satisfier<Pk> for Older {
     }
 }
 
+pub(crate) type SecpCtx = Secp256k1<All>;
+pub(crate) fn descriptor_to_pk_ctx(secp: &SecpCtx) -> DescriptorPublicKeyCtx<'_, All> {
+    // Create a `to_pk_ctx` with a dummy derivation index, since we always use this on descriptor
+    // that have already been derived with `Descriptor::derive()`, so the child number added here
+    // is ignored.
+    DescriptorPublicKeyCtx::new(secp, bip32::ChildNumber::Normal { index: 0 })
+}
+
 pub struct ChunksIterator<I: Iterator> {
     iter: I,
     size: usize,
index f7947a542c1d787f03fa87670661783d2e17525a..55b526a264e65ffc8bb9aaa8cb08228fea5cb4d9 100644 (file)
@@ -116,15 +116,19 @@ impl TestIncomingTx {
 #[macro_export]
 macro_rules! testutils {
     ( @external $descriptors:expr, $child:expr ) => ({
-        use miniscript::descriptor::{Descriptor, DescriptorPublicKey};
+        use bitcoin::secp256k1::Secp256k1;
+        use miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorPublicKeyCtx};
 
-        let parsed = Descriptor::<DescriptorPublicKey>::parse_secret(&$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
-        parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest).expect("No address form")
+        let secp = Secp256k1::new();
+        let deriv_ctx = DescriptorPublicKeyCtx::new(&secp, bitcoin::util::bip32::ChildNumber::from_normal_idx(0).unwrap());
+
+        let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
+        parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest, deriv_ctx).expect("No address form")
     });
     ( @internal $descriptors:expr, $child:expr ) => ({
         use miniscript::descriptor::{Descriptor, DescriptorPublicKey};
 
-        let parsed = Descriptor::<DescriptorPublicKey>::parse_secret(&$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
+        let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
         parsed.derive(bitcoin::util::bip32::ChildNumber::from_normal_idx($child).unwrap()).address(bitcoin::Network::Regtest).expect("No address form")
     });
     ( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });