From 81f133ad4a9c34b61f3b72152096b73b4afe2660 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 16 Oct 2024 17:34:33 +1100 Subject: [PATCH] WIP: Track rust-bitcoin master branch As we develop `primitives` it is useful to have crates using the changes to catch mistakes as we go. --- Cargo.toml | 34 ++++++++++++++++- bitcoind-tests/Cargo.toml | 44 ++++++++++++++++++++++ bitcoind-tests/tests/setup/test_util.rs | 2 +- bitcoind-tests/tests/test_cpp.rs | 5 ++- bitcoind-tests/tests/test_desc.rs | 8 ++-- examples/psbt_sign_finalize.rs | 6 ++- examples/sign_multisig.rs | 4 +- src/descriptor/bare.rs | 3 +- src/descriptor/key.rs | 18 ++++----- src/descriptor/mod.rs | 50 +++++++++++++++++-------- src/descriptor/segwitv0.rs | 17 ++++++--- src/descriptor/sh.rs | 5 ++- src/interpreter/error.rs | 6 +-- src/interpreter/inner.rs | 44 +++++++++++----------- src/interpreter/mod.rs | 4 +- src/interpreter/stack.rs | 4 +- src/lib.rs | 25 ++++++++++--- src/miniscript/astelem.rs | 27 +++++++------ src/miniscript/decode.rs | 2 +- src/miniscript/iter.rs | 2 +- src/miniscript/lex.rs | 35 +++++++++-------- src/miniscript/mod.rs | 37 +++++++++++++++--- src/plan.rs | 4 +- src/policy/compiler.rs | 11 +++--- src/psbt/finalizer.rs | 26 ++++++------- src/psbt/mod.rs | 14 +++++-- src/util.rs | 40 +++++++++++++++----- 27 files changed, 329 insertions(+), 148 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1a5f8d039..6c1796d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,14 +23,14 @@ base64 = ["bitcoin/base64"] [dependencies] bech32 = { version = "0.11.0", default-features = false, features = ["alloc"] } -bitcoin = { version = "0.32.6", default-features = false } +bitcoin = { version = "0.33.0-alpha.0", default-features = false } hex = { package = "hex-conservative", default-features = false, features = ["alloc"], version = "1.0.0" } serde = { version = "1.0.103", optional = true } [dev-dependencies] serde_test = "1.0.147" -bitcoin = { version = "0.32.0", features = ["base64"] } +bitcoin = { version = "0.33.0-alpha", features = ["base64"] } secp256k1 = {version = "0.29.0", features = ["rand-std"]} [[example]] @@ -73,3 +73,33 @@ required-features = ["compiler"] [workspace] members = ["fuzz"] exclude = ["embedded", "bitcoind-tests"] + +# Patch rust-bitcoin crates, use `branch` here because its easier when testing new branches (worktrees). + +[patch.crates-io.base58ck] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin_hashes] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-internals] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-io] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-primitives] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-units] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" diff --git a/bitcoind-tests/Cargo.toml b/bitcoind-tests/Cargo.toml index 02450bb1f..51f8453ec 100644 --- a/bitcoind-tests/Cargo.toml +++ b/bitcoind-tests/Cargo.toml @@ -32,3 +32,47 @@ secp256k1 = {version = "0.29.0", features = ["rand-std"]} "0_19_1" = ["bitcoind/0_19_1"] "0_18_1" = ["bitcoind/0_18_1"] "0_17_1" = ["bitcoind/0_17_1"] + +# Patch crates from bitcoind-json-rpc repo. + +[patch.crates-io.bitcoind-json-rpc-client] +git = "https://github.com/tcharding/rust-bitcoind-json-rpc" +branch = "track-bitcoin-master" + +[patch.crates-io.bitcoind-json-rpc-regtest] +git = "https://github.com/tcharding/rust-bitcoind-json-rpc" +branch = "track-bitcoin-master" + +[patch.crates-io.bitcoind-json-rpc-types] +git = "https://github.com/tcharding/rust-bitcoind-json-rpc" +branch = "track-bitcoin-master" + +# Patch rust-bitcoin crates, use `branch` here because its easier when testing new branches (worktrees). + +[patch.crates-io.base58ck] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin_hashes] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-internals] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-io] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-primitives] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" + +[patch.crates-io.bitcoin-units] +git = "https://github.com/rust-bitcoin/rust-bitcoin" +branch = "master" diff --git a/bitcoind-tests/tests/setup/test_util.rs b/bitcoind-tests/tests/setup/test_util.rs index 64ffa6f5a..79660314e 100644 --- a/bitcoind-tests/tests/setup/test_util.rs +++ b/bitcoind-tests/tests/setup/test_util.rs @@ -20,7 +20,7 @@ use std::str::FromStr; use actual_rand as rand; -use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; +use bitcoin::hashes::{hash160, ripemd160, sha256}; use bitcoin::hex::DisplayHex; use bitcoin::secp256k1; use miniscript::descriptor::{SinglePub, SinglePubKey}; diff --git a/bitcoind-tests/tests/test_cpp.rs b/bitcoind-tests/tests/test_cpp.rs index cdc336b2f..b205b43e8 100644 --- a/bitcoind-tests/tests/test_cpp.rs +++ b/bitcoind-tests/tests/test_cpp.rs @@ -9,8 +9,9 @@ use std::fs::File; use std::io::{self, BufRead}; use std::path::Path; -use bitcoin::hashes::{sha256d, Hash}; +use bitcoin::hashes::sha256d; use bitcoin::psbt::Psbt; +use bitcoin::transaction::OutPointExt as _; use bitcoin::{ psbt, secp256k1, transaction, Amount, OutPoint, Sequence, Transaction, TxIn, TxOut, Txid, }; @@ -119,7 +120,7 @@ pub fn test_from_cpp_ms(cl: &Client, testdata: &TestData) { // processed correctly. // We waited 50 blocks, keep 49 for safety sequence: Sequence::from_height(49), - ..Default::default() + ..TxIn::EMPTY_COINBASE }; psbt.unsigned_tx.input.push(txin); // Get a new script pubkey from the node so that diff --git a/bitcoind-tests/tests/test_desc.rs b/bitcoind-tests/tests/test_desc.rs index e09260097..f3259eda9 100644 --- a/bitcoind-tests/tests/test_desc.rs +++ b/bitcoind-tests/tests/test_desc.rs @@ -9,11 +9,11 @@ use std::{error, fmt}; use actual_rand as rand; use bitcoin::blockdata::witness::Witness; -use bitcoin::hashes::{sha256d, Hash}; -use bitcoin::key::TapTweak as _; +use bitcoin::hashes::sha256d; use bitcoin::psbt::Psbt; use bitcoin::sighash::SighashCache; -use bitcoin::taproot::{LeafVersion, TapLeafHash}; +use bitcoin::taproot::{LeafVersion, TapLeafHash, TapTweakHashExt as _, TapLeafHashExt as _}; +use bitcoin::transaction::OutPointExt as _; use bitcoin::{ absolute, psbt, secp256k1, sighash, transaction, Amount, OutPoint, Sequence, Transaction, TxIn, TxOut, Txid, @@ -128,7 +128,7 @@ pub fn test_desc_satisfy( // processed correctly. // We waited 2 blocks, keep 1 for safety sequence: Sequence::from_height(1), - ..Default::default() + ..TxIn::EMPTY_COINBASE }; psbt.unsigned_tx.input.push(txin); // Get a new script pubkey from the node so that diff --git a/examples/psbt_sign_finalize.rs b/examples/psbt_sign_finalize.rs index 29bf8bc4e..ee0290e99 100644 --- a/examples/psbt_sign_finalize.rs +++ b/examples/psbt_sign_finalize.rs @@ -3,7 +3,9 @@ use std::collections::BTreeMap; use std::str::FromStr; -use miniscript::bitcoin::consensus::encode::deserialize_hex; +use bitcoin::transaction::OutPointExt as _; +use miniscript::bitcoin::consensus::encode::deserialize; +use miniscript::bitcoin::hashes::hex::FromHex; use miniscript::bitcoin::psbt::{self, Psbt}; use miniscript::bitcoin::sighash::SighashCache; //use miniscript::bitcoin::secp256k1; // https://github.com/rust-lang/rust/issues/121684 @@ -83,7 +85,7 @@ fn main() { let txin = TxIn { previous_output: outpoint, sequence: Sequence::from_height(26), - ..Default::default() + ..TxIn::EMPTY_COINBASE }; psbt.unsigned_tx.input.push(txin); diff --git a/examples/sign_multisig.rs b/examples/sign_multisig.rs index b1117c102..e0f756f90 100644 --- a/examples/sign_multisig.rs +++ b/examples/sign_multisig.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use std::str::FromStr; use bitcoin::blockdata::witness::Witness; -use bitcoin::{absolute, ecdsa, transaction, Amount, Sequence}; +use bitcoin::{absolute, ecdsa, transaction, Amount, OutPoint, Sequence}; fn main() { let mut tx = spending_transaction(); @@ -81,7 +81,7 @@ fn spending_transaction() -> bitcoin::Transaction { version: transaction::Version::TWO, lock_time: absolute::LockTime::ZERO, input: vec![bitcoin::TxIn { - previous_output: Default::default(), + previous_output: OutPoint::COINBASE_PREVOUT, script_sig: bitcoin::ScriptBuf::new(), sequence: Sequence::MAX, witness: Witness::default(), diff --git a/src/descriptor/bare.rs b/src/descriptor/bare.rs index b41ee2850..1e5d93f66 100644 --- a/src/descriptor/bare.rs +++ b/src/descriptor/bare.rs @@ -9,6 +9,7 @@ use core::fmt; +use bitcoin::address::script_pubkey::BuilderExt as _; use bitcoin::script::{self, PushBytes}; use bitcoin::{Address, Network, ScriptBuf, Weight}; @@ -298,7 +299,7 @@ impl Pkh { // serialize() does not allocate here sig.serialize().as_ref(), ) - .push_key(&self.pk.to_public_key()) + .push_key(self.pk.to_public_key()) .into_script(); let witness = vec![]; Ok((witness, script_sig)) diff --git a/src/descriptor/key.rs b/src/descriptor/key.rs index 241f931ee..d2f55f064 100644 --- a/src/descriptor/key.rs +++ b/src/descriptor/key.rs @@ -6,9 +6,9 @@ use core::str::FromStr; #[cfg(feature = "std")] use std::error; -use bitcoin::bip32::{self, XKeyIdentifier}; -use bitcoin::hashes::{hash160, ripemd160, sha256, Hash, HashEngine}; -use bitcoin::key::{PublicKey, XOnlyPublicKey}; +use bitcoin::bip32; +use bitcoin::hashes::{hash160, ripemd160, sha256, HashEngine}; +use bitcoin::key::XOnlyPublicKey; use bitcoin::secp256k1::{Secp256k1, Signing, Verification}; use bitcoin::NetworkKind; @@ -232,11 +232,7 @@ impl DescriptorXKey { let hardened_path = &self.derivation_path[..last_hardened_idx]; let unhardened_path = &self.derivation_path[last_hardened_idx..]; - let xprv = self - .xkey - .derive_priv(secp, &hardened_path) - .map_err(DescriptorKeyParseError::DeriveHardenedKey)?; - + let xprv = self.xkey.derive_priv(secp, &hardened_path); let xpub = bip32::Xpub::from_priv(secp, &xprv); let origin = match &self.origin { @@ -723,15 +719,17 @@ impl DescriptorPublicKey { if let Some((fingerprint, _)) = single.origin { fingerprint } else { - let mut engine = XKeyIdentifier::engine(); + let mut engine = hash160::Hash::engine(); match single.key { SinglePubKey::FullKey(pk) => { pk.write_into(&mut engine).expect("engines don't error") } SinglePubKey::XOnly(x_only_pk) => engine.input(&x_only_pk.serialize()), }; + // FIXME: Fix the bip32 API for creating fingerprint? + let xkey_id = hash160::Hash::from_engine(engine); bip32::Fingerprint::from( - &XKeyIdentifier::from_engine(engine)[..4] + &xkey_id.as_byte_array()[..4] .try_into() .expect("4 byte slice"), ) diff --git a/src/descriptor/mod.rs b/src/descriptor/mod.rs index 81bad54c8..210d36c53 100644 --- a/src/descriptor/mod.rs +++ b/src/descriptor/mod.rs @@ -468,7 +468,7 @@ impl Descriptor { Descriptor::Pkh(ref pkh) => pkh.script_pubkey(), Descriptor::Wpkh(ref wpkh) => wpkh.script_pubkey(), Descriptor::Wsh(ref wsh) => wsh.script_pubkey(), - Descriptor::Sh(ref sh) => sh.script_pubkey(), + Descriptor::Sh(ref sh) => sh.script_pubkey().expect("TODO: Handle error"), Descriptor::Tr(ref tr) => tr.script_pubkey(), } } @@ -1109,13 +1109,15 @@ pub(crate) use write_descriptor; mod tests { use core::convert::TryFrom; + use bitcoin::address::script_pubkey::{BuilderExt as _, ScriptExt as _}; use bitcoin::blockdata::opcodes::all::{OP_CLTV, OP_CSV}; use bitcoin::blockdata::script::Instruction; use bitcoin::blockdata::{opcodes, script}; - use bitcoin::hashes::Hash; - use bitcoin::script::PushBytes; + use bitcoin::hashes::hex::FromHex; + use bitcoin::script::{PushBytes, ScriptBufExt as _, ScriptExt as _}; use bitcoin::sighash::EcdsaSighashType; - use bitcoin::{bip32, PublicKey, Sequence, XOnlyPublicKey}; + use bitcoin::witness::WitnessExt; + use bitcoin::{bip32, PublicKey, Sequence}; use super::{checksum, *}; use crate::hex_script; @@ -1402,7 +1404,7 @@ mod tests { let ms = ms_str!("c:pk_k({})", pk); let mut txin = bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: bitcoin::ScriptBuf::new(), sequence: Sequence::from_height(100), witness: Witness::default(), @@ -1413,7 +1415,7 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: script::Builder::new() .push_slice(<&PushBytes>::try_from(sigser.as_slice()).unwrap()) .into_script(), @@ -1428,10 +1430,10 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: script::Builder::new() .push_slice(<&PushBytes>::try_from(sigser.as_slice()).unwrap()) - .push_key(&pk) + .push_key(pk) .into_script(), sequence: Sequence::from_height(100), witness: Witness::default(), @@ -1444,7 +1446,7 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: bitcoin::ScriptBuf::new(), sequence: Sequence::from_height(100), witness: Witness::from_slice(&[sigser.clone(), pk.to_bytes()]), @@ -1465,7 +1467,7 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: script::Builder::new() .push_slice(<&PushBytes>::try_from(redeem_script.as_bytes()).unwrap()) .into_script(), @@ -1486,7 +1488,7 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: script::Builder::new() .push_slice(<&PushBytes>::try_from(sigser.as_slice()).unwrap()) .push_slice(<&PushBytes>::try_from(ms.encode().as_bytes()).unwrap()) @@ -1504,7 +1506,7 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: bitcoin::ScriptBuf::new(), sequence: Sequence::from_height(100), witness: Witness::from_slice(&[sigser.clone(), ms.encode().into_bytes()]), @@ -1517,9 +1519,17 @@ mod tests { assert_eq!( txin, bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: script::Builder::new() - .push_slice(<&PushBytes>::try_from(ms.encode().to_p2wsh().as_bytes()).unwrap()) + .push_slice( + <&PushBytes>::try_from( + ms.encode() + .to_p2wsh() + .expect("TODO: Handle error") + .as_bytes() + ) + .unwrap() + ) .into_script(), sequence: Sequence::from_height(100), witness: Witness::from_slice(&[sigser.clone(), ms.encode().into_bytes()]), @@ -1528,7 +1538,15 @@ mod tests { assert_eq!( shwsh.unsigned_script_sig(), script::Builder::new() - .push_slice(<&PushBytes>::try_from(ms.encode().to_p2wsh().as_bytes()).unwrap()) + .push_slice( + <&PushBytes>::try_from( + ms.encode() + .to_p2wsh() + .expect("TODO: Handle error") + .as_bytes() + ) + .unwrap() + ) .into_script() ); } @@ -1664,7 +1682,7 @@ mod tests { .unwrap(); let mut txin = bitcoin::TxIn { - previous_output: bitcoin::OutPoint::default(), + previous_output: bitcoin::OutPoint::COINBASE_PREVOUT, script_sig: bitcoin::ScriptBuf::new(), sequence: Sequence::ZERO, witness: Witness::default(), diff --git a/src/descriptor/segwitv0.rs b/src/descriptor/segwitv0.rs index a78867fba..0787225a6 100644 --- a/src/descriptor/segwitv0.rs +++ b/src/descriptor/segwitv0.rs @@ -8,6 +8,7 @@ use core::convert::TryFrom; use core::fmt; +use bitcoin::address::script_pubkey::ScriptExt; use bitcoin::{Address, Network, ScriptBuf, Weight}; use super::SortedMultiVec; @@ -144,13 +145,19 @@ impl Wsh { impl Wsh { /// Obtains the corresponding script pubkey for this descriptor. - pub fn script_pubkey(&self) -> ScriptBuf { self.inner_script().to_p2wsh() } + pub fn script_pubkey(&self) -> ScriptBuf { + self.inner_script().to_p2wsh().expect("TODO: Handle error") + } /// Obtains the corresponding script pubkey for this descriptor. pub fn address(&self, network: Network) -> Address { match self.inner { - WshInner::SortedMulti(ref smv) => Address::p2wsh(&smv.encode(), network), - WshInner::Ms(ref ms) => Address::p2wsh(&ms.encode(), network), + WshInner::SortedMulti(ref smv) => { + Address::p2wsh(&smv.encode(), network).expect("TODO: Handle error") + } + WshInner::Ms(ref ms) => { + Address::p2wsh(&ms.encode(), network).expect("TODO: Handle error") + } } } @@ -379,7 +386,7 @@ impl Wpkh { let compressed = bitcoin::key::CompressedPublicKey::try_from(pk) .expect("wpkh descriptors have compressed keys"); - let addr = Address::p2wpkh(&compressed, Network::Bitcoin); + let addr = Address::p2wpkh(compressed, Network::Bitcoin); addr.script_pubkey() } @@ -389,7 +396,7 @@ impl Wpkh { let compressed = bitcoin::key::CompressedPublicKey::try_from(pk) .expect("Rust Miniscript types don't allow uncompressed pks in segwit descriptors"); - Address::p2wpkh(&compressed, network) + Address::p2wpkh(compressed, network) } /// Obtains the underlying miniscript for this descriptor. diff --git a/src/descriptor/sh.rs b/src/descriptor/sh.rs index 4acb5fa73..89dac6d13 100644 --- a/src/descriptor/sh.rs +++ b/src/descriptor/sh.rs @@ -10,6 +10,7 @@ use core::convert::TryFrom; use core::fmt; +use bitcoin::address::script_pubkey::ScriptExt; use bitcoin::script::PushBytes; use bitcoin::{script, Address, Network, ScriptBuf, Weight}; @@ -274,7 +275,7 @@ impl Sh { impl Sh { /// Obtains the corresponding script pubkey for this descriptor. - pub fn script_pubkey(&self) -> ScriptBuf { + pub fn script_pubkey(&self) -> Result { match self.inner { ShInner::Wsh(ref wsh) => wsh.script_pubkey().to_p2sh(), ShInner::Wpkh(ref wpkh) => wpkh.script_pubkey().to_p2sh(), @@ -338,7 +339,7 @@ impl Sh { match self.inner { ShInner::Wsh(ref wsh) => { // wsh explicit must contain exactly 1 element - let witness_script = wsh.inner_script().to_p2wsh(); + let witness_script = wsh.inner_script().to_p2wsh().expect("TODO: Handle error"); let push_bytes = <&PushBytes>::try_from(witness_script.as_bytes()) .expect("Witness script is not too large"); script::Builder::new().push_slice(push_bytes).into_script() diff --git a/src/interpreter/error.rs b/src/interpreter/error.rs index 4e47932a6..69672efe2 100644 --- a/src/interpreter/error.rs +++ b/src/interpreter/error.rs @@ -33,7 +33,7 @@ pub enum Error { /// General Interpreter error. CouldNotEvaluate, /// ECDSA Signature related error - EcdsaSig(bitcoin::ecdsa::Error), + EcdsaSig(bitcoin::ecdsa::DecodeError), /// We expected a push (including a `OP_1` but no other numeric pushes) ExpectedPush, /// The preimage to the hash function must be exactly 32 bytes. @@ -247,8 +247,8 @@ impl From for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: bitcoin::ecdsa::Error) -> Error { Error::EcdsaSig(e) } +impl From for Error { + fn from(e: bitcoin::ecdsa::DecodeError) -> Error { Error::EcdsaSig(e) } } #[doc(hidden)] diff --git a/src/interpreter/inner.rs b/src/interpreter/inner.rs index 0564b7e79..d185d2385 100644 --- a/src/interpreter/inner.rs +++ b/src/interpreter/inner.rs @@ -1,7 +1,9 @@ // Written in 2019 by Sanket Kanjular and Andrew Poelstra // SPDX-License-Identifier: CC0-1.0 -use bitcoin::hashes::{hash160, sha256, Hash}; +use bitcoin::address::script_pubkey::{ScriptBufExt as _, ScriptExt as _}; +use bitcoin::hashes::{hash160, sha256}; +use bitcoin::script::ScriptExt as _; use bitcoin::taproot::{ControlBlock, TAPROOT_ANNEX_PREFIX}; use bitcoin::Witness; @@ -130,7 +132,7 @@ pub(super) fn from_txdata<'txin>( Some(elem) => { let pk = pk_from_stack_elem(&elem, false)?; if *spk - == bitcoin::ScriptBuf::new_p2pkh(&pk.to_pubkeyhash(SigType::Ecdsa).into()) + == bitcoin::ScriptBuf::new_p2pkh(pk.to_pubkeyhash(SigType::Ecdsa).into()) { Ok(( Inner::PublicKey(pk.into(), PubkeyType::Pkh), @@ -153,11 +155,11 @@ pub(super) fn from_txdata<'txin>( Some(elem) => { let pk = pk_from_stack_elem(&elem, true)?; let hash160 = pk.to_pubkeyhash(SigType::Ecdsa); - if *spk == bitcoin::ScriptBuf::new_p2wpkh(&hash160.into()) { + if *spk == bitcoin::ScriptBuf::new_p2wpkh(hash160.into()) { Ok(( Inner::PublicKey(pk.into(), PubkeyType::Wpkh), wit_stack, - Some(bitcoin::ScriptBuf::new_p2pkh(&hash160.into())), // bip143, why.. + Some(bitcoin::ScriptBuf::new_p2pkh(hash160.into())), // bip143, why.. )) } else { Err(Error::IncorrectWPubkeyHash) @@ -177,7 +179,7 @@ pub(super) fn from_txdata<'txin>( let script = miniscript.encode(); let miniscript = miniscript.to_no_checks_ms(); let scripthash = sha256::Hash::hash(script.as_bytes()); - if *spk == bitcoin::ScriptBuf::new_p2wsh(&scripthash.into()) { + if *spk == bitcoin::ScriptBuf::new_p2wsh(scripthash.into()) { Ok((Inner::Script(miniscript, ScriptType::Wsh), wit_stack, Some(script))) } else { Err(Error::IncorrectWScriptHash) @@ -248,7 +250,7 @@ pub(super) fn from_txdata<'txin>( Some(elem) => { if let stack::Element::Push(slice) = elem { let scripthash = hash160::Hash::hash(slice); - if *spk != bitcoin::ScriptBuf::new_p2sh(&scripthash.into()) { + if *spk != bitcoin::ScriptBuf::new_p2sh(scripthash.into()) { return Err(Error::IncorrectScriptHash); } // ** p2sh-wrapped wpkh ** @@ -261,13 +263,12 @@ pub(super) fn from_txdata<'txin>( let pk = pk_from_stack_elem(&elem, true)?; let hash160 = pk.to_pubkeyhash(SigType::Ecdsa); if slice - == bitcoin::ScriptBuf::new_p2wpkh(&hash160.into()) - .as_bytes() + == bitcoin::ScriptBuf::new_p2wpkh(hash160.into()).as_bytes() { Ok(( Inner::PublicKey(pk.into(), PubkeyType::ShWpkh), wit_stack, - Some(bitcoin::ScriptBuf::new_p2pkh(&hash160.into())), // bip143, why.. + Some(bitcoin::ScriptBuf::new_p2pkh(hash160.into())), // bip143, why.. )) } else { Err(Error::IncorrectWScriptHash) @@ -289,7 +290,7 @@ pub(super) fn from_txdata<'txin>( let miniscript = miniscript.to_no_checks_ms(); let scripthash = sha256::Hash::hash(script.as_bytes()); if slice - == bitcoin::ScriptBuf::new_p2wsh(&scripthash.into()) + == bitcoin::ScriptBuf::new_p2wsh(scripthash.into()) .as_bytes() { Ok(( @@ -312,7 +313,7 @@ pub(super) fn from_txdata<'txin>( let miniscript = miniscript.to_no_checks_ms(); if wit_stack.is_empty() { let scripthash = hash160::Hash::hash(script.as_bytes()); - if *spk == bitcoin::ScriptBuf::new_p2sh(&scripthash.into()) { + if *spk == bitcoin::ScriptBuf::new_p2sh(scripthash.into()) { Ok((Inner::Script(miniscript, ScriptType::Sh), ssig_stack, Some(script))) } else { Err(Error::IncorrectScriptHash) @@ -397,6 +398,7 @@ mod tests { use core::convert::TryFrom; use core::str::FromStr; + use bitcoin::address::script_pubkey::BuilderExt as _; use bitcoin::blockdata::script; use bitcoin::script::PushBytes; use bitcoin::ScriptBuf; @@ -433,22 +435,22 @@ mod tests { let pkhash = key.to_pubkeyhash(SigType::Ecdsa).into(); let wpkhash = key.to_pubkeyhash(SigType::Ecdsa).into(); - let wpkh_spk = bitcoin::ScriptBuf::new_p2wpkh(&wpkhash); + let wpkh_spk = bitcoin::ScriptBuf::new_p2wpkh(wpkhash); let wpkh_scripthash = hash160::Hash::hash(wpkh_spk.as_bytes()).into(); KeyTestData { - pk_spk: bitcoin::ScriptBuf::new_p2pk(&key), - pkh_spk: bitcoin::ScriptBuf::new_p2pkh(&pkhash), + pk_spk: bitcoin::ScriptBuf::new_p2pk(key), + pkh_spk: bitcoin::ScriptBuf::new_p2pkh(pkhash), pk_sig: script::Builder::new().push_slice(dummy_sig).into_script(), pkh_sig: script::Builder::new() .push_slice(dummy_sig) - .push_key(&key) + .push_key(key) .into_script(), - pkh_sig_justkey: script::Builder::new().push_key(&key).into_script(), + pkh_sig_justkey: script::Builder::new().push_key(key).into_script(), wpkh_spk: wpkh_spk.clone(), wpkh_stack: Witness::from_slice(&[dummy_sig_vec.clone(), key.to_bytes()]), wpkh_stack_justkey: Witness::from_slice(&[key.to_bytes()]), - sh_wpkh_spk: bitcoin::ScriptBuf::new_p2sh(&wpkh_scripthash), + sh_wpkh_spk: bitcoin::ScriptBuf::new_p2sh(wpkh_scripthash), sh_wpkh_sig: script::Builder::new() .push_slice(<&PushBytes>::try_from(wpkh_spk[..].as_bytes()).unwrap()) .into_script(), @@ -717,7 +719,7 @@ mod tests { let (miniscript, redeem_script) = ms_inner_script(&format!("hash160({})", hash)); let rs_hash = hash160::Hash::hash(redeem_script.as_bytes()).into(); - let spk = ScriptBuf::new_p2sh(&rs_hash); + let spk = ScriptBuf::new_p2sh(rs_hash); let script_sig = script::Builder::new() .push_slice(<&PushBytes>::try_from(redeem_script.as_bytes()).unwrap()) .into_script(); @@ -753,7 +755,7 @@ mod tests { let wit_hash = sha256::Hash::hash(witness_script.as_bytes()).into(); let wit_stack = Witness::from_slice(&[witness_script.to_bytes()]); - let spk = ScriptBuf::new_p2wsh(&wit_hash); + let spk = ScriptBuf::new_p2wsh(wit_hash); let blank_script = bitcoin::ScriptBuf::new(); // wsh without witness @@ -788,14 +790,14 @@ mod tests { let wit_hash = sha256::Hash::hash(witness_script.as_bytes()).into(); let wit_stack = Witness::from_slice(&[witness_script.to_bytes()]); - let redeem_script = ScriptBuf::new_p2wsh(&wit_hash); + let redeem_script = ScriptBuf::new_p2wsh(wit_hash); let script_sig = script::Builder::new() .push_slice(<&PushBytes>::try_from(redeem_script.as_bytes()).unwrap()) .into_script(); let blank_script = bitcoin::ScriptBuf::new(); let rs_hash = hash160::Hash::hash(redeem_script.as_bytes()).into(); - let spk = ScriptBuf::new_p2sh(&rs_hash); + let spk = ScriptBuf::new_p2sh(rs_hash); // shwsh without witness or scriptsig let err = from_txdata(&spk, &blank_script, &Witness::default()).unwrap_err(); diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 2914aafc4..4ef745cdd 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -11,7 +11,8 @@ use core::fmt; use core::str::FromStr; -use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; +use bitcoin::hashes::{hash160, ripemd160, sha256}; +use bitcoin::taproot::TapLeafHashExt as _; use bitcoin::{absolute, relative, secp256k1, sighash, taproot, Sequence, TxOut, Witness}; use crate::miniscript::context::{NoChecks, SigType}; @@ -1050,7 +1051,6 @@ fn verify_sersig<'txin>( #[cfg(test)] mod tests { - use bitcoin::secp256k1::Secp256k1; use super::inner::ToNoChecks; diff --git a/src/interpreter/stack.rs b/src/interpreter/stack.rs index 647a84713..176755336 100644 --- a/src/interpreter/stack.rs +++ b/src/interpreter/stack.rs @@ -4,7 +4,7 @@ //! Interpreter stack use bitcoin::blockdata::{opcodes, script}; -use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; +use bitcoin::hashes::{hash160, ripemd160, sha256}; use bitcoin::{absolute, relative, Sequence}; use super::error::PkEvalErrInner; @@ -282,7 +282,7 @@ impl<'txin> Stack<'txin> { if preimage.len() != 32 { return Some(Err(Error::HashPreimageLengthMismatch)); } - if hash256::Hash::hash(preimage) == *hash { + if bitcoin::hashes::sha256d::Hash::hash(preimage) == (*hash).into() { self.push(Element::Satisfied); Some(Ok(SatisfiedConstraint::HashLock { hash: HashLockType::Hash256(*hash), diff --git a/src/lib.rs b/src/lib.rs index eac67c5d1..d73dc0b30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -130,7 +130,9 @@ mod util; use core::{fmt, hash, str}; -use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; +use bitcoin::hashes::{hash160, ripemd160, sha256}; +use bitcoin::hex::DisplayHex; +use bitcoin::{script, Opcode}; pub use crate::blanket_traits::FromStrKey; pub use crate::descriptor::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey}; @@ -439,7 +441,15 @@ pub enum Error { /// rust-bitcoin address error AddrError(bitcoin::address::ParseError), /// rust-bitcoin p2sh address error - AddrP2shError(bitcoin::address::P2shError), + RedeemScriptSizeError(bitcoin::script::RedeemScriptSizeError), + /// A `CHECKMULTISIG` opcode was preceded by a number > 20 + CmsTooManyKeys(u32), + /// A tapscript multi_a cannot support more than Weight::MAX_BLOCK/32 keys + MultiATooManyKeys(u64), + /// Encountered unprintable character in descriptor + Unprintable(u8), + /// expected character while parsing descriptor; didn't find one + ExpectedChar(char), /// While parsing backward, hit beginning of script UnexpectedStart, /// Got something we were not expecting @@ -512,7 +522,10 @@ impl fmt::Display for Error { match *self { Error::ScriptLexer(ref e) => e.fmt(f), Error::AddrError(ref e) => fmt::Display::fmt(e, f), - Error::AddrP2shError(ref e) => fmt::Display::fmt(e, f), + Error::RedeemScriptSizeError(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), Error::UnexpectedStart => f.write_str("unexpected start of script"), Error::Unexpected(ref s) => write!(f, "unexpected «{}»", s), Error::UnknownWrapper(ch) => write!(f, "unknown wrapper «{}:»", ch), @@ -578,7 +591,7 @@ impl std::error::Error for Error { | MultipathDescLenMismatch => None, ScriptLexer(e) => Some(e), AddrError(e) => Some(e), - AddrP2shError(e) => Some(e), + RedeemScriptSizeError(e) => Some(e), Secp(e) => Some(e), #[cfg(feature = "compiler")] CompilerError(e) => Some(e), @@ -638,8 +651,8 @@ impl From for Error { } #[doc(hidden)] -impl From for Error { - fn from(e: bitcoin::address::P2shError) -> Error { Error::AddrP2shError(e) } +impl From for Error { + fn from(e: bitcoin::script::RedeemScriptSizeError) -> Error { Error::RedeemScriptSizeError(e) } } #[doc(hidden)] diff --git a/src/miniscript/astelem.rs b/src/miniscript/astelem.rs index 19611b083..f5121fd4c 100644 --- a/src/miniscript/astelem.rs +++ b/src/miniscript/astelem.rs @@ -7,7 +7,10 @@ //! encoding in Bitcoin script, as well as a datatype. Full details //! are given on the Miniscript website. -use bitcoin::hashes::Hash; +use core::str::FromStr; + +use bitcoin::address::script_pubkey::BuilderExt; +use bitcoin::hashes::hash160; use bitcoin::{absolute, opcodes, script}; use crate::miniscript::context::SigType; @@ -52,35 +55,35 @@ impl Terminal { .push_slice(hash.to_byte_array()) .push_opcode(opcodes::all::OP_EQUALVERIFY), Terminal::After(t) => builder - .push_int(absolute::LockTime::from(t).to_consensus_u32() as i64) + .push_int_unchecked(absolute::LockTime::from(t).to_consensus_u32() as i64) .push_opcode(opcodes::all::OP_CLTV), Terminal::Older(t) => builder - .push_int(t.to_consensus_u32().into()) + .push_int_unchecked(t.to_consensus_u32() as i64) .push_opcode(opcodes::all::OP_CSV), Terminal::Sha256(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) - .push_int(32) + .push_int_unchecked(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_SHA256) .push_slice(Pk::to_sha256(h).to_byte_array()) .push_opcode(opcodes::all::OP_EQUAL), Terminal::Hash256(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) - .push_int(32) + .push_int_unchecked(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_HASH256) .push_slice(Pk::to_hash256(h).to_byte_array()) .push_opcode(opcodes::all::OP_EQUAL), Terminal::Ripemd160(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) - .push_int(32) + .push_int_unchecked(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_RIPEMD160) .push_slice(Pk::to_ripemd160(h).to_byte_array()) .push_opcode(opcodes::all::OP_EQUAL), Terminal::Hash160(ref h) => builder .push_opcode(opcodes::all::OP_SIZE) - .push_int(32) + .push_int_unchecked(32) .push_opcode(opcodes::all::OP_EQUALVERIFY) .push_opcode(opcodes::all::OP_HASH160) .push_slice(Pk::to_hash160(h).to_byte_array()) @@ -149,17 +152,17 @@ impl Terminal { builder = builder.push_astelem(sub).push_opcode(opcodes::all::OP_ADD); } builder - .push_int(thresh.k() as i64) + .push_int_unchecked(thresh.k() as i64) .push_opcode(opcodes::all::OP_EQUAL) } Terminal::Multi(ref thresh) => { debug_assert!(Ctx::sig_type() == SigType::Ecdsa); - builder = builder.push_int(thresh.k() as i64); + builder = builder.push_int_unchecked(thresh.k() as i64); for pk in thresh.data() { - builder = builder.push_key(&pk.to_public_key()); + builder = builder.push_key(pk.to_public_key()); } builder - .push_int(thresh.n() as i64) + .push_int_unchecked(thresh.n() as i64) .push_opcode(opcodes::all::OP_CHECKMULTISIG) } Terminal::MultiA(ref thresh) => { @@ -172,7 +175,7 @@ impl Terminal { builder = builder.push_opcode(opcodes::all::OP_CHECKSIGADD); } builder - .push_int(thresh.k() as i64) + .push_int_unchecked(thresh.k() as i64) .push_opcode(opcodes::all::OP_NUMEQUAL) } } diff --git a/src/miniscript/decode.rs b/src/miniscript/decode.rs index 213ac8266..9ace554d1 100644 --- a/src/miniscript/decode.rs +++ b/src/miniscript/decode.rs @@ -9,7 +9,7 @@ use core::{fmt, mem}; #[cfg(feature = "std")] use std::error; -use bitcoin::hashes::{hash160, ripemd160, sha256, Hash}; +use bitcoin::hashes::{hash160, ripemd160, sha256}; use sync::Arc; use crate::iter::TreeLike; diff --git a/src/miniscript/iter.rs b/src/miniscript/iter.rs index 0ac48e651..e9f41fb31 100644 --- a/src/miniscript/iter.rs +++ b/src/miniscript/iter.rs @@ -203,7 +203,7 @@ impl Iterator for PkIter<'_, Pk, Ctx> { /// dependent libraries for their own tasts based on Miniscript AST #[cfg(test)] pub mod test { - use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash}; + use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d}; use super::Miniscript; use crate::miniscript::context::Segwitv0; diff --git a/src/miniscript/lex.rs b/src/miniscript/lex.rs index 1df1f2dd4..e87ba42a7 100644 --- a/src/miniscript/lex.rs +++ b/src/miniscript/lex.rs @@ -9,6 +9,7 @@ use core::fmt; use bitcoin::blockdata::{opcodes, script}; use bitcoin::hex::DisplayHex as _; +use bitcoin::script::ScriptExt; use crate::prelude::*; @@ -205,22 +206,26 @@ pub fn lex(script: &'_ script::Script) -> Result, Error> { ret.push(Token::Hash256); } script::Instruction::PushBytes(bytes) => { - if let Ok(bytes) = bytes.as_bytes().try_into() { - ret.push(Token::Hash20(bytes)); - } else if let Ok(bytes) = bytes.as_bytes().try_into() { - ret.push(Token::Bytes32(bytes)); - } else if let Ok(bytes) = bytes.as_bytes().try_into() { - ret.push(Token::Bytes33(bytes)); - } else if let Ok(bytes) = bytes.as_bytes().try_into() { - ret.push(Token::Bytes65(bytes)); - } else { - // check minimality of the number - match script::read_scriptint(bytes.as_bytes()) { - Ok(v) if v >= 0 => { - ret.push(Token::Num(v as u32)); + match bytes.len() { + 20 => ret.push(Token::Hash20(bytes.as_bytes())), + 32 => ret.push(Token::Bytes32(bytes.as_bytes())), + 33 => ret.push(Token::Bytes33(bytes.as_bytes())), + 65 => ret.push(Token::Bytes65(bytes.as_bytes())), + _ => { + match bytes.read_scriptint() { + Ok(v) if v >= 0 => { + // check minimality of the number + let builder = script::Builder::new().push_int_unchecked(v as i64); + if builder.into_script()[1..].as_bytes() == bytes.as_bytes() { + ret.push(Token::Num(v as u32)); + } else { + return Err(Error::InvalidPush(bytes.to_owned().into())); + } + } + Ok(_) => return Err(Error::InvalidPush(bytes.to_owned().into())), + Err(e) => return Err(Error::Script(e)), } - Ok(n) => return Err(Error::NegativeInt { bytes: bytes.to_owned(), n }), - Err(err) => return Err(Error::InvalidInt { bytes: bytes.to_owned(), err }), + // FIXME(tcharding): I might have botched the rebase here. } } } diff --git a/src/miniscript/mod.rs b/src/miniscript/mod.rs index a36cef547..b7e5172e5 100644 --- a/src/miniscript/mod.rs +++ b/src/miniscript/mod.rs @@ -16,7 +16,7 @@ use core::{hash, str}; use bitcoin::hashes::hash160; use bitcoin::script; -use bitcoin::taproot::{LeafVersion, TapLeafHash}; +use bitcoin::taproot::{LeafVersion, TapLeafHash, TapLeafHashExt as _}; use self::analyzable::ExtParams; pub use self::context::{BareCtx, Legacy, Segwitv0, Tap}; @@ -1048,15 +1048,40 @@ impl str::FromStr for Miniscript { serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext); -/// Provides a Double SHA256 `Hash` type that displays forwards. +/// Provides a general purpose Double SHA256 `Hash` type that displays forwards. pub mod hash256 { - use bitcoin::hashes::{hash_newtype, sha256d}; + use bitcoin::hashes::{hash_newtype, sha256d, GeneralHash, HashEngine as _}; hash_newtype! { /// A hash256 of preimage. #[hash_newtype(forward)] pub struct Hash(sha256d::Hash); } + + impl Hash { + /// Constructs a new engine. + pub fn engine() -> sha256d::HashEngine { sha256d::HashEngine::default() } + + /// Produces a hash from the current state of a given engine. + pub fn from_engine(e: sha256d::HashEngine) -> Self { + let sha256d = sha256d::Hash::from_engine(e); + Hash(sha256d) + } + + /// Hashes some bytes. + pub fn hash(data: &[u8]) -> Self { + let mut engine = Self::engine(); + engine.input(data); + Self::from_engine(engine) + } + } + + impl GeneralHash for Hash { + type Engine = sha256d::HashEngine; + + fn engine() -> Self::Engine { Hash::engine() } + fn from_engine(e: Self::Engine) -> Self { Self::from_engine(e) } + } } #[cfg(test)] @@ -1065,7 +1090,7 @@ mod tests { use core::str; use core::str::FromStr; - use bitcoin::hashes::{hash160, sha256, Hash}; + use bitcoin::hashes::{hash160, sha256}; use bitcoin::secp256k1::XOnlyPublicKey; use bitcoin::taproot::TapLeafHash; use sync::Arc; @@ -1675,7 +1700,7 @@ mod tests { "02c2fd50ceae468857bb7eb32ae9cd4083e6c7e42fbbec179d81134b3e3830586c", ) .unwrap(); - let hash160 = pk.pubkey_hash().to_raw_hash(); + let hash160 = pk.pubkey_hash(); let ms_str = &format!("c:expr_raw_pkh({})", hash160); type SegwitMs = Miniscript; @@ -1692,7 +1717,7 @@ mod tests { // Try replacing the raw_pkh with a pkh let mut map = BTreeMap::new(); - map.insert(hash160, pk); + map.insert(hash160::Hash::from_byte_array(hash160.to_byte_array()), pk); let ms_no_raw = ms.substitute_raw_pkh(&map); assert_eq!(ms_no_raw.to_string(), format!("pkh({})", pk),); } diff --git a/src/plan.rs b/src/plan.rs index c3bfd5618..eb21f5125 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -18,6 +18,7 @@ use core::iter::FromIterator; +use bitcoin::address::script_pubkey::ScriptExt as _; use bitcoin::hashes::{hash160, ripemd160, sha256}; use bitcoin::key::XOnlyPublicKey; use bitcoin::script::PushBytesBuf; @@ -406,7 +407,8 @@ impl Plan { Descriptor::Sh(sh) => match sh.as_inner() { descriptor::ShInner::Wsh(wsh) => { input.witness_script = Some(wsh.inner_script()); - input.redeem_script = Some(wsh.inner_script().to_p2wsh()); + input.redeem_script = + Some(wsh.inner_script().to_p2wsh().expect("TODO: Handle erorr")); } descriptor::ShInner::Wpkh(..) => input.redeem_script = Some(sh.inner_script()), descriptor::ShInner::SortedMulti(_) | descriptor::ShInner::Ms(_) => { diff --git a/src/policy/compiler.rs b/src/policy/compiler.rs index 9775320d1..5dab95245 100644 --- a/src/policy/compiler.rs +++ b/src/policy/compiler.rs @@ -1227,6 +1227,7 @@ where mod tests { use core::str::FromStr; + use bitcoin::address::script_pubkey::BuilderExt as _; use bitcoin::blockdata::{opcodes, script}; use bitcoin::hashes; @@ -1347,7 +1348,7 @@ mod tests { assert_eq!( ms.encode(), script::Builder::new() - .push_key(&keys[0]) + .push_key(keys[0]) .push_opcode(opcodes::all::OP_CHECKSIG) .into_script() ); @@ -1364,12 +1365,12 @@ mod tests { ms.encode(), script::Builder::new() .push_opcode(opcodes::all::OP_PUSHNUM_2) - .push_key(&keys[5]) - .push_key(&keys[6]) - .push_key(&keys[7]) + .push_key(keys[5]) + .push_key(keys[6]) + .push_key(keys[7]) .push_opcode(opcodes::all::OP_PUSHNUM_3) .push_opcode(opcodes::all::OP_CHECKMULTISIGVERIFY) - .push_int(10000) + .push_int_unchecked(10000) .push_opcode(opcodes::all::OP_CSV) .into_script() ); diff --git a/src/psbt/finalizer.rs b/src/psbt/finalizer.rs index 9c7259713..925e0a315 100644 --- a/src/psbt/finalizer.rs +++ b/src/psbt/finalizer.rs @@ -11,8 +11,10 @@ use core::convert::TryFrom; use core::mem; +use bitcoin::address::script_pubkey::ScriptExt as _; use bitcoin::hashes::hash160; use bitcoin::key::XOnlyPublicKey; +use bitcoin::script::ScriptExt as _; #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/121684 use bitcoin::secp256k1; use bitcoin::secp256k1::Secp256k1; @@ -152,8 +154,8 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In let public_keys = psbt_input.bip32_derivation.keys(); for key in public_keys { let bitcoin_key = bitcoin::PublicKey::new(*key); - let hash = bitcoin_key.pubkey_hash().to_raw_hash(); - map.insert(hash, bitcoin_key); + let hash = bitcoin_key.pubkey_hash().to_byte_array(); + map.insert(hash160::Hash::from_byte_array(hash), bitcoin_key); } } @@ -191,7 +193,7 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In 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); + let addr = bitcoin::Address::p2wpkh(compressed, bitcoin::Network::Bitcoin); *script_pubkey == addr.script_pubkey() } Err(_) => false, @@ -207,7 +209,7 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In return Err(InputError::NonEmptyRedeemScript); } if let Some(ref witness_script) = inp.witness_script { - if witness_script.to_p2wsh() != *script_pubkey { + if witness_script.to_p2wsh().expect("TODO: Handle error") != *script_pubkey { return Err(InputError::InvalidWitnessScript { witness_script: witness_script.clone(), p2wsh_expected: script_pubkey.clone(), @@ -225,7 +227,7 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In match inp.redeem_script { None => Err(InputError::MissingRedeemScript), Some(ref redeem_script) => { - if redeem_script.to_p2sh() != *script_pubkey { + if redeem_script.to_p2sh().expect("TODO: Handle error") != *script_pubkey { return Err(InputError::InvalidRedeemScript { redeem: redeem_script.clone(), p2sh_expected: script_pubkey.clone(), @@ -234,7 +236,8 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In if redeem_script.is_p2wsh() { // 5. `ShWsh` case if let Some(ref witness_script) = inp.witness_script { - if witness_script.to_p2wsh() != *redeem_script { + if witness_script.to_p2wsh().expect("TODO: Handle error") != *redeem_script + { return Err(InputError::InvalidWitnessScript { witness_script: witness_script.clone(), p2wsh_expected: redeem_script.clone(), @@ -253,10 +256,8 @@ fn get_descriptor(psbt: &Psbt, index: usize) -> Result, In let partial_sig_contains_pk = inp.partial_sigs.iter().find(|&(&pk, _sig)| { match bitcoin::key::CompressedPublicKey::try_from(pk) { Ok(compressed) => { - let addr = bitcoin::Address::p2wpkh( - &compressed, - bitcoin::Network::Bitcoin, - ); + let addr = + bitcoin::Address::p2wpkh(compressed, bitcoin::Network::Bitcoin); *redeem_script == addr.script_pubkey() } Err(_) => false, @@ -318,10 +319,9 @@ pub fn interpreter_check( let witness = input .final_script_witness .as_ref() - .map(|wit_slice| Witness::from_slice(&wit_slice.to_vec())) // TODO: Update rust-bitcoin psbt API to use witness - .unwrap_or(empty_witness); + .unwrap_or(&empty_witness); - interpreter_inp_check(psbt, secp, index, utxos, &witness, script_sig)?; + interpreter_inp_check(psbt, secp, index, utxos, witness, script_sig)?; } Ok(()) } diff --git a/src/psbt/mod.rs b/src/psbt/mod.rs index 916f8d3c1..ef429399d 100644 --- a/src/psbt/mod.rs +++ b/src/psbt/mod.rs @@ -12,13 +12,16 @@ use core::fmt; #[cfg(feature = "std")] use std::error; -use bitcoin::hashes::{hash160, sha256d, Hash}; +use bitcoin::address::script_pubkey::ScriptExt as _; +use bitcoin::hashes::{hash160, sha256d}; use bitcoin::psbt::{self, Psbt}; +use bitcoin::script::ScriptExt as _; #[cfg(not(test))] // https://github.com/rust-lang/rust/issues/121684 use bitcoin::secp256k1; use bitcoin::secp256k1::{Secp256k1, VerifyOnly}; use bitcoin::sighash::{self, SighashCache}; -use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapLeafHash}; +use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapLeafHash, TapLeafHashExt as _}; +use bitcoin::transaction::TxInExt as _; use bitcoin::{absolute, bip32, relative, transaction, Script, ScriptBuf}; use crate::miniscript::context::SigType; @@ -1159,7 +1162,8 @@ fn update_item_with_descriptor_helper( Descriptor::Sh(sh) => match sh.as_inner() { descriptor::ShInner::Wsh(wsh) => { *item.witness_script() = Some(wsh.inner_script()); - *item.redeem_script() = Some(wsh.inner_script().to_p2wsh()); + *item.redeem_script() = + Some(wsh.inner_script().to_p2wsh().expect("TODO: Handle error")); } descriptor::ShInner::Wpkh(..) => *item.redeem_script() = Some(sh.inner_script()), descriptor::ShInner::SortedMulti(_) | descriptor::ShInner::Ms(_) => { @@ -1370,7 +1374,9 @@ mod tests { use bitcoin::bip32::{DerivationPath, Xpub}; use bitcoin::consensus::encode::deserialize; use bitcoin::key::XOnlyPublicKey; + use bitcoin::script::ScriptBufExt as _; use bitcoin::secp256k1::PublicKey; + use bitcoin::transaction::VersionExt as _; use bitcoin::{Amount, OutPoint, TxIn, TxOut}; use hex; @@ -1579,7 +1585,7 @@ mod tests { lock_time: absolute::LockTime::ZERO, input: vec![TxIn { previous_output: OutPoint { txid: non_witness_utxo.compute_txid(), vout: 0 }, - ..Default::default() + ..TxIn::EMPTY_COINBASE }], output: vec![], }; diff --git a/src/util.rs b/src/util.rs index 5de37b780..ef043e798 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,16 +2,25 @@ use core::convert::TryFrom; -use bitcoin::constants::MAX_SCRIPT_ELEMENT_SIZE; -use bitcoin::hashes::Hash; +use bitcoin::address::script_pubkey::BuilderExt as _; +use bitcoin::constants::MAX_REDEEM_SCRIPT_SIZE; +use bitcoin::hashes::hash160; use bitcoin::script::{self, PushBytes, ScriptBuf}; -use bitcoin::PubkeyHash; use crate::miniscript::context; use crate::miniscript::satisfy::Placeholder; use crate::prelude::*; use crate::{MiniscriptKey, ScriptContext, ToPublicKey}; -pub(crate) fn varint_len(n: usize) -> usize { bitcoin::VarInt(n as u64).size() } + +// Copied from `bitcoin_internals::compact_size`. +pub(crate) fn varint_len(n: usize) -> usize { + match n { + 0..=0xFC => 1, + 0xFD..=0xFFFF => 3, + 0x10000..=0xFFFFFFFF => 5, + _ => 9, + } +} pub(crate) trait ItemSize { fn size(&self) -> usize; @@ -52,15 +61,25 @@ pub(crate) fn witness_size(wit: &[T]) -> usize { } pub(crate) fn witness_to_scriptsig(witness: &[Vec]) -> ScriptBuf { + let read_scriptint = |slice: &[u8]| { + if let Ok(push) = <&PushBytes>::try_from(slice) { + if let Ok(n) = push.read_scriptint() { + return Some(n); + } + } + None + }; + let mut b = script::Builder::new(); for (i, wit) in witness.iter().enumerate() { - if let Ok(n) = script::read_scriptint(wit) { - b = b.push_int(n); + if let Some(n) = read_scriptint(wit) { + // FIXME: Use `push_int` and handle errors. + b = b.push_int_unchecked(n); } else { if i != witness.len() - 1 { assert!(wit.len() < 73, "All pushes in miniscript are < 73 bytes"); } else { - assert!(wit.len() <= MAX_SCRIPT_ELEMENT_SIZE, "P2SH redeem script is <= 520 bytes"); + assert!(wit.len() <= MAX_REDEEM_SCRIPT_SIZE, "P2SH redeem script is <= 520 bytes"); } let push = <&PushBytes>::try_from(wit.as_slice()).expect("checked above"); b = b.push_slice(push) @@ -91,7 +110,7 @@ impl MsKeyBuilder for script::Builder { Ctx: ScriptContext, { match Ctx::sig_type() { - context::SigType::Ecdsa => self.push_key(&key.to_public_key()), + context::SigType::Ecdsa => self.push_key(key.to_public_key()), context::SigType::Schnorr => self.push_slice(key.to_x_only_pubkey().serialize()), } } @@ -104,7 +123,10 @@ impl MsKeyBuilder for script::Builder { match Ctx::sig_type() { context::SigType::Ecdsa => self.push_slice(key.to_public_key().pubkey_hash()), context::SigType::Schnorr => { - self.push_slice(PubkeyHash::hash(&key.to_x_only_pubkey().serialize())) + let hash = hash160::Hash::hash(&key.to_x_only_pubkey().serialize()); + self.push_slice( + <&PushBytes>::try_from(hash.as_byte_array()).expect("32 bytes is fine to push"), + ) } } }