Repository: rust-bitcoin/rust-bitcoin
Pull request #1621 (2023-02-04 04:22:01)

Rename `from_slice` methods to `decode`
The `TaprootMerkleBranch` and `ControlBlock` both have methods on them called `from_slice` but these methods do more that just basic copy from a slice. `decode` is a more descriptive name.

Deprecate the `from_slice` methods and implement `decode`, on other changes to the logic.

cc sanket1729

tcharding: Thanks for the review man. I used you suggestions but I went with using only the heading line for the deprecated functions.

Patch:
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 9b671e0a..51bd0f36 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -21,6 +21,7 @@ serde = ["actual-serde", "bitcoin_hashes/serde", "secp256k1/serde"]
 secp-lowmemory = ["secp256k1/lowmemory"]
 secp-recovery = ["secp256k1/recovery"]
 bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
+rust_v_1_53 = []
 
 # At least one of std, no-std must be enabled.
 #
diff --git a/bitcoin/build.rs b/bitcoin/build.rs
index d4785580..0879f651 100644
--- a/bitcoin/build.rs
+++ b/bitcoin/build.rs
@@ -23,7 +23,7 @@ fn main() {
         .parse::<u64>()
         .expect("invalid Rust minor version");
 
-    for activate_version in &[46, 53] {
+    for activate_version in &[46] {
         if minor >= *activate_version {
             println!("cargo:rustc-cfg=rust_v_1_{}", activate_version);
         }
diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs
index af99fad5..a8eacbb5 100644
--- a/bitcoin/src/bip152.rs
+++ b/bitcoin/src/bip152.rs
@@ -376,9 +376,9 @@ mod test {
     use crate::blockdata::locktime::absolute;
     use crate::consensus::encode::{deserialize, serialize};
     use crate::hashes::hex::FromHex;
-    use crate::hash_types::TxMerkleNode;
     use crate::{
-        CompactTarget, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut, Txid, Witness,
+        CompactTarget, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxMerkleNode, TxOut, Txid,
+        Witness,
     };
 
     fn dummy_tx(nonce: &[u8]) -> Transaction {
diff --git a/bitcoin/src/blockdata/script.rs b/bitcoin/src/blockdata/script.rs
index cef569bb..ee496c0f 100644
--- a/bitcoin/src/blockdata/script.rs
+++ b/bitcoin/src/blockdata/script.rs
@@ -59,7 +59,7 @@
 use core::borrow::{Borrow, BorrowMut};
 use core::{fmt, default::Default};
 use core::ops::{Deref, DerefMut, Index, Range, RangeFull, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive};
-#[cfg(rust_v_1_53)]
+#[cfg(feature = "rust_v_1_53")]
 use core::ops::Bound;
 
 #[cfg(feature = "serde")] use serde;
@@ -615,8 +615,8 @@ fn index(&self, index: $type) -> &Self::Output {
 }
 
 delegate_index!(Range<usize>, RangeFrom<usize>, RangeTo<usize>, RangeFull, RangeInclusive<usize>, RangeToInclusive<usize>);
-#[cfg(rust_v_1_53)]
-#[cfg_attr(docsrs, doc(cfg(rust_v_1_53)))]
+#[cfg(feature = "rust_v_1_53")]
+#[cfg_attr(docsrs, doc(cfg(feature = "rust_v_1_53")))]
 delegate_index!((Bound<usize>, Bound<usize>));
 
 impl AsRef<Script> for Script {
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 620c41d3..6a9bb334 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -568,8 +568,7 @@ impl<E> EncodeSigningDataResult<E> {
     ///
     /// ```rust
     /// # use bitcoin::consensus::deserialize;
-    /// # use bitcoin::Transaction;
-    /// # use bitcoin::hash_types::Sighash;
+    /// # use bitcoin::{Transaction, Sighash};
     /// # use bitcoin_hashes::{Hash, hex::FromHex};
     /// # let mut writer = Sighash::engine();
     /// # let input_index = 0;
diff --git a/bitcoin/src/crypto/schnorr.rs b/bitcoin/src/crypto/schnorr.rs
index 3f1630c6..5cf9cec8 100644
--- a/bitcoin/src/crypto/schnorr.rs
+++ b/bitcoin/src/crypto/schnorr.rs
@@ -48,7 +48,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 /// # Examples
 /// ```
 /// # #[cfg(feature = "rand-std")] {
-/// # use bitcoin::schnorr::{KeyPair, TweakedKeyPair, TweakedPublicKey};
+/// # use bitcoin::schnorr::{TweakedKeyPair, TweakedPublicKey};
 /// # use bitcoin::secp256k1::{rand, Secp256k1};
 /// # let secp = Secp256k1::new();
 /// # let keypair = TweakedKeyPair::dangerous_assume_tweaked(KeyPair::new(&secp, &mut rand::thread_rng()));
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index e7d4f7c0..a5288cbb 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -137,7 +137,7 @@
 pub use crate::crypto::key::{self, PrivateKey, PublicKey};
 pub use crate::crypto::{ecdsa, schnorr};
 pub use crate::error::Error;
-pub use crate::hash_types::{Txid, Wtxid, BlockHash, PubkeyHash,  ScriptHash, WPubkeyHash, WScriptHash};
+pub use crate::hash_types::*;
 pub use crate::merkle_tree::MerkleBlock;
 pub use crate::network::constants::Network;
 pub use crate::pow::{CompactTarget, Target, Work};
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index a760e994..ebca2936 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -11,6 +11,7 @@
 use std::collections::{HashMap, HashSet};
 
 use core::{fmt, cmp};
+use core::ops::Deref;
 
 use secp256k1::{Message, Secp256k1, Signing};
 use bitcoin_internals::write_err;
@@ -265,7 +266,7 @@ fn bip32_sign_ecdsa<C, K, T>(
     ) -> Result<Vec<PublicKey>, SignError>
     where
         C: Signing,
-        T: Borrow<Transaction>,
+        T: Deref<Target=Transaction>,
         K: GetKey,
     {
         let msg_sighash_ty_res = self.sighash_ecdsa(input_index, cache);
@@ -308,7 +309,7 @@ fn bip32_sign_ecdsa<C, K, T>(
     /// Uses the [`EcdsaSighashType`] from this input if one is specified. If no sighash type is
     /// specified uses [`EcdsaSighashType::All`]. This function does not support scripts that
     /// contain `OP_CODESEPARATOR`.
-    pub fn sighash_ecdsa<T: Borrow<Transaction>>(
+    pub fn sighash_ecdsa<T: Deref<Target=Transaction>>(
         &self,
         input_index: usize,
         cache: &mut SighashCache<T>,
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index f908f975..d8dba369 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -335,7 +335,7 @@ fn serialize(&self) -> Vec<u8> {
 
 impl Deserialize for ControlBlock {
     fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
-        Self::from_slice(bytes)
+        Self::decode(bytes)
             .map_err(|_| Error::InvalidControlBlock)
     }
 }
diff --git a/bitcoin/src/sighash.rs b/bitcoin/src/sighash.rs
index f6e1afdd..39e4a1a3 100644
--- a/bitcoin/src/sighash.rs
+++ b/bitcoin/src/sighash.rs
@@ -1,26 +1,23 @@
 // SPDX-License-Identifier: CC0-1.0
 
-//! Signature hash implementation (used in transaction signing).
+//! Generalized, efficient, signature hash implementation.
 //!
-//! Efficient implementation of the algorithm to compute the message to be signed according to
+//! Implementation of the algorithm to compute the message to be signed according to
 //! [Bip341](https://github.com/bitcoin/bips/blob/150ab6f5c3aca9da05fccc5b435e9667853407f4/bip-0341.mediawiki),
 //! [Bip143](https://github.com/bitcoin/bips/blob/99701f68a88ce33b2d0838eb84e115cef505b4c2/bip-0143.mediawiki)
 //! and legacy (before Bip143).
 //!
-//! Computing signature hashes is required to sign a transaction and this module is designed to
-//! handle its complexity efficiently. Computing these hashes is as simple as creating
-//! [`SighashCache`] and calling its methods.
 
-use core::borrow::{Borrow, BorrowMut};
+use core::borrow::Borrow;
+use core::ops::{Deref, DerefMut};
 use core::{fmt, str};
 
-use crate::{io, Script, ScriptBuf, Transaction, TxIn, TxOut, Sequence};
+use crate::{io, Script, ScriptBuf, Transaction, TxIn, TxOut, Sequence, Sighash};
 use crate::blockdata::transaction::EncodeSigningDataResult;
 use crate::blockdata::witness::Witness;
 use crate::consensus::{encode, Encodable};
 use crate::error::impl_std_error;
 use crate::hashes::{sha256, sha256d, Hash};
-use crate::hash_types::Sighash;
 use crate::prelude::*;
 use crate::taproot::{LeafVersion, TapLeafHash, TapSighashHash, TAPROOT_ANNEX_PREFIX};
 
@@ -35,9 +32,9 @@
 
 /// Efficiently calculates signature hash message for legacy, segwit and taproot inputs.
 #[derive(Debug)]
-pub struct SighashCache<T: Borrow<Transaction>> {
+pub struct SighashCache<T: Deref<Target = Transaction>> {
     /// Access to transaction required for transaction introspection. Moreover, type
-    /// `T: Borrow<Transaction>` allows us to use borrowed and mutable borrowed types,
+    /// `T: Deref<Target=Transaction>` allows us to use borrowed and mutable borrowed types,
     /// the latter in particular is necessary for [`SighashCache::witness_mut`].
     tx: T,
 
@@ -493,7 +490,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 
 impl_std_error!(SighashTypeParseError);
 
-impl<R: Borrow<Transaction>> SighashCache<R> {
+impl<R: Deref<Target = Transaction>> SighashCache<R> {
     /// Constructs a new `SighashCache` from an unsigned transaction.
     ///
     /// The sighash components are computed in a lazy manner when required. For the generated
@@ -503,16 +500,6 @@ pub fn new(tx: R) -> Self {
         SighashCache { tx, common_cache: None, taproot_cache: None, segwit_cache: None }
     }
 
-    /// Returns the reference to the cached transaction.
-    pub fn transaction(&self) -> &Transaction {
-        self.tx.borrow()
-    }
-
-    /// Destroys the cache and recovers the stored transaction.
-    pub fn into_transaction(self) -> R {
-        self.tx
-    }
-
     /// Encodes the BIP341 signing data for any flag type into a given object implementing a
     /// [`io::Write`] trait.
     pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
@@ -524,7 +511,7 @@ pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
         leaf_hash_code_separator: Option<(TapLeafHash, u32)>,
         sighash_type: SchnorrSighashType,
     ) -> Result<(), Error> {
-        prevouts.check_all(self.tx.borrow())?;
+        prevouts.check_all(&self.tx)?;
 
         let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
 
@@ -537,10 +524,10 @@ pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
 
         // * Transaction Data:
         // nVersion (4): the nVersion of the transaction.
-        self.tx.borrow().version.consensus_encode(&mut writer)?;
+        self.tx.version.consensus_encode(&mut writer)?;
 
         // nLockTime (4): the nLockTime of the transaction.
-        self.tx.borrow().lock_time.consensus_encode(&mut writer)?;
+        self.tx.lock_time.consensus_encode(&mut writer)?;
 
         // If the hash_type & 0x80 does not equal SIGHASH_ANYONECANPAY:
         //     sha_prevouts (32): the SHA256 of the serialization of all input outpoints.
@@ -578,9 +565,9 @@ pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
         //      scriptPubKey (35): scriptPubKey of the previous output spent by this input, serialized as script inside CTxOut. Its size is always 35 bytes.
         //      nSequence (4): nSequence of this input.
         if anyone_can_pay {
-            let txin = &self.tx.borrow().input.get(input_index).ok_or(Error::IndexOutOfInputsBounds {
+            let txin = &self.tx.input.get(input_index).ok_or(Error::IndexOutOfInputsBounds {
                 index: input_index,
-                inputs_size: self.tx.borrow().input.len(),
+                inputs_size: self.tx.input.len(),
             })?;
             let previous_output = prevouts.get(input_index)?;
             txin.previous_output.consensus_encode(&mut writer)?;
@@ -607,12 +594,11 @@ pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
         if sighash == SchnorrSighashType::Single {
             let mut enc = sha256::Hash::engine();
             self.tx
-                .borrow()
                 .output
                 .get(input_index)
                 .ok_or(Error::SingleWithoutCorrespondingOutput {
                     index: input_index,
-                    outputs_size: self.tx.borrow().output.len(),
+                    outputs_size: self.tx.output.len(),
                 })?
                 .consensus_encode(&mut enc)?;
             let hash = sha256::Hash::from_engine(enc);
@@ -709,7 +695,7 @@ pub fn segwit_encode_signing_data_to<Write: io::Write>(
 
         let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
 
-        self.tx.borrow().version.consensus_encode(&mut writer)?;
+        self.tx.version.consensus_encode(&mut writer)?;
 
         if !anyone_can_pay {
             self.segwit_cache().prevouts.consensus_encode(&mut writer)?;
@@ -727,9 +713,9 @@ pub fn segwit_encode_signing_data_to<Write: io::Write>(
         }
 
         {
-            let txin = &self.tx.borrow().input.get(input_index).ok_or(Error::IndexOutOfInputsBounds {
+            let txin = &self.tx.input.get(input_index).ok_or(Error::IndexOutOfInputsBounds {
                 index: input_index,
-                inputs_size: self.tx.borrow().input.len(),
+                inputs_size: self.tx.input.len(),
             })?;
 
             txin.previous_output.consensus_encode(&mut writer)?;
@@ -740,16 +726,16 @@ pub fn segwit_encode_signing_data_to<Write: io::Write>(
 
         if sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None {
             self.segwit_cache().outputs.consensus_encode(&mut writer)?;
-        } else if sighash == EcdsaSighashType::Single && input_index < self.tx.borrow().output.len() {
+        } else if sighash == EcdsaSighashType::Single && input_index < self.tx.output.len() {
             let mut single_enc = Sighash::engine();
-            self.tx.borrow().output[input_index].consensus_encode(&mut single_enc)?;
+            self.tx.output[input_index].consensus_encode(&mut single_enc)?;
             let hash = Sighash::from_engine(single_enc);
             writer.write_all(&hash[..])?;
         } else {
             writer.write_all(&zero_hash[..])?;
         }
 
-        self.tx.borrow().lock_time.consensus_encode(&mut writer)?;
+        self.tx.lock_time.consensus_encode(&mut writer)?;
         sighash_type.to_u32().consensus_encode(&mut writer)?;
         Ok(())
     }
@@ -802,15 +788,15 @@ pub fn legacy_encode_signing_data_to<Write: io::Write, U: Into<u32>>(
         script_pubkey: &Script,
         sighash_type: U,
     ) -> EncodeSigningDataResult<Error> {
-        if input_index >= self.tx.borrow().input.len() {
+        if input_index >= self.tx.input.len() {
             return EncodeSigningDataResult::WriteResult(Err(Error::IndexOutOfInputsBounds {
                 index: input_index,
-                inputs_size: self.tx.borrow().input.len(),
+                inputs_size: self.tx.input.len(),
             }));
         }
         let sighash_type: u32 = sighash_type.into();
 
-        if is_invalid_use_of_sighash_single(sighash_type, input_index, self.tx.borrow().output.len()) {
+        if is_invalid_use_of_sighash_single(sighash_type, input_index, self.tx.output.len()) {
             // We cannot correctly handle the SIGHASH_SINGLE bug here because usage of this function
             // will result in the data written to the writer being hashed, however the correct
             // handling of the SIGHASH_SINGLE bug is to return the 'one array' - either implement
@@ -896,7 +882,7 @@ fn encode_signing_data_to_inner<Write: io::Write>(
 
         EncodeSigningDataResult::WriteResult(
             encode_signing_data_to_inner(
-                self.tx.borrow(),
+                &self.tx,
                 writer,
                 input_index,
                 script_pubkey,
@@ -944,12 +930,12 @@ pub fn legacy_signature_hash(
 
     #[inline]
     fn common_cache(&mut self) -> &CommonCache {
-        Self::common_cache_minimal_borrow(&mut self.common_cache, self.tx.borrow())
+        Self::common_cache_minimal_borrow(&mut self.common_cache, &self.tx)
     }
 
     fn common_cache_minimal_borrow<'a>(
         common_cache: &'a mut Option<CommonCache>,
-        tx: &Transaction,
+        tx: &R,
     ) -> &'a CommonCache {
         common_cache.get_or_insert_with(|| {
             let mut enc_prevouts = sha256::Hash::engine();
@@ -974,7 +960,7 @@ fn common_cache_minimal_borrow<'a>(
 
     fn segwit_cache(&mut self) -> &SegwitCache {
         let common_cache = &mut self.common_cache;
-        let tx = self.tx.borrow();
+        let tx = &self.tx;
         self.segwit_cache.get_or_insert_with(|| {
             let common_cache = Self::common_cache_minimal_borrow(common_cache, tx);
             SegwitCache {
@@ -1001,7 +987,7 @@ fn taproot_cache<T: Borrow<TxOut>>(&mut self, prevouts: &[T]) -> &TaprootCache {
     }
 }
 
-impl<R: BorrowMut<Transaction>> SighashCache<R> {
+impl<R: DerefMut<Target = Transaction>> SighashCache<R> {
     /// When the `SighashCache` is initialized with a mutable reference to a transaction instead of
     /// a regular reference, this method is available to allow modification to the witnesses.
     ///
@@ -1022,7 +1008,7 @@ impl<R: BorrowMut<Transaction>> SighashCache<R> {
     /// }
     /// ```
     pub fn witness_mut(&mut self, input_index: usize) -> Option<&mut Witness> {
-        self.tx.borrow_mut().input.get_mut(input_index).map(|i| &mut i.witness)
+        self.tx.input.get_mut(input_index).map(|i| &mut i.witness)
     }
 }
 
diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs
index a9ee2174..1aef78c0 100644
--- a/bitcoin/src/taproot.rs
+++ b/bitcoin/src/taproot.rs
@@ -644,8 +644,22 @@ impl TaprootMerkleBranch {
     /// Returns a reference to the inner vector of hashes.
     pub fn as_inner(&self) -> &[TapNodeHash] { &self.0 }
 
-    /// Creates a merkle proof from raw data representing a list of hashes.
+    /// Decodes bytes from control block.
+    #[deprecated(since = "0.30.0", note = "Use decode instead")]
     pub fn from_slice(sl: &[u8]) -> Result<Self, TaprootError> {
+        Self::decode(sl)
+    }
+
+    /// Decodes bytes from control block.
+    ///
+    /// This reads the branch as encoded in the control block: the concatenated 32B byte chunks -
+    /// one for each hash.
+    ///
+    /// # Errors
+    ///
+    /// The function returns an error if the the number of bytes is not an integer multiple of 32 or
+    /// if the number of hashes exceeds 128.
+    pub fn decode(sl: &[u8]) -> Result<Self, TaprootError> {
         if sl.len() % TAPROOT_CONTROL_NODE_SIZE != 0 {
             Err(TaprootError::InvalidMerkleBranchSize(sl.len()))
         } else if sl.len() > TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT {
@@ -727,23 +741,6 @@ fn try_from(v: $from) -> Result<Self, Self::Error> {
 impl_try_from!(Vec<TapNodeHash>);
 impl_try_from!(Box<[TapNodeHash]>);
 
-macro_rules! impl_try_from_array {
-    ($($len:expr),* $(,)?) => {
-        $(
-            impl From<[TapNodeHash; $len]> for TaprootMerkleBranch {
-                fn from(a: [TapNodeHash; $len]) -> Self {
-                    Self(a.to_vec())
-                }
-            }
-        )*
-    }
-}
-// Implement for all values [0, 128] inclusive.
-//
-// The reason zero is included is that `TaprootMerkleBranch` doesn't contain the hash of the node
-// that's being proven - it's not needed because the script is already right before control block.
-impl_try_from_array!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128);
-
 /// Control block data structure used in Tapscript satisfaction.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -760,9 +757,17 @@ pub struct ControlBlock {
 }
 
 impl ControlBlock {
-    /// Constructs a `ControlBlock` from slice. This is an extra witness element that provides the
-    /// proof that taproot script pubkey is correctly computed with some specified leaf hash. This
-    /// is the last element in taproot witness when spending a output via script path.
+    /// Constructs a `ControlBlock` from slice.
+    #[deprecated(since = "0.30.0", note = "Use decode instead")]
+    pub fn from_slice(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
+        Self::decode(sl)
+    }
+
+    /// Decodes bytes representing a `ControlBlock`.
+    ///
+    /// This is an extra witness element that provides the proof that taproot script pubkey is
+    /// correctly computed with some specified leaf hash. This is the last element in taproot
+    /// witness when spending a output via script path.
     ///
     /// # Errors
     ///
@@ -771,7 +776,7 @@ impl ControlBlock {
     /// - [`TaprootError::InvalidTaprootLeafVersion`] if first byte of `sl` is not a valid leaf version.
     /// - [`TaprootError::InvalidInternalKey`] if internal key is invalid (first 32 bytes after the parity byte).
     /// - [`TaprootError::InvalidMerkleTreeDepth`] if merkle tree is too deep (more than 128 levels).
-    pub fn from_slice(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
+    pub fn decode(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
         if sl.len() < TAPROOT_CONTROL_BASE_SIZE
             || (sl.len() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE != 0
         {
@@ -782,7 +787,7 @@ pub fn from_slice(sl: &[u8]) -> Result<ControlBlock, TaprootError> {
         let leaf_version = LeafVersion::from_consensus(sl[0] & TAPROOT_LEAF_MASK)?;
         let internal_key = UntweakedPublicKey::from_slice(&sl[1..TAPROOT_CONTROL_BASE_SIZE])
             .map_err(TaprootError::InvalidInternalKey)?;
-        let merkle_branch = TaprootMerkleBranch::from_slice(&sl[TAPROOT_CONTROL_BASE_SIZE..])?;
+        let merkle_branch = TaprootMerkleBranch::decode(&sl[TAPROOT_CONTROL_BASE_SIZE..])?;
         Ok(ControlBlock { leaf_version, output_key_parity, internal_key, merkle_branch })
     }
 
@@ -1224,7 +1229,7 @@ fn _verify_tap_commitments(
         let out_pk = TweakedPublicKey::dangerous_assume_tweaked(out_pk);
         let script = ScriptBuf::from_hex(script_hex).unwrap();
         let control_block =
-            ControlBlock::from_slice(&Vec::<u8>::from_hex(control_block_hex).unwrap()).unwrap();
+            ControlBlock::decode(&Vec::<u8>::from_hex(control_block_hex).unwrap()).unwrap();
         assert_eq!(control_block_hex, control_block.serialize().to_lower_hex_string());
         assert!(control_block.verify_taproot_commitment(secp, out_pk.to_inner(), &script));
     }
@@ -1448,7 +1453,7 @@ fn process_script_trees(
                 let spend_info = builder.finalize(secp, internal_key).unwrap();
                 for (i, script_ver) in leaves.iter().enumerate() {
                     let expected_leaf_hash = leaf_hashes[i].as_str().unwrap();
-                    let expected_ctrl_blk = ControlBlock::from_slice(
+                    let expected_ctrl_blk = ControlBlock::decode(
                         &Vec::<u8>::from_hex(ctrl_blks[i].as_str().unwrap()).unwrap(),
                     )
                     .unwrap();


Test patch:
diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs
index 47fccf67..79088793 100644
--- a/bitcoin/tests/serde.rs
+++ b/bitcoin/tests/serde.rs
@@ -187,7 +187,7 @@ fn serde_regression_ecdsa_sig() {
 #[test]
 fn serde_regression_control_block() {
     let s = include_str!("data/serde/control_block_hex");
-    let block = ControlBlock::from_slice(&Vec::<u8>::from_hex(s.trim()).unwrap()).unwrap();
+    let block = ControlBlock::decode(&Vec::<u8>::from_hex(s.trim()).unwrap()).unwrap();
     let got = serialize(&block).unwrap();
 
     let want = include_bytes!("data/serde/control_block_bincode") as &[_];