]> Untitled Git - bdk/commitdiff
Upgrade to rust-bitcoin 0.28 and miniscript 7.0
authorAlekos Filini <alekos.filini@gmail.com>
Thu, 14 Apr 2022 15:20:46 +0000 (17:20 +0200)
committerAlekos Filini <alekos.filini@gmail.com>
Thu, 12 May 2022 10:51:21 +0000 (12:51 +0200)
14 files changed:
CHANGELOG.md
Cargo.toml
src/blockchain/esplora/api.rs
src/descriptor/derived.rs
src/descriptor/dsl.rs
src/descriptor/mod.rs
src/descriptor/policy.rs
src/keys/mod.rs
src/psbt/mod.rs
src/testutils/blockchain_tests.rs
src/testutils/mod.rs
src/wallet/mod.rs
src/wallet/signer.rs
src/wallet/tx_builder.rs

index 22152cf4b2bf2d2bf6e4a187ead72c547c120406..d1aca5dbb0a5f401e66f76e8d931b341d1f34b52 100644 (file)
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - added `OldestFirstCoinSelection` impl to `CoinSelectionAlgorithm`
 - New MSRV set to `1.56`
 - Add traits to reuse `Blockchain`s across multiple wallets (`BlockchainFactory` and `StatelessBlockchain`).
+- Upgrade to rust-bitcoin `0.28` 
 
 
 ## [v0.18.0] - [v0.17.0]
index 63d81e75fdd8953fa0f316d47101eea47c587626..bcbe43dff9ed6bef51514cf3b7196d03e9557952 100644 (file)
@@ -14,15 +14,15 @@ license = "MIT OR Apache-2.0"
 [dependencies]
 bdk-macros = "^0.6"
 log = "^0.4"
-miniscript = { version = "^6.1", features = ["use-serde"] }
-bitcoin = { version = "^0.27", features = ["use-serde", "base64"] }
+miniscript = { version = "7.0", features = ["use-serde"] }
+bitcoin = { version = "0.28", features = ["use-serde", "base64"] }
 serde = { version = "^1.0", features = ["derive"] }
 serde_json = { version = "^1.0" }
 rand = "^0.7"
 
 # Optional dependencies
 sled = { version = "0.34", optional = true }
-electrum-client = { version = "0.8", optional = true }
+electrum-client = { version = "0.10", optional = true }
 rusqlite = { version = "0.25.3", optional = true }
 ahash = { version = "=0.7.4", optional = true }
 reqwest = { version = "0.11", optional = true, default-features = false, features = ["json"] }
@@ -37,8 +37,8 @@ lazy_static = { version = "1.4", optional = true }
 bip39 = { version = "1.0.1", optional = true }
 bitcoinconsensus = { version = "0.19.0-3", optional = true }
 
-# Needed by bdk_blockchain_tests macro
-bitcoincore-rpc = { version = "0.14", optional = true }
+# Needed by bdk_blockchain_tests macro and the `rpc` feature
+bitcoincore-rpc = { version = "0.15", optional = true }
 
 # Platform-specific dependencies
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@@ -97,7 +97,7 @@ test-md-docs = ["electrum"]
 lazy_static = "1.4"
 env_logger = "0.7"
 clap = "2.33"
-electrsd = { version= "0.15", features = ["trigger", "bitcoind_22_0"] }
+electrsd = { version= "0.19.1", features = ["bitcoind_22_0"] }
 
 [[example]]
 name = "address_validator"
index 640f7a0bb7db76d399d09c2b3b1946fc1fe45249..d548b5be80e9d40f825a3b37be393c767a38f110 100644 (file)
@@ -2,7 +2,7 @@
 //!
 //! see: <https://github.com/Blockstream/esplora/blob/master/API.md>
 use crate::BlockTime;
-use bitcoin::{OutPoint, Script, Transaction, TxIn, TxOut, Txid};
+use bitcoin::{OutPoint, Script, Transaction, TxIn, TxOut, Txid, Witness};
 
 #[derive(serde::Deserialize, Clone, Debug)]
 pub struct PrevOut {
@@ -63,7 +63,7 @@ impl Tx {
                     },
                     script_sig: vin.scriptsig,
                     sequence: vin.sequence,
-                    witness: vin.witness,
+                    witness: Witness::from_vec(vin.witness),
                 })
                 .collect(),
             output: self
index b6556a0b608ce018646f8ffd7a0eabb7726987ba..585c39749db88acd8b0ec5312e19afbb9464005c 100644 (file)
@@ -52,10 +52,10 @@ use std::hash::{Hash, Hasher};
 use std::ops::Deref;
 
 use bitcoin::hashes::hash160;
-use bitcoin::PublicKey;
+use bitcoin::{PublicKey, XOnlyPublicKey};
 
-use miniscript::{descriptor::Wildcard, Descriptor, DescriptorPublicKey};
-use miniscript::{MiniscriptKey, ToPublicKey, TranslatePk};
+use miniscript::descriptor::{DescriptorSinglePub, SinglePubKey, Wildcard};
+use miniscript::{Descriptor, DescriptorPublicKey, MiniscriptKey, ToPublicKey, TranslatePk};
 
 use crate::wallet::utils::SecpCtx;
 
@@ -128,21 +128,44 @@ impl<'s> MiniscriptKey for DerivedDescriptorKey<'s> {
     fn is_uncompressed(&self) -> bool {
         self.0.is_uncompressed()
     }
-    fn serialized_len(&self) -> usize {
-        self.0.serialized_len()
-    }
 }
 
 impl<'s> ToPublicKey for DerivedDescriptorKey<'s> {
     fn to_public_key(&self) -> PublicKey {
         match &self.0 {
-            DescriptorPublicKey::SinglePub(ref spub) => spub.key.to_public_key(),
-            DescriptorPublicKey::XPub(ref xpub) => {
+            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+                key: SinglePubKey::XOnly(_),
+                ..
+            }) => panic!("Found x-only public key in non-tr descriptor"),
+            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+                key: SinglePubKey::FullKey(ref pk),
+                ..
+            }) => *pk,
+            DescriptorPublicKey::XPub(ref xpub) => PublicKey::new(
+                xpub.xkey
+                    .derive_pub(self.1, &xpub.derivation_path)
+                    .expect("Shouldn't fail, only normal derivations")
+                    .public_key,
+            ),
+        }
+    }
+
+    fn to_x_only_pubkey(&self) -> XOnlyPublicKey {
+        match &self.0 {
+            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+                key: SinglePubKey::XOnly(ref pk),
+                ..
+            }) => *pk,
+            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+                key: SinglePubKey::FullKey(ref pk),
+                ..
+            }) => XOnlyPublicKey::from(pk.inner),
+            DescriptorPublicKey::XPub(ref xpub) => XOnlyPublicKey::from(
                 xpub.xkey
                     .derive_pub(self.1, &xpub.derivation_path)
                     .expect("Shouldn't fail, only normal derivations")
-                    .public_key
-            }
+                    .public_key,
+            ),
         }
     }
 
index 9ba5bb52f4c55185800305bfa35967b63daf042d..caee94bd2a707b779abf604c11b08097e218d452 100644 (file)
@@ -1054,7 +1054,9 @@ mod test {
     }
 
     #[test]
