From cf8c1c892d1fb24fab7c2b45281e7fcdfae236f8 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 14 May 2024 09:00:15 +1000 Subject: [PATCH] Upgrade bitcoin v0.32.0 We just released a new version of `bitcoin`, lets use it. Includes upgrading the version of: - `bitcoind` to `v0.36.0` - `secp256k1` to `0.29.0` --- Cargo.toml | 8 ++--- bitcoind-tests/Cargo.toml | 4 +-- bitcoind-tests/tests/test_cpp.rs | 8 ++--- bitcoind-tests/tests/test_desc.rs | 21 ++++++------ examples/psbt_sign_finalize.rs | 4 +-- examples/sign_multisig.rs | 4 +-- examples/verify_tx.rs | 4 +-- src/descriptor/bare.rs | 2 +- src/descriptor/key.rs | 17 +++++----- src/descriptor/mod.rs | 8 ++--- src/descriptor/segwitv0.rs | 15 ++++++--- src/interpreter/error.rs | 6 ++-- src/interpreter/mod.rs | 43 +++++++++++++++---------- src/lib.rs | 15 +++++++-- src/miniscript/decode.rs | 2 +- src/miniscript/mod.rs | 8 ++--- src/policy/compiler.rs | 8 +++-- src/psbt/finalizer.rs | 30 +++++++++++------ src/psbt/mod.rs | 53 +++++++++++++++++++------------ 19 files changed, 155 insertions(+), 105 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c78a20d0b..b7a6ef186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ edition = "2018" [features] default = ["std"] std = ["bitcoin/std", "bitcoin/secp-recovery", "bech32/std"] -no-std = ["bitcoin/no-std", "bech32/alloc"] +no-std = ["bech32/alloc"] compiler = [] trace = [] @@ -23,15 +23,15 @@ base64 = ["bitcoin/base64"] [dependencies] bech32 = { version = "0.11.0", default-features = false } -bitcoin = { version = "0.31.0", default-features = false } +bitcoin = { version = "0.32.0", default-features = false } # Do NOT use this as a feature! Use the `serde` feature instead. actual-serde = { package = "serde", version = "1.0.103", optional = true } [dev-dependencies] serde_test = "1.0.147" -bitcoin = { version = "0.31.0", features = ["base64"] } -secp256k1 = {version = "0.28.0", features = ["rand-std"]} +bitcoin = { version = "0.32.0", features = ["base64"] } +secp256k1 = {version = "0.29.0", features = ["rand-std"]} [[example]] name = "htlc" diff --git a/bitcoind-tests/Cargo.toml b/bitcoind-tests/Cargo.toml index 1e5d11359..a9071a05b 100644 --- a/bitcoind-tests/Cargo.toml +++ b/bitcoind-tests/Cargo.toml @@ -9,6 +9,6 @@ publish = false [dependencies] miniscript = {path = "../"} -bitcoind = { version = "0.34.0" } +bitcoind = { version = "0.36.0" } actual-rand = { package = "rand", version = "0.8.4"} -secp256k1 = {version = "0.28.0", features = ["rand-std"]} +secp256k1 = {version = "0.29.0", features = ["rand-std"]} diff --git a/bitcoind-tests/tests/test_cpp.rs b/bitcoind-tests/tests/test_cpp.rs index ca04dbceb..0e06fd810 100644 --- a/bitcoind-tests/tests/test_cpp.rs +++ b/bitcoind-tests/tests/test_cpp.rs @@ -170,9 +170,9 @@ pub fn test_from_cpp_ms(cl: &Client, testdata: &TestData) { // Get the required sighash message let amt = btc(1); let mut sighash_cache = bitcoin::sighash::SighashCache::new(&psbts[i].unsigned_tx); - let sighash_ty = bitcoin::sighash::EcdsaSighashType::All; + let sighash_type = bitcoin::sighash::EcdsaSighashType::All; let sighash = sighash_cache - .p2wsh_signature_hash(0, &ms.encode(), amt, sighash_ty) + .p2wsh_signature_hash(0, &ms.encode(), amt, sighash_type) .unwrap(); // requires both signing and verification because we check the tx @@ -181,11 +181,11 @@ pub fn test_from_cpp_ms(cl: &Client, testdata: &TestData) { // Finally construct the signature and add to psbt for sk in sks_reqd { - let sig = secp.sign_ecdsa(&msg, &sk); + let signature = secp.sign_ecdsa(&msg, &sk); let pk = pks[sks.iter().position(|&x| x == sk).unwrap()]; psbts[i].inputs[0] .partial_sigs - .insert(pk, bitcoin::ecdsa::Signature { sig, hash_ty: sighash_ty }); + .insert(pk, bitcoin::ecdsa::Signature { signature, sighash_type }); } // Add the hash preimages to the psbt psbts[i].inputs[0] diff --git a/bitcoind-tests/tests/test_desc.rs b/bitcoind-tests/tests/test_desc.rs index 24abb1e6a..a96c0d067 100644 --- a/bitcoind-tests/tests/test_desc.rs +++ b/bitcoind-tests/tests/test_desc.rs @@ -155,7 +155,7 @@ pub fn test_desc_satisfy( match derived_desc { Descriptor::Tr(ref tr) => { // Fixme: take a parameter - let hash_ty = sighash::TapSighashType::Default; + let sighash_type = sighash::TapSighashType::Default; let internal_key_present = x_only_pks .iter() @@ -170,14 +170,15 @@ pub fn test_desc_satisfy( .add_xonly_tweak(&secp, &tr.spend_info().tap_tweak().to_scalar()) .expect("Tweaking failed"); let sighash_msg = sighash_cache - .taproot_key_spend_signature_hash(0, &prevouts, hash_ty) + .taproot_key_spend_signature_hash(0, &prevouts, sighash_type) .unwrap(); let msg = secp256k1::Message::from_digest(sighash_msg.to_byte_array()); let mut aux_rand = [0u8; 32]; rand::thread_rng().fill_bytes(&mut aux_rand); let schnorr_sig = secp.sign_schnorr_with_aux_rand(&msg, &internal_keypair, &aux_rand); - psbt.inputs[0].tap_key_sig = Some(taproot::Signature { sig: schnorr_sig, hash_ty }); + psbt.inputs[0].tap_key_sig = + Some(taproot::Signature { signature: schnorr_sig, sighash_type }); } else { // No internal key } @@ -194,17 +195,17 @@ pub fn test_desc_satisfy( .collect(); for (keypair, leaf_hash) in x_only_keypairs_reqd { let sighash_msg = sighash_cache - .taproot_script_spend_signature_hash(0, &prevouts, leaf_hash, hash_ty) + .taproot_script_spend_signature_hash(0, &prevouts, leaf_hash, sighash_type) .unwrap(); let msg = secp256k1::Message::from_digest(sighash_msg.to_byte_array()); let mut aux_rand = [0u8; 32]; rand::thread_rng().fill_bytes(&mut aux_rand); - let sig = secp.sign_schnorr_with_aux_rand(&msg, &keypair, &aux_rand); + let signature = secp.sign_schnorr_with_aux_rand(&msg, &keypair, &aux_rand); let x_only_pk = x_only_pks[xonly_keypairs.iter().position(|&x| x == keypair).unwrap()]; psbt.inputs[0] .tap_script_sigs - .insert((x_only_pk, leaf_hash), taproot::Signature { sig, hash_ty }); + .insert((x_only_pk, leaf_hash), taproot::Signature { signature, sighash_type }); } } _ => { @@ -246,16 +247,16 @@ pub fn test_desc_satisfy( .to_secp_msg(); // Fixme: Take a parameter - let hash_ty = sighash::EcdsaSighashType::All; + let sighash_type = sighash::EcdsaSighashType::All; // Finally construct the signature and add to psbt for sk in sks_reqd { - let sig = secp.sign_ecdsa(&msg, &sk); + let signature = secp.sign_ecdsa(&msg, &sk); let pk = pks[sks.iter().position(|&x| x == sk).unwrap()]; - assert!(secp.verify_ecdsa(&msg, &sig, &pk.inner).is_ok()); + assert!(secp.verify_ecdsa(&msg, &signature, &pk.inner).is_ok()); psbt.inputs[0] .partial_sigs - .insert(pk, ecdsa::Signature { sig, hash_ty }); + .insert(pk, ecdsa::Signature { signature, sighash_type }); } } } diff --git a/examples/psbt_sign_finalize.rs b/examples/psbt_sign_finalize.rs index 34e72e3c2..b7c2a62f3 100644 --- a/examples/psbt_sign_finalize.rs +++ b/examples/psbt_sign_finalize.rs @@ -134,7 +134,7 @@ fn main() { psbt.inputs[0] .partial_sigs - .insert(pk1, bitcoin::ecdsa::Signature { sig: sig1, hash_ty }); + .insert(pk1, bitcoin::ecdsa::Signature { signature: sig1, sighash_type: hash_ty }); println!("{:#?}", psbt); println!("{}", psbt); @@ -150,7 +150,7 @@ fn main() { fn get_vout(tx: &Transaction, spk: &Script) -> (OutPoint, TxOut) { for (i, txout) in tx.clone().output.into_iter().enumerate() { if spk == &txout.script_pubkey { - return (OutPoint::new(tx.txid(), i as u32), txout); + return (OutPoint::new(tx.compute_txid(), i as u32), txout); } } panic!("Only call get vout on functions which have the expected outpoint"); diff --git a/examples/sign_multisig.rs b/examples/sign_multisig.rs index 688924843..b1117c102 100644 --- a/examples/sign_multisig.rs +++ b/examples/sign_multisig.rs @@ -118,7 +118,7 @@ fn list_of_three_arbitrary_public_keys() -> Vec { // a valid signature for this transaction; Miniscript does not verify the validity. fn random_signature_from_the_blockchain() -> ecdsa::Signature { ecdsa::Signature { - sig: secp256k1::ecdsa::Signature::from_str( + signature: secp256k1::ecdsa::Signature::from_str( "3045\ 0221\ 00f7c3648c390d87578cd79c8016940aa8e3511c4104cb78daa8fb8e429375efc1\ @@ -126,6 +126,6 @@ fn random_signature_from_the_blockchain() -> ecdsa::Signature { 531d75c136272f127a5dc14acc0722301cbddc222262934151f140da345af177", ) .unwrap(), - hash_ty: bitcoin::sighash::EcdsaSighashType::All, + sighash_type: bitcoin::sighash::EcdsaSighashType::All, } } diff --git a/examples/verify_tx.rs b/examples/verify_tx.rs index 37d985d54..f2a63f447 100644 --- a/examples/verify_tx.rs +++ b/examples/verify_tx.rs @@ -87,9 +87,9 @@ fn main() { let iter = interpreter.iter_custom(Box::new(|key_sig: &KeySigPair| { let (pk, ecdsa_sig) = key_sig.as_ecdsa().expect("Ecdsa Sig"); - ecdsa_sig.hash_ty == bitcoin::sighash::EcdsaSighashType::All + ecdsa_sig.sighash_type == bitcoin::sighash::EcdsaSighashType::All && secp - .verify_ecdsa(&message, &ecdsa_sig.sig, &pk.inner) + .verify_ecdsa(&message, &ecdsa_sig.signature, &pk.inner) .is_ok() })); diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index 30058e2b6..66be5d6d4 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -273,7 +273,7 @@ impl Pkh { /// Obtains the corresponding script pubkey for this descriptor. pub fn address(&self, network: Network) -> Address { - Address::p2pkh(&self.pk.to_public_key(), network) + Address::p2pkh(self.pk.to_public_key(), network) } /// Obtains the underlying miniscript for this descriptor. diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 85bb27ac5..79818465e 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -7,7 +7,6 @@ use core::str::FromStr; use std::error; use bitcoin::bip32::{self, XKeyIdentifier}; -use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::{hash160, ripemd160, sha256, Hash, HashEngine}; use bitcoin::key::XOnlyPublicKey; use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; @@ -1234,17 +1233,17 @@ mod test { fn test_wildcard() { let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2").unwrap(); assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'/2"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0'/1'/2"); assert!(!public_key.has_wildcard()); let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/*").unwrap(); assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0'/1'"); assert!(public_key.has_wildcard()); let public_key = DescriptorPublicKey::from_str("[abcdef00/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/*h").unwrap(); assert_eq!(public_key.master_fingerprint().to_string(), "abcdef00"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0'/1'"); assert!(public_key.has_wildcard()); } @@ -1256,32 +1255,32 @@ mod test { let public_key = secret_key.to_public(&secp).unwrap(); assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2"); assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'/2"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0'/1'/2"); assert!(!public_key.has_wildcard()); let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2'").unwrap(); let public_key = secret_key.to_public(&secp).unwrap(); assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1'/2']tpubDDPuH46rv4dbFtmF6FrEtJEy1CvLZonyBoVxF6xsesHdYDdTBrq2mHhm8AbsPh39sUwL2nZyxd6vo4uWNTU9v4t893CwxjqPnwMoUACLvMV"); assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0'/1'/2'"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0'/1'/2'"); let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0/1/2").unwrap(); let public_key = secret_key.to_public(&secp).unwrap(); assert_eq!(public_key.to_string(), "tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr/0/1/2"); assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0/1/2"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0/1/2"); let secret_key = DescriptorSecretKey::from_str("[aabbccdd]tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0/1/2").unwrap(); let public_key = secret_key.to_public(&secp).unwrap(); assert_eq!(public_key.to_string(), "[aabbccdd]tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr/0/1/2"); assert_eq!(public_key.master_fingerprint().to_string(), "aabbccdd"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/0/1/2"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "0/1/2"); let secret_key = DescriptorSecretKey::from_str("[aabbccdd/90']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2").unwrap(); let public_key = secret_key.to_public(&secp).unwrap(); assert_eq!(public_key.to_string(), "[aabbccdd/90'/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2"); assert_eq!(public_key.master_fingerprint().to_string(), "aabbccdd"); - assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "m/90'/0'/1'/2"); + assert_eq!(public_key.full_derivation_path().unwrap().to_string(), "90'/0'/1'/2"); } #[test] diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index b3509cd4a..a38dac154 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -1267,8 +1267,8 @@ mod tests { ) -> Option { if *pk == self.pk { Some(bitcoin::ecdsa::Signature { - sig: self.sig, - hash_ty: bitcoin::sighash::EcdsaSighashType::All, + signature: self.sig, + sighash_type: bitcoin::sighash::EcdsaSighashType::All, }) } else { None @@ -1534,11 +1534,11 @@ mod tests { satisfier.insert( a, - bitcoin::ecdsa::Signature { sig: sig_a, hash_ty: EcdsaSighashType::All }, + bitcoin::ecdsa::Signature { signature: sig_a, sighash_type: EcdsaSighashType::All }, ); satisfier.insert( b, - bitcoin::ecdsa::Signature { sig: sig_b, hash_ty: EcdsaSighashType::All }, + bitcoin::ecdsa::Signature { signature: sig_b, sighash_type: EcdsaSighashType::All }, ); satisfier diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index 3de71babf..b04c1ad3a 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -5,6 +5,7 @@ //! Implementation of Segwit Descriptors. Contains the implementation //! of wsh, wpkh and sortedmulti inside wsh. +use core::convert::TryFrom; use core::fmt; use bitcoin::{Address, Network, ScriptBuf, Weight}; @@ -374,15 +375,21 @@ impl Wpkh { impl Wpkh { /// Obtains the corresponding script pubkey for this descriptor. pub fn script_pubkey(&self) -> ScriptBuf { - let addr = Address::p2wpkh(&self.pk.to_public_key(), Network::Bitcoin) + let pk = self.pk.to_public_key(); + let compressed = bitcoin::key::CompressedPublicKey::try_from(pk) .expect("wpkh descriptors have compressed keys"); + + let addr = Address::p2wpkh(&compressed, Network::Bitcoin); addr.script_pubkey() } /// Obtains the corresponding script pubkey for this descriptor. pub fn address(&self, network: Network) -> Address { - Address::p2wpkh(&self.pk.to_public_key(), network) - .expect("Rust Miniscript types don't allow uncompressed pks in segwit descriptors") + let pk = self.pk.to_public_key(); + let compressed = bitcoin::key::CompressedPublicKey::try_from(pk) + .expect("Rust Miniscript types don't allow uncompressed pks in segwit descriptors"); + + Address::p2wpkh(&compressed, network) } /// Obtains the underlying miniscript for this descriptor. @@ -394,7 +401,7 @@ impl Wpkh { // the previous txo's scriptPubKey. // The item 5: // - For P2WPKH witness program, the scriptCode is `0x1976a914{20-byte-pubkey-hash}88ac`. - let addr = Address::p2pkh(&self.pk.to_public_key(), Network::Bitcoin); + let addr = Address::p2pkh(self.pk.to_public_key(), Network::Bitcoin); addr.script_pubkey() } diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index d19c28bea..4e47932a6 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -95,7 +95,7 @@ pub enum Error { /// Schnorr Signature error SchnorrSig(bitcoin::taproot::SigFromSliceError), /// Errors in signature hash calculations - SighashError(bitcoin::sighash::Error), + SighashError(bitcoin::sighash::InvalidSighashTypeError), /// Taproot Annex Unsupported TapAnnexUnsupported, /// An uncompressed public key was encountered in a context where it is @@ -242,8 +242,8 @@ impl From for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: bitcoin::sighash::Error) -> Error { Error::SighashError(e) } +impl From for Error { + fn from(e: bitcoin::sighash::InvalidSighashTypeError) -> Error { Error::SighashError(e) } } #[doc(hidden)] diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 2ed64abf6..a7b316f23 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -218,7 +218,7 @@ impl<'txin> Interpreter<'txin> { KeySigPair::Ecdsa(key, ecdsa_sig) => { let script_pubkey = self.script_code.as_ref().expect("Legacy have script code"); let msg = if self.is_legacy() { - let sighash_u32 = ecdsa_sig.hash_ty.to_u32(); + let sighash_u32 = ecdsa_sig.sighash_type.to_u32(); let sighash = cache.legacy_signature_hash(input_idx, script_pubkey, sighash_u32); sighash.map(|hash| secp256k1::Message::from_digest(hash.to_byte_array())) @@ -227,11 +227,12 @@ impl<'txin> Interpreter<'txin> { Some(txout) => txout.borrow().value, None => return false, }; - let sighash = cache.segwit_signature_hash( + // TODO: Don't manually handle the script code. + let sighash = cache.p2wsh_signature_hash( input_idx, script_pubkey, amt, - ecdsa_sig.hash_ty, + ecdsa_sig.sighash_type, ); sighash.map(|hash| secp256k1::Message::from_digest(hash.to_byte_array())) } else { @@ -239,13 +240,19 @@ impl<'txin> Interpreter<'txin> { return false; }; - let success = - msg.map(|msg| secp.verify_ecdsa(&msg, &ecdsa_sig.sig, &key.inner).is_ok()); + let success = msg.map(|msg| { + secp.verify_ecdsa(&msg, &ecdsa_sig.signature, &key.inner) + .is_ok() + }); success.unwrap_or(false) // unwrap_or checks for errors, while success would have checksig results } KeySigPair::Schnorr(xpk, schnorr_sig) => { let sighash_msg = if self.is_taproot_v1_key_spend() { - cache.taproot_key_spend_signature_hash(input_idx, prevouts, schnorr_sig.hash_ty) + cache.taproot_key_spend_signature_hash( + input_idx, + prevouts, + schnorr_sig.sighash_type, + ) } else if self.is_taproot_v1_script_spend() { let tap_script = self.script_code.as_ref().expect( "Internal Hack: Saving leaf script instead\ @@ -259,7 +266,7 @@ impl<'txin> Interpreter<'txin> { input_idx, prevouts, leaf_hash, - schnorr_sig.hash_ty, + schnorr_sig.sighash_type, ) } else { // schnorr sigs in ecdsa descriptors @@ -267,8 +274,10 @@ impl<'txin> Interpreter<'txin> { }; let msg = sighash_msg.map(|hash| secp256k1::Message::from_digest(hash.to_byte_array())); - let success = - msg.map(|msg| secp.verify_schnorr(&schnorr_sig.sig, &msg, xpk).is_ok()); + let success = msg.map(|msg| { + secp.verify_schnorr(&schnorr_sig.signature, &msg, xpk) + .is_ok() + }); success.unwrap_or(false) // unwrap_or_default checks for errors, while success would have checksig results } } @@ -1081,12 +1090,12 @@ mod tests { inner: secp256k1::PublicKey::from_secret_key(&secp, &sk), compressed: true, }; - let sig = secp.sign_ecdsa(&msg, &sk); + let signature = secp.sign_ecdsa(&msg, &sk); ecdsa_sigs.push(bitcoin::ecdsa::Signature { - sig, - hash_ty: bitcoin::sighash::EcdsaSighashType::All, + signature, + sighash_type: bitcoin::sighash::EcdsaSighashType::All, }); - let mut sigser = sig.serialize_der().to_vec(); + let mut sigser = signature.serialize_der().to_vec(); sigser.push(0x01); // sighash_all pks.push(pk); der_sigs.push(sigser); @@ -1096,8 +1105,8 @@ mod tests { x_only_pks.push(x_only_pk); let schnorr_sig = secp.sign_schnorr_with_aux_rand(&msg, &keypair, &[0u8; 32]); let schnorr_sig = bitcoin::taproot::Signature { - sig: schnorr_sig, - hash_ty: bitcoin::sighash::TapSighashType::Default, + signature: schnorr_sig, + sighash_type: bitcoin::sighash::TapSighashType::Default, }; ser_schnorr_sigs.push(schnorr_sig.to_vec()); schnorr_sigs.push(schnorr_sig); @@ -1112,10 +1121,10 @@ mod tests { let secp_ref = &secp; let vfyfn = |pksig: &KeySigPair| match pksig { KeySigPair::Ecdsa(pk, ecdsa_sig) => secp_ref - .verify_ecdsa(&sighash, &ecdsa_sig.sig, &pk.inner) + .verify_ecdsa(&sighash, &ecdsa_sig.signature, &pk.inner) .is_ok(), KeySigPair::Schnorr(xpk, schnorr_sig) => secp_ref - .verify_schnorr(&schnorr_sig.sig, &sighash, xpk) + .verify_schnorr(&schnorr_sig.signature, &sighash, xpk) .is_ok(), }; diff --git a/src/lib.rs b/src/lib.rs index fd1b92847..f93b0b82d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -431,7 +431,9 @@ pub enum Error { /// rust-bitcoin script error Script(script::Error), /// rust-bitcoin address error - AddrError(bitcoin::address::Error), + AddrError(bitcoin::address::ParseError), + /// rust-bitcoin p2sh address error + AddrP2shError(bitcoin::address::P2shError), /// A `CHECKMULTISIG` opcode was preceded by a number > 20 CmsTooManyKeys(u32), /// A tapscript multi_a cannot support more than Weight::MAX_BLOCK/32 keys @@ -514,6 +516,7 @@ impl fmt::Display for Error { }, Error::Script(ref e) => fmt::Display::fmt(e, f), Error::AddrError(ref e) => fmt::Display::fmt(e, f), + Error::AddrP2shError(ref e) => fmt::Display::fmt(e, f), Error::CmsTooManyKeys(n) => write!(f, "checkmultisig with {} keys", n), Error::Unprintable(x) => write!(f, "unprintable character 0x{:02x}", x), Error::ExpectedChar(c) => write!(f, "expected {}", c), @@ -594,6 +597,7 @@ impl error::Error for Error { | MultipathDescLenMismatch => None, Script(e) => Some(e), AddrError(e) => Some(e), + AddrP2shError(e) => Some(e), Secp(e) => Some(e), #[cfg(feature = "compiler")] CompilerError(e) => Some(e), @@ -636,8 +640,13 @@ impl From for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: bitcoin::address::Error) -> Error { Error::AddrError(e) } +impl From for Error { + fn from(e: bitcoin::address::ParseError) -> Error { Error::AddrError(e) } +} + +#[doc(hidden)] +impl From for Error { + fn from(e: bitcoin::address::P2shError) -> Error { Error::AddrP2shError(e) } } #[doc(hidden)] diff --git a/src/miniscript/decode.rs b/src/miniscript/decode.rs index 6cbb92931..56c7af458 100644 --- a/src/miniscript/decode.rs +++ b/src/miniscript/decode.rs @@ -48,7 +48,7 @@ impl ParseableKey for bitcoin::secp256k1::XOnlyPublicKey { #[derive(Debug, Clone, PartialEq, Eq)] pub enum KeyParseError { /// Bitcoin PublicKey parse error - FullKeyParseError(bitcoin::key::Error), + FullKeyParseError(bitcoin::key::FromSliceError), /// Xonly key parse Error XonlyKeyParseError(bitcoin::secp256k1::Error), } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index 725e88586..4bfdccb5e 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -1288,8 +1288,8 @@ mod tests { _h: &TapLeafHash, ) -> Option { Some(bitcoin::taproot::Signature { - sig: self.0, - hash_ty: bitcoin::sighash::TapSighashType::Default, + signature: self.0, + sighash_type: bitcoin::sighash::TapSighashType::Default, }) } } @@ -1419,8 +1419,8 @@ mod tests { ) -> Option { if pk == &self.1 { Some(bitcoin::taproot::Signature { - sig: self.0, - hash_ty: bitcoin::sighash::TapSighashType::Default, + signature: self.0, + sighash_type: bitcoin::sighash::TapSighashType::Default, }) } else { None diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index 8f9b1e964..d306da905 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -1312,7 +1312,7 @@ mod tests { #[test] #[allow(clippy::needless_range_loop)] fn compile_misc() { - let (keys, sig) = pubkeys_and_a_sig(10); + let (keys, signature) = pubkeys_and_a_sig(10); let key_pol: Vec = keys.iter().map(|k| Concrete::Key(*k)).collect(); let policy: BPolicy = Concrete::Key(keys[0]); @@ -1399,8 +1399,10 @@ mod tests { assert_eq!(abs.n_keys(), 5); assert_eq!(abs.minimum_n_keys(), Some(3)); - let bitcoinsig = - bitcoin::ecdsa::Signature { sig, hash_ty: bitcoin::sighash::EcdsaSighashType::All }; + let bitcoinsig = bitcoin::ecdsa::Signature { + signature, + sighash_type: bitcoin::sighash::EcdsaSighashType::All, + }; let sigvec = bitcoinsig.to_vec(); let no_sat = BTreeMap::::new(); diff --git a/src/psbt/finalizer.rs b/src/psbt/finalizer.rs index 7dafcec53..ec361ca3e 100644 --- a/src/psbt/finalizer.rs +++ b/src/psbt/finalizer.rs @@ -8,6 +8,7 @@ //! `https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki` //! +use core::convert::TryFrom; use core::mem; use bitcoin::hashes::hash160; @@ -174,7 +175,7 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In // Partial sigs loses the compressed flag that is necessary // TODO: See https://github.com/rust-bitcoin/rust-bitcoin/pull/836 // The type checker will fail again after we update to 0.28 and this can be removed - let addr = bitcoin::Address::p2pkh(&pk, bitcoin::Network::Bitcoin); + let addr = bitcoin::Address::p2pkh(pk, bitcoin::Network::Bitcoin); *script_pubkey == addr.script_pubkey() }); match partial_sig_contains_pk { @@ -184,11 +185,15 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In } else if script_pubkey.is_p2wpkh() { // 3. `Wpkh`: creates a `wpkh` descriptor if the partial sig has corresponding pk. let partial_sig_contains_pk = inp.partial_sigs.iter().find(|&(&pk, _sig)| { - // Indirect way to check the equivalence of pubkey-hashes. - // Create a pubkey hash and check if they are the same. - let addr = bitcoin::Address::p2wpkh(&pk, bitcoin::Network::Bitcoin) - .expect("Address corresponding to valid pubkey"); - *script_pubkey == addr.script_pubkey() + match bitcoin::key::CompressedPublicKey::try_from(pk) { + Ok(compressed) => { + // Indirect way to check the equivalence of pubkey-hashes. + // Create a pubkey hash and check if they are the same. + let addr = bitcoin::Address::p2wpkh(&compressed, bitcoin::Network::Bitcoin); + *script_pubkey == addr.script_pubkey() + } + Err(_) => false, + } }); match partial_sig_contains_pk { Some((pk, _sig)) => Ok(Descriptor::new_wpkh(*pk)?), @@ -244,9 +249,16 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In } else if redeem_script.is_p2wpkh() { // 6. `ShWpkh` case let partial_sig_contains_pk = inp.partial_sigs.iter().find(|&(&pk, _sig)| { - let addr = bitcoin::Address::p2wpkh(&pk, bitcoin::Network::Bitcoin) - .expect("Address corresponding to valid pubkey"); - *redeem_script == addr.script_pubkey() + match bitcoin::key::CompressedPublicKey::try_from(pk) { + Ok(compressed) => { + let addr = bitcoin::Address::p2wpkh( + &compressed, + bitcoin::Network::Bitcoin, + ); + *redeem_script == addr.script_pubkey() + } + Err(_) => false, + } }); match partial_sig_contains_pk { Some((pk, _sig)) => Ok(Descriptor::new_sh_wpkh(*pk)?), diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 500534d3c..c315e156c 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -88,7 +88,7 @@ pub enum InputError { /// Get the secp Errors directly SecpErr(bitcoin::secp256k1::Error), /// Key errors - KeyErr(bitcoin::key::Error), + KeyErr(bitcoin::key::FromSliceError), /// Could not satisfy taproot descriptor /// This error is returned when both script path and key paths could not be /// satisfied. We cannot return a detailed error because we try all miniscripts @@ -231,8 +231,8 @@ impl From for InputError { } #[doc(hidden)] -impl From for InputError { - fn from(e: bitcoin::key::Error) -> InputError { InputError::KeyErr(e) } +impl From for InputError { + fn from(e: bitcoin::key::FromSliceError) -> InputError { InputError::KeyErr(e) } } /// Psbt satisfier for at inputs at a particular index @@ -395,16 +395,15 @@ fn sanity_check(psbt: &Psbt) -> Result<(), Error> { None => sighash::EcdsaSighashType::All, }; for (key, ecdsa_sig) in &input.partial_sigs { - let flag = sighash::EcdsaSighashType::from_standard(ecdsa_sig.hash_ty as u32).map_err( - |_| { + let flag = sighash::EcdsaSighashType::from_standard(ecdsa_sig.sighash_type as u32) + .map_err(|_| { Error::InputError( InputError::Interpreter(interpreter::Error::NonStandardSighash( ecdsa_sig.to_vec(), )), index, ) - }, - )?; + })?; if target_ecdsa_sighash_ty != flag { return Err(Error::InputError( InputError::WrongSighashFlag { @@ -736,7 +735,7 @@ impl PsbtExt for Psbt { let desc_type = desc.desc_type(); if let Some(non_witness_utxo) = &input.non_witness_utxo { - if txin.previous_output.txid != non_witness_utxo.txid() { + if txin.previous_output.txid != non_witness_utxo.compute_txid() { return Err(UtxoUpdateError::UtxoCheck); } } @@ -1315,10 +1314,12 @@ pub enum SighashError { MissingSpendUtxos, /// Invalid Sighash type InvalidSighashType, - /// Sighash computation error - /// Only happens when single does not have corresponding output as psbts - /// already have information to compute the sighash - SighashComputationError(sighash::Error), + /// Computation error for taproot sighash. + SighashTaproot(sighash::TaprootError), + /// Computation error for P2WPKH sighash. + SighashP2wpkh(sighash::P2wpkhError), + /// Computation error for P2WSH sighash. + TransactionInputsIndex(transaction::InputsIndexError), /// Missing Witness script MissingWitnessScript, /// Missing Redeem script, @@ -1334,11 +1335,11 @@ impl fmt::Display for SighashError { SighashError::MissingInputUtxo => write!(f, "Missing input utxo in pbst"), SighashError::MissingSpendUtxos => write!(f, "Missing Psbt spend utxos"), SighashError::InvalidSighashType => write!(f, "Invalid Sighash type"), - SighashError::SighashComputationError(e) => { - write!(f, "Sighash computation error : {}", e) - } SighashError::MissingWitnessScript => write!(f, "Missing Witness Script"), SighashError::MissingRedeemScript => write!(f, "Missing Redeem Script"), + SighashError::SighashTaproot(ref e) => write!(f, "sighash taproot: {}", e), + SighashError::SighashP2wpkh(ref e) => write!(f, "sighash p2wpkh: {}", e), + SighashError::TransactionInputsIndex(ref e) => write!(f, "tx inputs index: {}", e), } } } @@ -1355,13 +1356,23 @@ impl error::Error for SighashError { | InvalidSighashType | MissingWitnessScript | MissingRedeemScript => None, - SighashComputationError(e) => Some(e), + SighashTaproot(ref e) => Some(e), + SighashP2wpkh(ref e) => Some(e), + TransactionInputsIndex(ref e) => Some(e), } } } -impl From for SighashError { - fn from(e: sighash::Error) -> Self { SighashError::SighashComputationError(e) } +impl From for SighashError { + fn from(e: sighash::TaprootError) -> Self { SighashError::SighashTaproot(e) } +} + +impl From for SighashError { + fn from(e: sighash::P2wpkhError) -> Self { SighashError::SighashP2wpkh(e) } +} + +impl From for SighashError { + fn from(e: transaction::InputsIndexError) -> Self { SighashError::TransactionInputsIndex(e) } } /// Sighash message(signing data) for a given psbt transaction input. @@ -1494,7 +1505,7 @@ mod tests { let (leaf_hashes, (key_fingerprint, deriv_path)) = psbt_input.tap_key_origins.get(&key_0_1).unwrap(); assert_eq!(key_fingerprint, &fingerprint); - assert_eq!(&deriv_path.to_string(), "m/86'/0'/0'/0/1"); + assert_eq!(&deriv_path.to_string(), "86'/0'/0'/0/1"); assert_eq!(leaf_hashes.len(), 2); assert!(leaf_hashes.contains(&first_leaf_hash)); } @@ -1508,7 +1519,7 @@ mod tests { let (leaf_hashes, (key_fingerprint, deriv_path)) = psbt_input.tap_key_origins.get(&key_1_0).unwrap(); assert_eq!(key_fingerprint, &fingerprint); - assert_eq!(&deriv_path.to_string(), "m/86'/0'/0'/1/0"); + assert_eq!(&deriv_path.to_string(), "86'/0'/0'/1/0"); assert_eq!(leaf_hashes.len(), 1); assert!(!leaf_hashes.contains(&first_leaf_hash)); } @@ -1605,7 +1616,7 @@ mod tests { version: transaction::Version::ONE, lock_time: absolute::LockTime::ZERO, input: vec![TxIn { - previous_output: OutPoint { txid: non_witness_utxo.txid(), vout: 0 }, + previous_output: OutPoint { txid: non_witness_utxo.compute_txid(), vout: 0 }, ..Default::default() }], output: vec![],