-    #[should_panic(expected = "Miniscript(ContextError(CompressedOnly))")]
+    #[should_panic(
+        expected = "Miniscript(ContextError(CompressedOnly(\"04b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a87378ec38ff91d43e8c2092ebda601780485263da089465619e0358a5c1be7ac91f4\")))"
+    )]
     fn test_dsl_miniscript_checks() {
         let mut uncompressed_pk =
             PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap();
index 5b8a3babc7d9201fac9635474e72cdd3b69e73c9..a6212e32daae72367d1d87e7ce3673e80e4a348f 100644 (file)
 use std::collections::{BTreeMap, HashMap, HashSet};
 use std::ops::Deref;
 
+use bitcoin::secp256k1;
 use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
 use bitcoin::util::psbt;
-use bitcoin::{Network, PublicKey, Script, TxOut};
+use bitcoin::{Network, Script, TxOut};
 
 use miniscript::descriptor::{DescriptorType, InnerXKey};
 pub use miniscript::{
@@ -58,7 +59,7 @@ pub type DerivedDescriptor<'s> = Descriptor<DerivedDescriptorKey<'s>>;
 ///
 /// [`psbt::Input`]: bitcoin::util::psbt::Input
 /// [`psbt::Output`]: bitcoin::util::psbt::Output
-pub type HdKeyPaths = BTreeMap<PublicKey, KeySource>;
+pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
 
 /// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`]
 pub trait IntoWalletDescriptor {
@@ -306,6 +307,7 @@ pub(crate) trait DerivedDescriptorMeta {
 
 pub(crate) trait DescriptorMeta {
     fn is_witness(&self) -> bool;
+    fn is_taproot(&self) -> bool;
     fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError>;
     fn derive_from_hd_keypaths<'s>(
         &self,
@@ -328,21 +330,21 @@ pub(crate) trait DescriptorScripts {
 impl<'s> DescriptorScripts for DerivedDescriptor<'s> {
     fn psbt_redeem_script(&self) -> Option<Script> {
         match self.desc_type() {
-            DescriptorType::ShWpkh => Some(self.explicit_script()),
-            DescriptorType::ShWsh => Some(self.explicit_script().to_v0_p2wsh()),
-            DescriptorType::Sh => Some(self.explicit_script()),
-            DescriptorType::Bare => Some(self.explicit_script()),
-            DescriptorType::ShSortedMulti => Some(self.explicit_script()),
+            DescriptorType::ShWpkh => Some(self.explicit_script().unwrap()),
+            DescriptorType::ShWsh => Some(self.explicit_script().unwrap().to_v0_p2wsh()),
+            DescriptorType::Sh => Some(self.explicit_script().unwrap()),
+            DescriptorType::Bare => Some(self.explicit_script().unwrap()),
+            DescriptorType::ShSortedMulti => Some(self.explicit_script().unwrap()),
             _ => None,
         }
     }
 
     fn psbt_witness_script(&self) -> Option<Script> {
         match self.desc_type() {
-            DescriptorType::Wsh => Some(self.explicit_script()),
-            DescriptorType::ShWsh => Some(self.explicit_script()),
+            DescriptorType::Wsh => Some(self.explicit_script().unwrap()),
+            DescriptorType::ShWsh => Some(self.explicit_script().unwrap()),
             DescriptorType::WshSortedMulti | DescriptorType::ShWshSortedMulti => {
-                Some(self.explicit_script())
+                Some(self.explicit_script().unwrap())
             }
             _ => None,
         }
@@ -362,6 +364,10 @@ impl DescriptorMeta for ExtendedDescriptor {
         )
     }
 
+    fn is_taproot(&self) -> bool {
+        self.desc_type() == DescriptorType::Tr
+    }
+
     fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError> {
         let mut answer = Vec::new();
 
@@ -448,7 +454,10 @@ impl DescriptorMeta for ExtendedDescriptor {
         let descriptor = self.as_derived_fixed(secp);
         match descriptor.desc_type() {
             // TODO: add pk() here
-            DescriptorType::Pkh | DescriptorType::Wpkh | DescriptorType::ShWpkh
+            DescriptorType::Pkh
+            | DescriptorType::Wpkh
+            | DescriptorType::ShWpkh
+            | DescriptorType::Tr
                 if utxo.is_some()
                     && descriptor.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
             {
@@ -456,7 +465,7 @@ impl DescriptorMeta for ExtendedDescriptor {
             }
             DescriptorType::Bare | DescriptorType::Sh | DescriptorType::ShSortedMulti
                 if psbt_input.redeem_script.is_some()
-                    && &descriptor.explicit_script()
+                    && &descriptor.explicit_script().unwrap()
                         == psbt_input.redeem_script.as_ref().unwrap() =>
             {
                 Some(descriptor)
@@ -466,7 +475,7 @@ impl DescriptorMeta for ExtendedDescriptor {
             | DescriptorType::ShWshSortedMulti
             | DescriptorType::WshSortedMulti
                 if psbt_input.witness_script.is_some()
-                    && &descriptor.explicit_script()
+                    && &descriptor.explicit_script().unwrap()
                         == psbt_input.witness_script.as_ref().unwrap() =>
             {
                 Some(descriptor)
index 8716a86cfd2dfb7f71cea45832183bf01b925b78..98b265734f542b7709af62ea43714902e9530b31 100644 (file)
@@ -43,23 +43,27 @@ use serde::ser::SerializeMap;
 use serde::{Serialize, Serializer};
 
 use bitcoin::hashes::*;
+use bitcoin::secp256k1;
 use bitcoin::util::bip32::Fingerprint;
-use bitcoin::PublicKey;
+use bitcoin::{PublicKey, XOnlyPublicKey};
 
-use miniscript::descriptor::{DescriptorPublicKey, ShInner, SortedMultiVec, WshInner};
+use miniscript::descriptor::{
+    DescriptorPublicKey, DescriptorSinglePub, ShInner, SinglePubKey, SortedMultiVec, WshInner,
+};
 use miniscript::{Descriptor, Miniscript, MiniscriptKey, Satisfier, ScriptContext, Terminal};
 
 #[allow(unused_imports)]
 use log::{debug, error, info, trace};
 
 use crate::descriptor::ExtractPolicy;
+use crate::keys::ExtScriptContext;
 use crate::wallet::signer::{SignerId, SignersContainer};
 use crate::wallet::utils::{self, After, Older, SecpCtx};
 
 use super::checksum::get_checksum;
 use super::error::Error;
 use super::XKeyUtils;
-use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
+use bitcoin::util::psbt::{Input as PsbtInput, PartiallySignedTransaction as Psbt};
 use miniscript::psbt::PsbtInputSatisfier;
 
 /// Raw public key or extended key fingerprint
@@ -68,6 +72,8 @@ pub struct PkOrF {
     #[serde(skip_serializing_if = "Option::is_none")]
     pubkey: Option<PublicKey>,
     #[serde(skip_serializing_if = "Option::is_none")]
+    x_only_pubkey: Option<XOnlyPublicKey>,
+    #[serde(skip_serializing_if = "Option::is_none")]
     pubkey_hash: Option<hash160::Hash>,
     #[serde(skip_serializing_if = "Option::is_none")]
     fingerprint: Option<Fingerprint>,
@@ -76,8 +82,18 @@ pub struct PkOrF {
 impl PkOrF {
     fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
         match k {
-            DescriptorPublicKey::SinglePub(pubkey) => PkOrF {
-                pubkey: Some(pubkey.key),
+            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+                key: SinglePubKey::FullKey(pk),
+                ..
+            }) => PkOrF {
+                pubkey: Some(*pk),
+                ..Default::default()
+            },
+            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+                key: SinglePubKey::XOnly(pk),
+                ..
+            }) => PkOrF {
+                x_only_pubkey: Some(*pk),
                 ..Default::default()
             },
             DescriptorPublicKey::XPub(xpub) => PkOrF {
@@ -93,10 +109,10 @@ impl PkOrF {
 #[serde(tag = "type", rename_all = "UPPERCASE")]
 pub enum SatisfiableItem {
     // Leaves
-    /// Signature for a raw public key
-    Signature(PkOrF),
-    /// Signature for an extended key fingerprint
-    SignatureKey(PkOrF),
+    /// ECDSA Signature for a raw public key
+    EcdsaSignature(PkOrF),
+    /// Schnorr Signature for a raw public key
+    SchnorrSignature(PkOrF),
     /// SHA256 preimage hash
     Sha256Preimage {
         /// The digest value
@@ -571,6 +587,7 @@ impl Policy {
         build_sat: BuildSatisfaction,
         threshold: usize,
         sorted: bool,
+        is_ecdsa: bool,
         secp: &SecpCtx,
     ) -> Result<Option<Policy>, PolicyError> {
         if threshold == 0 {
@@ -599,7 +616,9 @@ impl Policy {
             }
 
             if let Some(psbt) = build_sat.psbt() {
-                if signature_in_psbt(psbt, key, secp) {
+                if is_ecdsa && ecdsa_signature_in_psbt(psbt, key, secp)
+                    || !is_ecdsa && schnorr_signature_in_psbt(psbt, key, secp)
+                {
                     satisfaction.add(
                         &Satisfaction::Complete {
                             condition: Default::default(),
@@ -715,7 +734,14 @@ impl From<SatisfiableItem> for Policy {
 
 fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
     match key {
-        DescriptorPublicKey::SinglePub(pubkey) => pubkey.key.to_pubkeyhash().into(),
+        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+            key: SinglePubKey::FullKey(pk),
+            ..
+        }) => pk.to_pubkeyhash().into(),
+        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+            key: SinglePubKey::XOnly(pk),
+            ..
+        }) => pk.to_pubkeyhash().into(),
         DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
     }
 }
@@ -726,7 +752,7 @@ fn signature(
     build_sat: BuildSatisfaction,
     secp: &SecpCtx,
 ) -> Policy {
-    let mut policy: Policy = SatisfiableItem::Signature(PkOrF::from_key(key, secp)).into();
+    let mut policy: Policy = SatisfiableItem::EcdsaSignature(PkOrF::from_key(key, secp)).into();
 
     policy.contribution = if signers.find(signer_id(key, secp)).is_some() {
         Satisfaction::Complete {
@@ -737,7 +763,7 @@ fn signature(
     };
 
     if let Some(psbt) = build_sat.psbt() {
-        policy.satisfaction = if signature_in_psbt(psbt, key, secp) {
+        policy.satisfaction = if ecdsa_signature_in_psbt(psbt, key, secp) {
             Satisfaction::Complete {
                 condition: Default::default(),
             }
@@ -749,10 +775,19 @@ fn signature(
     policy
 }
 
-fn signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool {
+fn generic_sig_in_psbt<
+    C: Fn(&PsbtInput, &SinglePubKey) -> bool,
+    M: Fn(&secp256k1::PublicKey) -> SinglePubKey,
+>(
+    psbt: &Psbt,
+    key: &DescriptorPublicKey,
+    secp: &SecpCtx,
+    map: M,
+    check: C,
+) -> bool {
     //TODO check signature validity
     psbt.inputs.iter().all(|input| match key {
-        DescriptorPublicKey::SinglePub(key) => input.partial_sigs.contains_key(&key.key),
+        DescriptorPublicKey::SinglePub(DescriptorSinglePub { key, .. }) => check(input, key),
         DescriptorPublicKey::XPub(xpub) => {
             let pubkey = input
                 .bip32_derivation
@@ -761,14 +796,49 @@ fn signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) ->
                 .map(|(p, _)| p);
             //TODO check actual derivation matches
             match pubkey {
-                Some(pubkey) => input.partial_sigs.contains_key(pubkey),
+                Some(pubkey) => check(input, &map(pubkey)),
                 None => false,
             }
         }
     })
 }
 
-impl<Ctx: ScriptContext> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
+fn ecdsa_signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool {
+    generic_sig_in_psbt(
+        psbt,
+        key,
+        secp,
+        |pk| SinglePubKey::FullKey(PublicKey::new(*pk)),
+        |input, pk| match pk {
+            SinglePubKey::FullKey(pk) => input.partial_sigs.contains_key(pk),
+            _ => false,
+        },
+    )
+}
+
+fn schnorr_signature_in_psbt(psbt: &Psbt, key: &DescriptorPublicKey, secp: &SecpCtx) -> bool {
+    generic_sig_in_psbt(
+        psbt,
+        key,
+        secp,
+        |pk| SinglePubKey::XOnly((*pk).into()),
+        |input, pk| {
+            let pk = match pk {
+                SinglePubKey::XOnly(pk) => pk,
+                _ => return false,
+            };
+
+            // This assumes the internal key is never used in the script leaves, which I think is
+            // reasonable
+            match &input.tap_internal_key {
+                Some(ik) if ik == pk => input.tap_key_sig.is_some(),
+                _ => input.tap_script_sigs.keys().any(|(sk, _)| sk == pk),
+            }
+        },
+    )
+}
+
+impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublicKey, Ctx> {
     fn extract_policy(
         &self,
         signers: &SignersContainer,
@@ -840,9 +910,15 @@ 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, signers, build_sat, *k, false, secp)?
-            }
+            Terminal::Multi(k, pks) | Terminal::MultiA(k, pks) => Policy::make_multisig(
+                pks,
+                signers,
+                build_sat,
+                *k,
+                false,
+                !Ctx::as_enum().is_taproot(),
+                secp,
+            )?,
             // Identities
             Terminal::Alt(inner)
             | Terminal::Swap(inner)
@@ -944,6 +1020,7 @@ impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
                 build_sat,
                 keys.k,
                 true,
+                true,
                 secp,
             )?)
         }
@@ -967,6 +1044,19 @@ impl ExtractPolicy for Descriptor<DescriptorPublicKey> {
                 WshInner::SortedMulti(ref keys) => make_sortedmulti(keys, signers, build_sat, secp),
             },
             Descriptor::Bare(ms) => Ok(ms.as_inner().extract_policy(signers, build_sat, secp)?),
+            Descriptor::Tr(tr) => {
+                let mut items = vec![signature(tr.internal_key(), signers, build_sat, secp)];
+                items.append(
+                    &mut tr
+                        .iter_scripts()
+                        .filter_map(|(_, ms)| {
+                            ms.extract_policy(signers, build_sat, secp).transpose()
+                        })
+                        .collect::<Result<Vec<_>, _>>()?,
+                );
+
+                Ok(Policy::make_thresh(items, 1)?)
+            }
         }
     }
 }
@@ -978,7 +1068,7 @@ mod test {
 
     use super::*;
     use crate::descriptor::derived::AsDerived;
-    use crate::descriptor::policy::SatisfiableItem::{Multisig, Signature, Thresh};
+    use crate::descriptor::policy::SatisfiableItem::{EcdsaSignature, Multisig, Thresh};
     use crate::keys::{DescriptorKey, IntoDescriptorKey};
     use crate::wallet::signer::SignersContainer;
     use bitcoin::secp256k1::Secp256k1;
@@ -1000,7 +1090,7 @@ mod test {
     ) -> (DescriptorKey<Ctx>, DescriptorKey<Ctx>, Fingerprint) {
         let path = bip32::DerivationPath::from_str(path).unwrap();
         let tprv = bip32::ExtendedPrivKey::from_str(tprv).unwrap();
-        let tpub = bip32::ExtendedPubKey::from_private(secp, &tprv);
+        let tpub = bip32::ExtendedPubKey::from_priv(secp, &tprv);
         let fingerprint = tprv.fingerprint(secp);
         let prvkey = (tprv, path.clone()).into_descriptor_key().unwrap();
         let pubkey = (tpub, path).into_descriptor_key().unwrap();
@@ -1026,7 +1116,7 @@ mod test {
             .unwrap();
 
         assert!(
-            matches!(&policy.item, Signature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
+            matches!(&policy.item, EcdsaSignature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
         );
         assert!(matches!(&policy.contribution, Satisfaction::None));
 
@@ -1041,7 +1131,7 @@ mod test {
             .unwrap();
 
         assert!(
-            matches!(&policy.item, Signature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
+            matches!(&policy.item, EcdsaSignature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
         );
         assert!(
             matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None)
@@ -1194,7 +1284,7 @@ mod test {
             .unwrap();
 
         assert!(
-            matches!(&policy.item, Signature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
+            matches!(&policy.item, EcdsaSignature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
         );
         assert!(matches!(&policy.contribution, Satisfaction::None));
 
@@ -1210,7 +1300,7 @@ mod test {
             .unwrap();
 
         assert!(
-            matches!(&policy.item, Signature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
+            matches!(&policy.item, EcdsaSignature(pk_or_f) if pk_or_f.fingerprint.unwrap() == fingerprint)
         );
         assert!(
             matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None)
index 14e16e7bbab8e8f8c4778c7c472bd445c6123280..320cca1f567843bc25d7e52e23d492eee188899a 100644 (file)
@@ -20,12 +20,12 @@ use std::str::FromStr;
 use bitcoin::secp256k1::{self, Secp256k1, Signing};
 
 use bitcoin::util::bip32;
-use bitcoin::{Network, PrivateKey, PublicKey};
+use bitcoin::{Network, PrivateKey, PublicKey, XOnlyPublicKey};
 
 use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard};
 pub use miniscript::descriptor::{
     DescriptorPublicKey, DescriptorSecretKey, DescriptorSinglePriv, DescriptorSinglePub, KeyMap,
-    SortedMultiVec,
+    SinglePubKey, SortedMultiVec,
 };
 pub use miniscript::ScriptContext;
 use miniscript::{Miniscript, Terminal};
@@ -127,6 +127,8 @@ pub enum ScriptContextEnum {
     Legacy,
     /// Segwitv0 scripts
     Segwitv0,
+    /// Taproot scripts
+    Tap,
 }
 
 impl ScriptContextEnum {
@@ -139,6 +141,11 @@ impl ScriptContextEnum {
     pub fn is_segwit_v0(&self) -> bool {
         self == &ScriptContextEnum::Segwitv0
     }
+
+    /// Returns whether the script context is [`ScriptContextEnum::Tap`]
+    pub fn is_taproot(&self) -> bool {
+        self == &ScriptContextEnum::Tap
+    }
 }
 
 /// Trait that adds extra useful methods to [`ScriptContext`]s
@@ -155,6 +162,11 @@ pub trait ExtScriptContext: ScriptContext {
     fn is_segwit_v0() -> bool {
         Self::as_enum().is_segwit_v0()
     }
+
+    /// Returns whether the script context is [`Tap`](miniscript::Tap), aka Taproot or Segwit V1
+    fn is_taproot() -> bool {
+        Self::as_enum().is_taproot()
+    }
 }
 
 impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
@@ -162,6 +174,7 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
         match TypeId::of::<Ctx>() {
             t if t == TypeId::of::<miniscript::Legacy>() => ScriptContextEnum::Legacy,
             t if t == TypeId::of::<miniscript::Segwitv0>() => ScriptContextEnum::Segwitv0,
+            t if t == TypeId::of::<miniscript::Tap>() => ScriptContextEnum::Tap,
             _ => unimplemented!("Unknown ScriptContext type"),
         }
     }
@@ -212,7 +225,7 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
 ///
 /// use bdk::keys::{
 ///     mainnet_network, DescriptorKey, DescriptorPublicKey, DescriptorSinglePub,
-///     IntoDescriptorKey, KeyError, ScriptContext,
+///     IntoDescriptorKey, KeyError, ScriptContext, SinglePubKey,
 /// };
 ///
 /// pub struct MyKeyType {
@@ -224,7 +237,7 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
 ///         Ok(DescriptorKey::from_public(
 ///             DescriptorPublicKey::SinglePub(DescriptorSinglePub {
 ///                 origin: None,
-///                 key: self.pubkey,
+///                 key: SinglePubKey::FullKey(self.pubkey),
 ///             }),
 ///             mainnet_network(),
 ///         ))
@@ -319,7 +332,6 @@ impl<Ctx: ScriptContext> ExtendedKey<Ctx> {
         match self {
             ExtendedKey::Private((mut xprv, _)) => {
                 xprv.network = network;
-                xprv.private_key.network = network;
                 Some(xprv)
             }
             ExtendedKey::Public(_) => None,
@@ -334,7 +346,7 @@ impl<Ctx: ScriptContext> ExtendedKey<Ctx> {
         secp: &Secp256k1<C>,
     ) -> bip32::ExtendedPubKey {
         let mut xpub = match self {
-            ExtendedKey::Private((xprv, _)) => bip32::ExtendedPubKey::from_private(secp, &xprv),
+            ExtendedKey::Private((xprv, _)) => bip32::ExtendedPubKey::from_priv(secp, &xprv),
             ExtendedKey::Public((xpub, _)) => xpub,
         };
 
@@ -388,7 +400,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
 ///             network: self.network,
 ///             depth: 0,
 ///             parent_fingerprint: bip32::Fingerprint::default(),
-///             private_key: self.key_data,
+///             private_key: self.key_data.inner,
 ///             chain_code: bip32::ChainCode::from(self.chain_code.as_ref()),
 ///             child_number: bip32::ChildNumber::Normal { index: 0 },
 ///         };
@@ -420,7 +432,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
 ///             network: bitcoin::Network::Bitcoin, // pick an arbitrary network here
 ///             depth: 0,
 ///             parent_fingerprint: bip32::Fingerprint::default(),
-///             private_key: self.key_data,
+///             private_key: self.key_data.inner,
 ///             chain_code: bip32::ChainCode::from(self.chain_code.as_ref()),
 ///             child_number: bip32::ChildNumber::Normal { index: 0 },
 ///         };
@@ -698,11 +710,11 @@ impl<Ctx: ScriptContext> GeneratableKey<Ctx> for PrivateKey {
         entropy: Self::Entropy,
     ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
         // pick a arbitrary network here, but say that we support all of them
-        let key = secp256k1::SecretKey::from_slice(&entropy)?;
+        let inner = secp256k1::SecretKey::from_slice(&entropy)?;
         let private_key = PrivateKey {
             compressed: options.compressed,
             network: Network::Bitcoin,
-            key,
+            inner,
         };
 
         Ok(GeneratedKey::new(private_key, any_network()))
@@ -841,7 +853,17 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorPublicKey {
 impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PublicKey {
     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
         DescriptorPublicKey::SinglePub(DescriptorSinglePub {
-            key: self,
+            key: SinglePubKey::FullKey(self),
+            origin: None,
+        })
+        .into_descriptor_key()
+    }
+}
+
+impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for XOnlyPublicKey {
+    fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
+        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+            key: SinglePubKey::XOnly(self),
             origin: None,
         })
         .into_descriptor_key()
@@ -943,29 +965,6 @@ pub mod test {
         );
     }
 
-    #[test]
-    fn test_keys_wif_network() {
-        // test mainnet wif
-        let generated_xprv: GeneratedKey<_, miniscript::Segwitv0> =
-            bip32::ExtendedPrivKey::generate_with_entropy_default(TEST_ENTROPY).unwrap();
-        let xkey = generated_xprv.into_extended_key().unwrap();
-
-        let network = Network::Bitcoin;
-        let xprv = xkey.into_xprv(network).unwrap();
-        let wif = PrivateKey::from_wif(&xprv.private_key.to_wif()).unwrap();
-        assert_eq!(wif.network, network);
-
-        // test testnet wif
-        let generated_xprv: GeneratedKey<_, miniscript::Segwitv0> =
-            bip32::ExtendedPrivKey::generate_with_entropy_default(TEST_ENTROPY).unwrap();
-        let xkey = generated_xprv.into_extended_key().unwrap();
-
-        let network = Network::Testnet;
-        let xprv = xkey.into_xprv(network).unwrap();
-        let wif = PrivateKey::from_wif(&xprv.private_key.to_wif()).unwrap();
-        assert_eq!(wif.network, network);
-    }
-
     #[cfg(feature = "keys-bip39")]
     #[test]
     fn test_keys_wif_network_bip39() {
@@ -977,8 +976,7 @@ pub mod test {
         .into_extended_key()
         .unwrap();
         let xprv = xkey.into_xprv(Network::Testnet).unwrap();
-        let wif = PrivateKey::from_wif(&xprv.private_key.to_wif()).unwrap();
 
-        assert_eq!(wif.network, Network::Testnet);
+        assert_eq!(xprv.network, Network::Testnet);
     }
 }
index 9ca5e2db7b07c3620a71ab4d867b0448f145a3dc..e02362a450567dc57fa677a104401be2bb2ad160 100644 (file)
@@ -19,7 +19,7 @@ pub trait PsbtUtils {
 impl PsbtUtils for Psbt {
     #[allow(clippy::all)] // We want to allow `manual_map` but it is too new.
     fn get_utxo_for(&self, input_index: usize) -> Option<TxOut> {
-        let tx = &self.global.unsigned_tx;
+        let tx = &self.unsigned_tx;
 
         if input_index >= tx.input.len() {
             return None;
@@ -93,7 +93,7 @@ mod test {
         let mut builder = wallet.build_tx();
         builder.add_recipient(send_to.script_pubkey(), 10_000);
         let (mut psbt, _) = builder.finish().unwrap();
-        psbt.global.unsigned_tx.input.push(TxIn::default());
+        psbt.unsigned_tx.input.push(TxIn::default());
         let options = SignOptions {
             trust_witness_utxo: true,
             ..Default::default()
@@ -112,10 +112,9 @@ mod test {
 
         // add a finalized input
         psbt.inputs.push(psbt_bip.inputs[0].clone());
-        psbt.global
-            .unsigned_tx
+        psbt.unsigned_tx
             .input
-            .push(psbt_bip.global.unsigned_tx.input[0].clone());
+            .push(psbt_bip.unsigned_tx.input[0].clone());
 
         let _ = wallet.sign(&mut psbt, SignOptions::default()).unwrap();
     }
index a3432b41f0a764914f17fe04ee4579e6093576f2..556d98f64932dc6d5383b17cd2b13b7f49af3108 100644 (file)
@@ -2,7 +2,7 @@ use crate::testutils::TestIncomingTx;
 use bitcoin::consensus::encode::{deserialize, serialize};
 use bitcoin::hashes::hex::{FromHex, ToHex};
 use bitcoin::hashes::sha256d;
-use bitcoin::{Address, Amount, Script, Transaction, Txid};
+use bitcoin::{Address, Amount, Script, Transaction, Txid, Witness};
 pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
 pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
 use core::str::FromStr;
@@ -193,7 +193,7 @@ impl TestClient {
                 previous_output: OutPoint::null(),
                 script_sig: Builder::new().push_int(height).into_script(),
                 sequence: 0xFFFFFFFF,
-                witness: vec![witness_reserved_value],
+                witness: Witness::from_vec(vec![witness_reserved_value]),
             }],
             output: vec![],
         };
@@ -203,23 +203,31 @@ impl TestClient {
 
         let mut block = Block { header, txdata };
 
-        let witness_root = block.witness_root();
-        let witness_commitment =
-            Block::compute_witness_commitment(&witness_root, &coinbase_tx.input[0].witness[0]);
+        if let Some(witness_root) = block.witness_root() {
+            let witness_commitment = Block::compute_witness_commitment(
+                &witness_root,
+                &coinbase_tx.input[0]
+                    .witness
+                    .last()
+                    .expect("Should contain the witness reserved value"),
+            );
 
-        // now update and replace the coinbase tx
-        let mut coinbase_witness_commitment_script = vec![0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
-        coinbase_witness_commitment_script.extend_from_slice(&witness_commitment);
+            // now update and replace the coinbase tx
+            let mut coinbase_witness_commitment_script = vec![0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
+            coinbase_witness_commitment_script.extend_from_slice(&witness_commitment);
+
+            coinbase_tx.output.push(TxOut {
+                value: 0,
+                script_pubkey: coinbase_witness_commitment_script.into(),
+            });
+        }
 
-        coinbase_tx.output.push(TxOut {
-            value: 0,
-            script_pubkey: coinbase_witness_commitment_script.into(),
-        });
         block.txdata[0] = coinbase_tx;
 
         // set merkle root
-        let merkle_root = block.merkle_root();
-        block.header.merkle_root = merkle_root;
+        if let Some(merkle_root) = block.compute_merkle_root() {
+            block.header.merkle_root = merkle_root;
+        }
 
         assert!(block.check_merkle_root());
         assert!(block.check_witness_commitment());
index f05c9df48db74f14a0e4c7899368102c45d399c4..3af478494cfc13a34574ee3db932e79759180d14 100644 (file)
 #[cfg(feature = "test-blockchains")]
 pub mod blockchain_tests;
 
-use bitcoin::secp256k1::{Secp256k1, Verification};
-use bitcoin::{Address, PublicKey, Txid};
-
-use miniscript::descriptor::DescriptorPublicKey;
-use miniscript::{Descriptor, MiniscriptKey, TranslatePk};
+use bitcoin::{Address, Txid};
 
 #[derive(Clone, Debug)]
 pub struct TestIncomingInput {
@@ -96,39 +92,6 @@ impl TestIncomingTx {
     }
 }
 
-#[doc(hidden)]
-pub trait TranslateDescriptor {
-    // derive and translate a `Descriptor<DescriptorPublicKey>` into a `Descriptor<PublicKey>`
-    fn derive_translated<C: Verification>(
-        &self,
-        secp: &Secp256k1<C>,
-        index: u32,
-    ) -> Descriptor<PublicKey>;
-}
-
-impl TranslateDescriptor for Descriptor<DescriptorPublicKey> {
-    fn derive_translated<C: Verification>(
-        &self,
-        secp: &Secp256k1<C>,
-        index: u32,
-    ) -> Descriptor<PublicKey> {
-        let translate = |key: &DescriptorPublicKey| -> PublicKey {
-            match key {
-                DescriptorPublicKey::XPub(xpub) => {
-                    xpub.xkey
-                        .derive_pub(secp, &xpub.derivation_path)
-                        .expect("hardened derivation steps")
-                        .public_key
-                }
-                DescriptorPublicKey::SinglePub(key) => key.key,
-            }
-        };
-
-        self.derive(index)
-            .translate_pk_infallible(|pk| translate(pk), |pkh| translate(pkh).to_pubkeyhash())
-    }
-}
-
 #[doc(hidden)]
 #[macro_export]
 macro_rules! testutils {
@@ -136,23 +99,23 @@ macro_rules! testutils {
         use $crate::bitcoin::secp256k1::Secp256k1;
         use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait};
 
-        use $crate::testutils::TranslateDescriptor;
+        use $crate::descriptor::AsDerived;
 
         let secp = Secp256k1::new();
 
         let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
-        parsed.derive_translated(&secp, $child).address(bitcoin::Network::Regtest).expect("No address form")
+        parsed.as_derived($child, &secp).address(bitcoin::Network::Regtest).expect("No address form")
     });
     ( @internal $descriptors:expr, $child:expr ) => ({
         use $crate::bitcoin::secp256k1::Secp256k1;
         use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait};
 
-        use $crate::testutils::TranslateDescriptor;
+        use $crate::descriptor::AsDerived;
 
         let secp = Secp256k1::new();
 
         let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
-        parsed.derive_translated(&secp, $child).address($crate::bitcoin::Network::Regtest).expect("No address form")
+        parsed.as_derived($child, &secp).address($crate::bitcoin::Network::Regtest).expect("No address form")
     });
     ( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
     ( @i $descriptors:expr, $child:expr ) => ({ testutils!(@internal $descriptors, $child) });
index 20e0c2630f09224112e5265d0bebd9df333588cd..0031bbb88497ed3a53bdf8332afda249ba7c2fe3 100644 (file)
@@ -24,11 +24,8 @@ use std::sync::Arc;
 use bitcoin::secp256k1::Secp256k1;
 
 use bitcoin::consensus::encode::serialize;
-use bitcoin::util::base58;
-use bitcoin::util::psbt::raw::Key as PsbtKey;
-use bitcoin::util::psbt::Input;
-use bitcoin::util::psbt::PartiallySignedTransaction as Psbt;
-use bitcoin::{Address, Network, OutPoint, Script, SigHashType, Transaction, TxOut, Txid};
+use bitcoin::util::psbt;
+use bitcoin::{Address, Network, OutPoint, Script, SigHashType, Transaction, TxOut, Txid, Witness};
 
 use miniscript::descriptor::DescriptorTrait;
 use miniscript::psbt::PsbtInputSatisfier;
@@ -542,7 +539,7 @@ where
         &self,
         coin_selection: Cs,
         params: TxParams,
-    ) -> Result<(Psbt, TransactionDetails), Error> {
+    ) -> Result<(psbt::PartiallySignedTransaction, TransactionDetails), Error> {
         let external_policy = self
             .descriptor
             .extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
@@ -703,7 +700,7 @@ where
         let mut outgoing: u64 = 0;
         let mut received: u64 = 0;
 
-        fee_amount += fee_rate.fee_wu(tx.get_weight());
+        fee_amount += fee_rate.fee_wu(tx.weight());
 
         let recipients = params.recipients.iter().map(|(r, v)| (r, *v));
 
@@ -761,7 +758,7 @@ where
                 previous_output: u.outpoint(),
                 script_sig: Script::default(),
                 sequence: n_sequence,
-                witness: vec![],
+                witness: Witness::new(),
             })
             .collect();
 
@@ -884,10 +881,7 @@ where
             return Err(Error::IrreplaceableTransaction);
         }
 
-        let feerate = FeeRate::from_wu(
-            details.fee.ok_or(Error::FeeRateUnavailable)?,
-            tx.get_weight(),
-        );
+        let feerate = FeeRate::from_wu(details.fee.ok_or(Error::FeeRateUnavailable)?, tx.weight());
 
         // remove the inputs from the tx and process them
         let original_txin = tx.input.drain(..).collect::<Vec<_>>();
@@ -1004,7 +998,11 @@ where
     /// let  finalized = wallet.sign(&mut psbt, SignOptions::default())?;
     /// assert!(finalized, "we should have signed all the inputs");
     /// # Ok::<(), bdk::Error>(())
-    pub fn sign(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, Error> {
+    pub fn sign(
+        &self,
+        psbt: &mut psbt::PartiallySignedTransaction,
+        sign_options: SignOptions,
+    ) -> Result<bool, Error> {
         // this helps us doing our job later
         self.add_input_hd_keypaths(psbt)?;
 
@@ -1023,10 +1021,9 @@ where
         // If the user hasn't explicitly opted-in, refuse to sign the transaction unless every input
         // is using `SIGHASH_ALL`
         if !sign_options.allow_all_sighashes
-            && !psbt
-                .inputs
-                .iter()
-                .all(|i| i.sighash_type.is_none() || i.sighash_type == Some(SigHashType::All))
+            && !psbt.inputs.iter().all(|i| {
+                i.sighash_type.is_none() || i.sighash_type == Some(SigHashType::All.into())
+            })
         {
             return Err(Error::Signer(signer::SignerError::NonStandardSighash));
         }
@@ -1085,8 +1082,12 @@ where
     /// Try to finalize a PSBT
     ///
     /// The [`SignOptions`] can be used to tweak the behavior of the finalizer.
-    pub fn finalize_psbt(&self, psbt: &mut Psbt, sign_options: SignOptions) -> Result<bool, Error> {
-        let tx = &psbt.global.unsigned_tx;
+    pub fn finalize_psbt(
+        &self,
+        psbt: &mut psbt::PartiallySignedTransaction,
+        sign_options: SignOptions,
+    ) -> Result<bool, Error> {
+        let tx = &psbt.unsigned_tx;
         let mut finished = true;
 
         for (n, input) in tx.input.iter().enumerate() {
@@ -1383,10 +1384,8 @@ where
         tx: Transaction,
         selected: Vec<Utxo>,
         params: TxParams,
-    ) -> Result<Psbt, Error> {
-        use bitcoin::util::psbt::serialize::Serialize;
-
-        let mut psbt = Psbt::from_unsigned_tx(tx)?;
+    ) -> Result<psbt::PartiallySignedTransaction, Error> {
+        let mut psbt = psbt::PartiallySignedTransaction::from_unsigned_tx(tx)?;
 
         if params.add_global_xpubs {
             let mut all_xpubs = self.descriptor.get_extended_keys()?;
@@ -1395,13 +1394,6 @@ where
             }
 
             for xpub in all_xpubs {
-                let serialized_xpub = base58::from_check(&xpub.xkey.to_string())
-                    .expect("Internal serialization error");
-                let key = PsbtKey {
-                    type_value: 0x01,
-                    key: serialized_xpub,
-                };
-
                 let origin = match xpub.origin {
                     Some(origin) => origin,
                     None if xpub.xkey.depth == 0 => {
@@ -1410,7 +1402,7 @@ where
                     _ => return Err(Error::MissingKeyOrigin(xpub.xkey.to_string())),
                 };
 
-                psbt.global.unknown.insert(key, origin.serialize());
+                psbt.xpub.insert(xpub.xkey, origin);
             }
         }
 
@@ -1420,11 +1412,7 @@ where
             .collect::<HashMap<_, _>>();
 
         // add metadata for the inputs
-        for (psbt_input, input) in psbt
-            .inputs
-            .iter_mut()
-            .zip(psbt.global.unsigned_tx.input.iter())
-        {
+        for (psbt_input, input) in psbt.inputs.iter_mut().zip(psbt.unsigned_tx.input.iter()) {
             let utxo = match lookup_output.remove(&input.previous_output) {
                 Some(utxo) => utxo,
                 None => continue,
@@ -1436,9 +1424,9 @@ where
                         match self.get_psbt_input(utxo, params.sighash, params.only_witness_utxo) {
                             Ok(psbt_input) => psbt_input,
                             Err(e) => match e {
-                                Error::UnknownUtxo => Input {
+                                Error::UnknownUtxo => psbt::Input {
                                     sighash_type: params.sighash,
-                                    ..Input::default()
+                                    ..psbt::Input::default()
                                 },
                                 _ => return Err(e),
                             },
@@ -1463,10 +1451,7 @@ where
         self.add_input_hd_keypaths(&mut psbt)?;
 
         // add metadata for the outputs
-        for (psbt_output, tx_output) in psbt
-            .outputs
-            .iter_mut()
-            .zip(psbt.global.unsigned_tx.output.iter())
+        for (psbt_output, tx_output) in psbt.outputs.iter_mut().zip(psbt.unsigned_tx.output.iter())
         {
             if let Some((keychain, child)) = self
                 .database
@@ -1491,9 +1476,9 @@ where
     pub fn get_psbt_input(
         &self,
         utxo: LocalUtxo,
-        sighash_type: Option<SigHashType>,
+        sighash_type: Option<psbt::PsbtSighashType>,
         only_witness_utxo: bool,
-    ) -> Result<Input, Error> {
+    ) -> Result<psbt::Input, Error> {
         // Try to find the prev_script in our db to figure out if this is internal or external,
         // and the derivation index
         let (keychain, child) = self
@@ -1502,9 +1487,9 @@ where
             .get_path_from_script_pubkey(&utxo.txout.script_pubkey)?
             .ok_or(Error::UnknownUtxo)?;
 
-        let mut psbt_input = Input {
+        let mut psbt_input = psbt::Input {
             sighash_type,
-            ..Input::default()
+            ..psbt::Input::default()
         };
 
         let desc = self.get_descriptor_for_keychain(keychain);
@@ -1526,7 +1511,10 @@ where
         Ok(psbt_input)
     }
 
-    fn add_input_hd_keypaths(&self, psbt: &mut Psbt) -> Result<(), Error> {
+    fn add_input_hd_keypaths(
+        &self,
+        psbt: &mut psbt::PartiallySignedTransaction,
+    ) -> Result<(), Error> {
         let mut input_utxos = Vec::with_capacity(psbt.inputs.len());
         for n in 0..psbt.inputs.len() {
             input_utxos.push(psbt.get_utxo_for(n).clone());
@@ -1808,7 +1796,7 @@ pub(crate) mod test {
             $(
                 $( $add_signature )*
                 for txin in &mut tx.input {
-                    txin.witness.push([0x00; 108].to_vec()); // fake signature
+                    txin.witness.push([0x00; 108]); // fake signature
                 }
             )*
 
@@ -1820,7 +1808,7 @@ pub(crate) mod test {
                 dust_change = true;
             )*
 
-            let tx_fee_rate = FeeRate::from_wu($fees, tx.get_weight());
+            let tx_fee_rate = FeeRate::from_wu($fees, tx.weight());
             let fee_rate = $fee_rate;
 
             if !dust_change {
@@ -1886,7 +1874,7 @@ pub(crate) mod test {
             .version(42);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.version, 42);
+        assert_eq!(psbt.unsigned_tx.version, 42);
     }
 
     #[test]
@@ -1897,7 +1885,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.lock_time, 0);
+        assert_eq!(psbt.unsigned_tx.lock_time, 0);
     }
 
     #[test]
@@ -1908,7 +1896,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.lock_time, 100_000);
+        assert_eq!(psbt.unsigned_tx.lock_time, 100_000);
     }
 
     #[test]
@@ -1921,7 +1909,7 @@ pub(crate) mod test {
             .nlocktime(630_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.lock_time, 630_000);
+        assert_eq!(psbt.unsigned_tx.lock_time, 630_000);
     }
 
     #[test]
@@ -1934,7 +1922,7 @@ pub(crate) mod test {
             .nlocktime(630_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.lock_time, 630_000);
+        assert_eq!(psbt.unsigned_tx.lock_time, 630_000);
     }
 
     #[test]
@@ -1959,7 +1947,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 6);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 6);
     }
 
     #[test]
@@ -1973,7 +1961,7 @@ pub(crate) mod test {
         let (psbt, _) = builder.finish().unwrap();
         // When CSV is enabled it takes precedence over the rbf value (unless forced by the user).
         // It will be set to the OP_CSV value, in this case 6
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 6);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 6);
     }
 
     #[test]
@@ -1998,7 +1986,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xFFFFFFFE);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xFFFFFFFE);
     }
 
     #[test]
@@ -2023,7 +2011,7 @@ pub(crate) mod test {
             .enable_rbf_with_sequence(0xDEADBEEF);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xDEADBEEF);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xDEADBEEF);
     }
 
     #[test]
@@ -2034,7 +2022,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
     }
 
     #[test]
@@ -2059,9 +2047,9 @@ pub(crate) mod test {
         builder.drain_to(addr.script_pubkey()).drain_wallet();
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
+        assert_eq!(psbt.unsigned_tx.output.len(), 1);
         assert_eq!(
-            psbt.global.unsigned_tx.output[0].value,
+            psbt.unsigned_tx.output[0].value,
             50_000 - details.fee.unwrap_or(0)
         );
     }
@@ -2078,7 +2066,7 @@ pub(crate) mod test {
             .drain_wallet();
         let (psbt, details) = builder.finish().unwrap();
         dbg!(&psbt);
-        let outputs = psbt.global.unsigned_tx.output;
+        let outputs = psbt.unsigned_tx.output;
 
         assert_eq!(outputs.len(), 2);
         let main_output = outputs
@@ -2129,9 +2117,9 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.fee.unwrap_or(0), 100);
-        assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
+        assert_eq!(psbt.unsigned_tx.output.len(), 1);
         assert_eq!(
-            psbt.global.unsigned_tx.output[0].value,
+            psbt.unsigned_tx.output[0].value,
             50_000 - details.fee.unwrap_or(0)
         );
     }
@@ -2148,9 +2136,9 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.fee.unwrap_or(0), 0);
-        assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
+        assert_eq!(psbt.unsigned_tx.output.len(), 1);
         assert_eq!(
-            psbt.global.unsigned_tx.output[0].value,
+            psbt.unsigned_tx.output[0].value,
             50_000 - details.fee.unwrap_or(0)
         );
     }
@@ -2180,10 +2168,10 @@ pub(crate) mod test {
             .ordering(TxOrdering::Untouched);
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.output.len(), 2);
-        assert_eq!(psbt.global.unsigned_tx.output[0].value, 25_000);
+        assert_eq!(psbt.unsigned_tx.output.len(), 2);
+        assert_eq!(psbt.unsigned_tx.output[0].value, 25_000);
         assert_eq!(
-            psbt.global.unsigned_tx.output[1].value,
+            psbt.unsigned_tx.output[1].value,
             25_000 - details.fee.unwrap_or(0)
         );
     }
@@ -2196,8 +2184,8 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 49_800);
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
-        assert_eq!(psbt.global.unsigned_tx.output[0].value, 49_800);
+        assert_eq!(psbt.unsigned_tx.output.len(), 1);
+        assert_eq!(psbt.unsigned_tx.output[0].value, 49_800);
         assert_eq!(details.fee.unwrap_or(0), 200);
     }
 
@@ -2226,13 +2214,13 @@ pub(crate) mod test {
             .ordering(super::tx_builder::TxOrdering::Bip69Lexicographic);
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.output.len(), 3);
+        assert_eq!(psbt.unsigned_tx.output.len(), 3);
         assert_eq!(
-            psbt.global.unsigned_tx.output[0].value,
+            psbt.unsigned_tx.output[0].value,
             10_000 - details.fee.unwrap_or(0)
         );
-        assert_eq!(psbt.global.unsigned_tx.output[1].value, 10_000);
-        assert_eq!(psbt.global.unsigned_tx.output[2].value, 30_000);
+        assert_eq!(psbt.unsigned_tx.output[1].value, 10_000);
+        assert_eq!(psbt.unsigned_tx.output[2].value, 30_000);
     }
 
     #[test]
@@ -2253,12 +2241,12 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .add_recipient(addr.script_pubkey(), 30_000)
-            .sighash(bitcoin::SigHashType::Single);
+            .sighash(bitcoin::SigHashType::Single.into());
         let (psbt, _) = builder.finish().unwrap();
 
         assert_eq!(
             psbt.inputs[0].sighash_type,
-            Some(bitcoin::SigHashType::Single)
+            Some(bitcoin::SigHashType::Single.into())
         );
     }
 
@@ -2450,7 +2438,7 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(
-            psbt.global.unsigned_tx.input.len(),
+            psbt.unsigned_tx.input.len(),
             2,
             "should add an additional input since 25_000 < 30_000"
         );
@@ -2507,7 +2495,7 @@ pub(crate) mod test {
             .policy_path(path, KeychainKind::External);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
     }
 
     #[test]
@@ -2526,14 +2514,13 @@ pub(crate) mod test {
             .policy_path(path, KeychainKind::External);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.global.unsigned_tx.input[0].sequence, 144);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, 144);
     }
 
     #[test]
     fn test_create_tx_global_xpubs_with_origin() {
         use bitcoin::hashes::hex::FromHex;
-        use bitcoin::util::base58;
-        use bitcoin::util::psbt::raw::Key;
+        use bitcoin::util::bip32;
 
         let (wallet, _, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
         let addr = wallet.get_address(New).unwrap();
@@ -2543,16 +2530,12 @@ pub(crate) mod test {
             .add_global_xpubs();
         let (psbt, _) = builder.finish().unwrap();
 
-        let type_value = 0x01;
-        let key = base58::from_check("tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3").unwrap();
-
-        let psbt_key = Key { type_value, key };
-
-        // This key has an explicit origin, so it will be encoded here
-        let value_bytes = Vec::<u8>::from_hex("73756c7f30000080000000800000008002000080").unwrap();
+        let key = bip32::ExtendedPubKey::from_str("tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3").unwrap();
+        let fingerprint = bip32::Fingerprint::from_hex("73756c7f").unwrap();
+        let path = bip32::DerivationPath::from_str("m/48'/0'/0'/2'").unwrap();
 
-        assert_eq!(psbt.global.unknown.len(), 1);
-        assert_eq!(psbt.global.unknown.get(&psbt_key), Some(&value_bytes));
+        assert_eq!(psbt.xpub.len(), 1);
+        assert_eq!(psbt.xpub.get(&key), Some(&(fingerprint, path)));
     }
 
     #[test]
@@ -2588,8 +2571,7 @@ pub(crate) mod test {
         );
 
         assert!(
-            psbt.global
-                .unsigned_tx
+            psbt.unsigned_tx
                 .input
                 .iter()
                 .any(|input| input.previous_output == utxo.outpoint),
@@ -2794,8 +2776,7 @@ pub(crate) mod test {
     #[test]
     fn test_create_tx_global_xpubs_master_without_origin() {
         use bitcoin::hashes::hex::FromHex;
-        use bitcoin::util::base58;
-        use bitcoin::util::psbt::raw::Key;
+        use bitcoin::util::bip32;
 
         let (wallet, _, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
         let addr = wallet.get_address(New).unwrap();
@@ -2805,17 +2786,14 @@ pub(crate) mod test {
             .add_global_xpubs();
         let (psbt, _) = builder.finish().unwrap();
 
-        let type_value = 0x01;
-        let key = base58::from_check("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
+        let key = bip32::ExtendedPubKey::from_str("tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL").unwrap();
+        let fingerprint = bip32::Fingerprint::from_hex("997a323b").unwrap();
 
-        let psbt_key = Key { type_value, key };
-
-        // This key doesn't have an explicit origin, but it's a master key (depth = 0). So we encode
-        // its fingerprint directly and an empty path
-        let value_bytes = Vec::<u8>::from_hex("997a323b").unwrap();
-
-        assert_eq!(psbt.global.unknown.len(), 1);
-        assert_eq!(psbt.global.unknown.get(&psbt_key), Some(&value_bytes));
+        assert_eq!(psbt.xpub.len(), 1);
+        assert_eq!(
+            psbt.xpub.get(&key),
+            Some(&(fingerprint, bip32::DerivationPath::default()))
+        );
     }
 
     #[test]
@@ -2937,7 +2915,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -2962,7 +2940,7 @@ pub(crate) mod test {
         );
         assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
             tx.output
@@ -2997,7 +2975,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3028,7 +3006,7 @@ pub(crate) mod test {
             original_details.fee.unwrap_or(0)
         );
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
             tx.output
@@ -3063,7 +3041,7 @@ pub(crate) mod test {
         let mut tx = psbt.extract_tx();
         let txid = tx.txid();
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3087,7 +3065,7 @@ pub(crate) mod test {
         assert_eq!(details.sent, original_details.sent);
         assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 1);
         assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent);
 
@@ -3107,7 +3085,7 @@ pub(crate) mod test {
         let mut tx = psbt.extract_tx();
         let txid = tx.txid();
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3131,7 +3109,7 @@ pub(crate) mod test {
         assert_eq!(details.sent, original_details.sent);
         assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.output.len(), 1);
         assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent);
 
@@ -3163,7 +3141,7 @@ pub(crate) mod test {
         let mut tx = psbt.extract_tx();
         let txid = tx.txid();
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3220,7 +3198,7 @@ pub(crate) mod test {
         let mut tx = psbt.extract_tx();
         let txid = tx.txid();
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3261,7 +3239,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3282,7 +3260,7 @@ pub(crate) mod test {
         assert_eq!(details.sent, original_details.sent + 25_000);
         assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
@@ -3324,7 +3302,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3345,7 +3323,7 @@ pub(crate) mod test {
         assert_eq!(details.sent, original_details.sent + 25_000);
         assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
@@ -3395,7 +3373,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3422,7 +3400,7 @@ pub(crate) mod test {
             75_000 - original_send_all_amount - details.fee.unwrap_or(0)
         );
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
@@ -3466,7 +3444,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3493,7 +3471,7 @@ pub(crate) mod test {
         assert_eq!(details.fee.unwrap_or(0), 30_000);
         assert_eq!(details.received, 0);
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
         assert_eq!(tx.output.len(), 1);
         assert_eq!(
@@ -3527,7 +3505,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3556,7 +3534,7 @@ pub(crate) mod test {
         assert_eq!(details.sent, original_details.sent + 25_000);
         assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
@@ -3598,7 +3576,7 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the new utxos, we know they can't be used anyways
         for txin in &mut tx.input {
-            txin.witness.push([0x00; 108].to_vec()); // fake signature
+            txin.witness.push([0x00; 108]); // fake signature
             wallet
                 .database
                 .borrow_mut()
@@ -3627,7 +3605,7 @@ pub(crate) mod test {
         assert_eq!(details.sent, original_details.sent + 25_000);
         assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
-        let tx = &psbt.global.unsigned_tx;
+        let tx = &psbt.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
         assert_eq!(tx.output.len(), 2);
         assert_eq!(
@@ -3785,7 +3763,7 @@ pub(crate) mod test {
         };
 
         psbt.inputs.push(dud_input);
-        psbt.global.unsigned_tx.input.push(bitcoin::TxIn::default());
+        psbt.unsigned_tx.input.push(bitcoin::TxIn::default());
         let is_final = wallet
             .sign(
                 &mut psbt,
@@ -3814,7 +3792,7 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .drain_to(addr.script_pubkey())
-            .sighash(sighash)
+            .sighash(sighash.into())
             .drain_wallet();
         let (mut psbt, _) = builder.finish().unwrap();
 
@@ -3847,8 +3825,8 @@ pub(crate) mod test {
 
         let extracted = psbt.extract_tx();
         assert_eq!(
-            *extracted.input[0].witness[0].last().unwrap(),
-            sighash.as_u32() as u8,
+            *extracted.input[0].witness.to_vec()[0].last().unwrap(),
+            sighash.to_u32() as u8,
             "The signature should have been made with the right sighash"
         );
     }
index d1db87d319fe692b5cd5fa2b9a7eff100cf481f2..35bbb84548096737fbef4d3340d296e3c49870e0 100644 (file)
@@ -91,10 +91,11 @@ use std::sync::Arc;
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::script::Builder as ScriptBuilder;
 use bitcoin::hashes::{hash160, Hash};
+use bitcoin::secp256k1;
 use bitcoin::secp256k1::{Message, Secp256k1};
 use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint};
-use bitcoin::util::{bip143, psbt};
-use bitcoin::{PrivateKey, Script, SigHash, SigHashType};
+use bitcoin::util::{bip143, ecdsa, psbt};
+use bitcoin::{PrivateKey, PublicKey, Script, SigHashType, Sighash};
 
 use miniscript::descriptor::{DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey, KeyMap};
 use miniscript::{Legacy, MiniscriptKey, Segwitv0};
@@ -153,6 +154,8 @@ pub enum SignerError {
     /// To enable signing transactions with non-standard sighashes set
     /// [`SignOptions::allow_all_sighashes`] to `true`.
     NonStandardSighash,
+    /// Invalid SIGHASH for the signing context in use
+    InvalidSighash,
 }
 
 impl fmt::Display for SignerError {
@@ -245,10 +248,16 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
             None => self.xkey.derive_priv(secp, &full_path).unwrap(),
         };
 
-        if &derived_key.private_key.public_key(secp) != public_key {
+        if &secp256k1::PublicKey::from_secret_key(secp, &derived_key.private_key) != public_key {
             Err(SignerError::InvalidKey)
         } else {
-            derived_key.private_key.sign(psbt, Some(input_index), secp)
+            // HD wallets imply compressed keys
+            PrivateKey {
+                compressed: true,
+                network: self.xkey.network,
+                inner: derived_key.private_key,
+            }
+            .sign(psbt, Some(input_index), secp)
         }
     }
 
@@ -273,7 +282,7 @@ impl Signer for PrivateKey {
         secp: &SecpCtx,
     ) -> Result<(), SignerError> {
         let input_index = input_index.unwrap();
-        if input_index >= psbt.inputs.len() || input_index >= psbt.global.unsigned_tx.input.len() {
+        if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
             return Err(SignerError::InputIndexOutOfRange);
         }
 
@@ -283,7 +292,7 @@ impl Signer for PrivateKey {
             return Ok(());
         }
 
-        let pubkey = self.public_key(secp);
+        let pubkey = PublicKey::from_private_key(secp, &self);
         if psbt.inputs[input_index].partial_sigs.contains_key(&pubkey) {
             return Ok(());
         }
@@ -297,15 +306,15 @@ impl Signer for PrivateKey {
             None => Legacy::sighash(psbt, input_index)?,
         };
 
-        let signature = secp.sign(
+        let sig = secp.sign_ecdsa(
             &Message::from_slice(&hash.into_inner()[..]).unwrap(),
-            &self.key,
+            &self.inner,
         );
 
-        let mut final_signature = Vec::with_capacity(75);
-        final_signature.extend_from_slice(&signature.serialize_der());
-        final_signature.push(sighash.as_u32() as u8);
-
+        let final_signature = ecdsa::EcdsaSig {
+            sig,
+            hash_ty: sighash.ecdsa_hash_ty().unwrap(), // FIXME
+        };
         psbt.inputs[input_index]
             .partial_sigs
             .insert(pubkey, final_signature);
@@ -494,22 +503,22 @@ pub(crate) trait ComputeSighash {
     fn sighash(
         psbt: &psbt::PartiallySignedTransaction,
         input_index: usize,
-    ) -> Result<(SigHash, SigHashType), SignerError>;
+    ) -> Result<(Sighash, psbt::PsbtSighashType), SignerError>;
 }
 
 impl ComputeSighash for Legacy {
     fn sighash(
         psbt: &psbt::PartiallySignedTransaction,
         input_index: usize,
-    ) -> Result<(SigHash, SigHashType), SignerError> {
-        if input_index >= psbt.inputs.len() || input_index >= psbt.global.unsigned_tx.input.len() {
+    ) -> Result<(Sighash, psbt::PsbtSighashType), SignerError> {
+        if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
             return Err(SignerError::InputIndexOutOfRange);
         }
 
         let psbt_input = &psbt.inputs[input_index];
-        let tx_input = &psbt.global.unsigned_tx.input[input_index];
+        let tx_input = &psbt.unsigned_tx.input[input_index];
 
-        let sighash = psbt_input.sighash_type.unwrap_or(SigHashType::All);
+        let sighash = psbt_input.sighash_type.unwrap_or(SigHashType::All.into());
         let script = match psbt_input.redeem_script {
             Some(ref redeem_script) => redeem_script.clone(),
             None => {
@@ -527,9 +536,8 @@ impl ComputeSighash for Legacy {
         };
 
         Ok((
-            psbt.global
-                .unsigned_tx
-                .signature_hash(input_index, &script, sighash.as_u32()),
+            psbt.unsigned_tx
+                .signature_hash(input_index, &script, sighash.to_u32()),
             sighash,
         ))
     }
@@ -549,15 +557,19 @@ impl ComputeSighash for Segwitv0 {
     fn sighash(
         psbt: &psbt::PartiallySignedTransaction,
         input_index: usize,
-    ) -> Result<(SigHash, SigHashType), SignerError> {
-        if input_index >= psbt.inputs.len() || input_index >= psbt.global.unsigned_tx.input.len() {
+    ) -> Result<(Sighash, psbt::PsbtSighashType), SignerError> {
+        if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
             return Err(SignerError::InputIndexOutOfRange);
         }
 
         let psbt_input = &psbt.inputs[input_index];
-        let tx_input = &psbt.global.unsigned_tx.input[input_index];
+        let tx_input = &psbt.unsigned_tx.input[input_index];
 
-        let sighash = psbt_input.sighash_type.unwrap_or(SigHashType::All);
+        let sighash = psbt_input
+            .sighash_type
+            .unwrap_or(SigHashType::All.into())
+            .ecdsa_hash_ty()
+            .map_err(|_| SignerError::InvalidSighash)?;
 
         // Always try first with the non-witness utxo
         let utxo = if let Some(prev_tx) = &psbt_input.non_witness_utxo {
@@ -600,13 +612,13 @@ impl ComputeSighash for Segwitv0 {
         };
 
         Ok((
-            bip143::SigHashCache::new(&psbt.global.unsigned_tx).signature_hash(
+            bip143::SigHashCache::new(&psbt.unsigned_tx).signature_hash(
                 input_index,
                 &script,
                 value,
                 sighash,
             ),
-            sighash,
+            sighash.into(),
         ))
     }
 }
@@ -757,7 +769,7 @@ mod signers_container_tests {
         let secp: Secp256k1<All> = Secp256k1::new();
         let path = bip32::DerivationPath::from_str(PATH).unwrap();
         let tprv = bip32::ExtendedPrivKey::from_str(tprv).unwrap();
-        let tpub = bip32::ExtendedPubKey::from_private(&secp, &tprv);
+        let tpub = bip32::ExtendedPubKey::from_priv(&secp, &tprv);
         let fingerprint = tprv.fingerprint(&secp);
         let prvkey = (tprv, path.clone()).into_descriptor_key().unwrap();
         let pubkey = (tpub, path).into_descriptor_key().unwrap();
index f23d62a514715012fe443ca3e2545e070d046cfe..9866117c3ad554659e8cf8d34463aa25c9c9c30c 100644 (file)
@@ -42,7 +42,7 @@ use std::default::Default;
 use std::marker::PhantomData;
 
 use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt};
-use bitcoin::{OutPoint, Script, SigHashType, Transaction};
+use bitcoin::{OutPoint, Script, Transaction};
 
 use miniscript::descriptor::DescriptorTrait;
 
@@ -103,10 +103,7 @@ impl TxBuilderContext for BumpFee {}
 ///     builder.finish()?
 /// };
 ///
-/// assert_eq!(
-///     psbt1.global.unsigned_tx.output[..2],
-///     psbt2.global.unsigned_tx.output[..2]
-/// );
+/// assert_eq!(psbt1.unsigned_tx.output[..2], psbt2.unsigned_tx.output[..2]);
 /// # Ok::<(), bdk::Error>(())
 /// ```
 ///
@@ -140,7 +137,7 @@ pub(crate) struct TxParams {
     pub(crate) utxos: Vec<WeightedUtxo>,
     pub(crate) unspendable: HashSet<OutPoint>,
     pub(crate) manually_selected_only: bool,
-    pub(crate) sighash: Option<SigHashType>,
+    pub(crate) sighash: Option<psbt::PsbtSighashType>,
     pub(crate) ordering: TxOrdering,
     pub(crate) locktime: Option<u32>,
     pub(crate) rbf: Option<RbfValue>,
@@ -412,7 +409,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
     /// Sign with a specific sig hash
     ///
     /// **Use this option very carefully**
-    pub fn sighash(&mut self, sighash: SigHashType) -> &mut Self {
+    pub fn sighash(&mut self, sighash: psbt::PsbtSighashType) -> &mut Self {
         self.params.sighash = Some(sighash);
         self
     }