Repository: rust-bitcoin/rust-bitcoin
Pull request #1682 (2023-03-01 14:35:50)

Include address in Error::NetworkValidation
Fixes: #1677

## Change
In `bitcoin/src/address.rs`, a new field `address` is added to the enum variant `Error::NetworkValidation`. Also, the implementation of `Display` trait for `Error` is updated to print the `address` field.

However, to print the `address` through `Display`, either the reference is needed or `Address` and `Payload` both need to derive the `Copy` trait. Since I am little new to both the rust-bitcoin codebase and rust itself, I am confused about choosing between the two and have moved with the first one. Would appreciate any feedback on this.

Kixunil: Could you squash the commits, please? Also looks like you added more indentation than required so while you're at it you might as well fix that.
Subhra264: > Also looks like you added more indentation than required so while you're at it you might as well fix that.

Ohh my bad! didn't notice it. Yeah fixing it
Subhra264: Strange, in my editor, it looks fine. In fact, I added indentation because that line had less indentation (at least in my editor). Maybe something related to tabs. Anyways fixing it

Patch:
diff --git a/README.md b/README.md
index e31dfd68..d2305355 100644
--- a/README.md
+++ b/README.md
@@ -79,9 +79,6 @@ For more information please see `./CONTRIBUTING.md`.
 This library should always compile with any combination of features (minus
 `no-std`) on **Rust 1.41.1** or **Rust 1.47** with `no-std`.
 
-To build with the MSRV you will need to pin some dependencies, currently this is
-only `syn`, and can be achieved using `cargo update -p syn --precise 1.0.107`.
-
 ## Installing Rust
 
 Rust can be installed using your package manager of choice or
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 20f3c96b..64ee773d 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -37,7 +37,7 @@ rustdoc-args = ["--cfg", "docsrs"]
 bitcoin-internals = { path = "../internals" }
 bech32 = { version = "0.9.0", default-features = false }
 bitcoin_hashes = { version = "0.11.0", default-features = false }
-secp256k1 = { git = "https://github.com/rust-bitcoin/rust-secp256k1", tag = "2023-02-01--fix-hashes", default-features = false, features = ["bitcoin_hashes"] }
+secp256k1 = { version = "0.25.0", default-features = false, features = ["bitcoin_hashes"] }
 hex_lit = "0.1.1"
 
 base64 = { version = "0.13.0", optional = true }
@@ -50,7 +50,7 @@ actual-serde = { package = "serde", version = "1.0.103", default-features = fals
 serde_json = "1.0.0"
 serde_test = "1.0.19"
 serde_derive = "1.0.103"
-secp256k1 = { git = "https://github.com/rust-bitcoin/rust-secp256k1", tag = "2023-02-01--fix-hashes", features = ["recovery"] }
+secp256k1 = { version = "0.25.0", features = ["recovery"] }
 bincode = "1.3.1"
 
 [target.'cfg(mutate)'.dev-dependencies]
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 6fd098eb..55d6dd34 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -81,12 +81,16 @@
 use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, ExtendedPubKey, Fingerprint};
 use bitcoin::consensus::encode;
 use bitcoin::constants::COIN_VALUE;
+use bitcoin::crypto::taproot;
+use bitcoin::hashes::Hash;
 use bitcoin::key::{TapTweak, XOnlyPublicKey};
 use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
 use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
-use bitcoin::secp256k1::Secp256k1;
-use bitcoin::sighash::{self, SighashCache, TapSighash, TapSighashType};
-use bitcoin::taproot::{self, LeafVersion, TapLeafHash, TaprootBuilder, TaprootSpendInfo};
+use bitcoin::secp256k1::{Message, Secp256k1};
+use bitcoin::sighash::{self, TapSighashType, SighashCache};
+use bitcoin::taproot::{
+    LeafVersion, TapLeafHash, TapSighash, TaprootBuilder, TaprootSpendInfo,
+};
 use bitcoin::{
     absolute, script, Address, Amount, Network, OutPoint, ScriptBuf, Transaction, TxIn, TxOut,  Witness,
 };
@@ -741,7 +745,7 @@ fn sign_psbt_taproot(
         Some(_) => keypair, // no tweak for script spend
     };
 
-    let sig = secp.sign_schnorr(&hash.into(), &keypair);
+    let sig = secp.sign_schnorr(&Message::from_slice(&hash.into_inner()[..]).unwrap(), &keypair);
 
     let final_signature = taproot::Signature { sig, hash_ty };
 
diff --git a/bitcoin/src/address.rs b/bitcoin/src/address.rs
index c8f56430..bdf4e7e6 100644
--- a/bitcoin/src/address.rs
+++ b/bitcoin/src/address.rs
@@ -92,7 +92,9 @@ pub enum Error {
         /// Network that was required.
         required: Network,
         /// Network on which the address was found to be valid.
-        found: Network
+        found: Network,
+        /// The address itself
+        address: Address<NetworkUnchecked>
     }
 }
 
@@ -112,7 +114,11 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
             Error::ExcessiveScriptSize => write!(f, "script size exceed 520 bytes"),
             Error::UnrecognizedScript => write!(f, "script is not a p2pkh, p2sh or witness program"),
             Error::UnknownAddressType(ref s) => write!(f, "unknown address type: '{}' is either invalid or not supported in rust-bitcoin", s),
-	    Error::NetworkValidation { required, found } => write!(f, "address's network {} is different from required {}", found, required),
+            Error::NetworkValidation { required, found, ref address } => {
+                write!(f, "address ")?;
+                address.fmt_internal(f)?; // Using fmt_internal in order to remove the "Address<NetworkUnchecked>(..)" wrapper
+                write!(f, " belongs to network {} which is different from required {}", found, required)
+            }
         }
     }
 }
@@ -435,12 +441,13 @@ impl Payload {
     /// Constructs a [Payload] from an output script (`scriptPubkey`).
     pub fn from_script(script: &Script) -> Result<Payload, Error> {
         Ok(if script.is_p2pkh() {
-            let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long");
-            Payload::PubkeyHash(PubkeyHash::from_byte_array(bytes))
-
+            let mut hash_inner = [0u8; 20];
+            hash_inner.copy_from_slice(&script.as_bytes()[3..23]);
+            Payload::PubkeyHash(PubkeyHash::from_inner(hash_inner))
         } else if script.is_p2sh() {
-            let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long");
-            Payload::ScriptHash(ScriptHash::from_byte_array(bytes))
+            let mut hash_inner = [0u8; 20];
+            hash_inner.copy_from_slice(&script.as_bytes()[2..22]);
+            Payload::ScriptHash(ScriptHash::from_inner(hash_inner))
         } else if script.is_witness_program() {
             let opcode = script.first_opcode().expect("witness_version guarantees len() > 4");
 
@@ -468,23 +475,6 @@ pub fn script_pubkey(&self) -> ScriptBuf {
         }
     }
 
-    /// Returns true if the address creates a particular script
-    /// This function doesn't make any allocations.
-    pub fn matches_script_pubkey(&self, script: &Script) -> bool {
-        match *self {
-            Payload::PubkeyHash(ref hash) if script.is_p2pkh() => {
-                &script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash)
-            },
-            Payload::ScriptHash(ref hash) if script.is_p2sh() => {
-                 &script.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash)
-            },
-            Payload::WitnessProgram(ref prog) if script.is_witness_program() => {
-                &script.as_bytes()[2..] == prog.program.as_bytes()
-            },
-            Payload::PubkeyHash(_) | Payload::ScriptHash(_) | Payload::WitnessProgram(_) => false,
-        }
-    }
-
     /// Creates a pay to (compressed) public key hash payload from a public key
     #[inline]
     pub fn p2pkh(pk: &PublicKey) -> Payload { Payload::PubkeyHash(pk.pubkey_hash()) }
@@ -958,9 +948,9 @@ pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool {
         let payload = self.payload.inner_prog_as_bytes();
         let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
 
-        (*pubkey_hash.as_byte_array() == *payload)
+        (*pubkey_hash.as_inner() == *payload)
             || (xonly_pubkey.serialize() == *payload)
-            || (*segwit_redeem_hash(&pubkey_hash).as_byte_array() == *payload)
+            || (*segwit_redeem_hash(&pubkey_hash).as_inner() == *payload)
     }
 
     /// Returns true if the supplied xonly public key can be used to derive the address.
@@ -971,12 +961,6 @@ pub fn is_related_to_xonly_pubkey(&self, xonly_pubkey: &XOnlyPublicKey) -> bool
         let payload = self.payload.inner_prog_as_bytes();
         payload == xonly_pubkey.serialize()
     }
-
-    /// Returns true if the address creates a particular script
-    /// This function doesn't make any allocations.
-    pub fn matches_script_pubkey(&self, script_pubkey: &Script) -> bool {
-        self.payload.matches_script_pubkey(script_pubkey)
-    }
 }
 
 /// Methods that can be called only on `Address<NetworkUnchecked>`.
@@ -1025,7 +1009,7 @@ pub fn require_network(self, required: Network) -> Result<Address, Error> {
         if self.is_valid_for_network(required) {
             Ok(self.assume_checked())
         } else {
-            Err(Error::NetworkValidation { found: self.network, required })
+            Err(Error::NetworkValidation { found: self.network, required, address: self })
         }
     }
 
@@ -1745,31 +1729,4 @@ fn invalid_address_parses_error() {
         let want = Err(Error::UnknownAddressType("invalid".to_string()));
         assert_eq!(got, want);
     }
-
-    #[test]
-    fn test_matches_script_pubkey() {
-        let addresses = [
-            "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY",
-            "1J4LVanjHMu3JkXbVrahNuQCTGCRRgfWWx",
-            "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k",
-            "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE",
-            "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
-            "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw",
-            "bc1p5cyxnuxmeuwuvkwfem96lqzszd02n6xdcjrs20cac6yqjjwudpxqkedrcr",
-            "bc1pgllnmtxs0g058qz7c6qgaqq4qknwrqj9z7rqn9e2dzhmcfmhlu4sfadf5e",
-        ];
-        for addr in &addresses {
-            let addr = Address::from_str(addr)
-                .unwrap()
-                .require_network(Network::Bitcoin)
-                .unwrap();
-            for another in &addresses {
-                let another = Address::from_str(another)
-                .unwrap()
-                .require_network(Network::Bitcoin)
-                .unwrap();
-                assert_eq!(addr.matches_script_pubkey(&another.script_pubkey()), addr == another);
-            }
-        }
-    }
 }
diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs
index 6e1c163a..af99fad5 100644
--- a/bitcoin/src/bip152.rs
+++ b/bitcoin/src/bip152.rs
@@ -221,8 +221,8 @@ pub fn from_block(
             } else {
                 short_ids.push(ShortId::with_siphash_keys(
                     &match version {
-                        1 => tx.txid().to_raw_hash(),
-                        2 => tx.wtxid().to_raw_hash(),
+                        1 => tx.txid().as_hash(),
+                        2 => tx.wtxid().as_hash(),
                         _ => unreachable!(),
                     },
                     siphash_keys,
diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs
index 277d81ea..0be1f29a 100644
--- a/bitcoin/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -170,7 +170,7 @@ pub struct BlockFilterWriter<'a, W> {
 impl<'a, W: io::Write> BlockFilterWriter<'a, W> {
     /// Creates a new [`BlockFilterWriter`] from `block`.
     pub fn new(writer: &'a mut W, block: &'a Block) -> BlockFilterWriter<'a, W> {
-        let block_hash_as_int = block.block_hash().to_byte_array();
+        let block_hash_as_int = block.block_hash().into_inner();
         let k0 = u64::from_le_bytes(block_hash_as_int[0..8].try_into().expect("8 byte slice"));
         let k1 = u64::from_le_bytes(block_hash_as_int[8..16].try_into().expect("8 byte slice"));
         let writer = GcsFilterWriter::new(writer, k0, k1, M, P);
@@ -225,7 +225,7 @@ pub struct BlockFilterReader {
 impl BlockFilterReader {
     /// Creates a new [`BlockFilterReader`] from `block_hash`.
     pub fn new(block_hash: &BlockHash) -> BlockFilterReader {
-        let block_hash_as_int = block_hash.to_byte_array();
+        let block_hash_as_int = block_hash.into_inner();
         let k0 = u64::from_le_bytes(block_hash_as_int[0..8].try_into().expect("8 byte slice"));
         let k1 = u64::from_le_bytes(block_hash_as_int[8..16].try_into().expect("8 byte slice"));
         BlockFilterReader { reader: GcsFilterReader::new(k0, k1, M, P) }
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index 45ab355c..3e525cc9 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -257,7 +257,7 @@ pub fn check_witness_commitment(&self) -> bool {
 
     /// Computes the transaction merkle root.
     pub fn compute_merkle_root(&self) -> Option<TxMerkleNode> {
-        let hashes = self.txdata.iter().map(|obj| obj.txid().to_raw_hash());
+        let hashes = self.txdata.iter().map(|obj| obj.txid().as_hash());
         merkle_tree::calculate_root(hashes).map(|h| h.into())
     }
 
@@ -274,9 +274,9 @@ pub fn witness_root(&self) -> Option<WitnessMerkleNode> {
         let hashes = self.txdata.iter().enumerate().map(|(i, t)| {
             if i == 0 {
                 // Replace the first hash with zeroes.
-                Wtxid::all_zeros().to_raw_hash()
+                Wtxid::all_zeros().as_hash()
             } else {
-                t.wtxid().to_raw_hash()
+                t.wtxid().as_hash()
             }
         });
         merkle_tree::calculate_root(hashes).map(|h| h.into())
diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs
index eb0534c8..3ae56bb5 100644
--- a/bitcoin/src/blockdata/constants.rs
+++ b/bitcoin/src/blockdata/constants.rs
@@ -54,8 +54,6 @@
 pub const SUBSIDY_HALVING_INTERVAL: u32 = 210_000;
 /// Maximum allowed value for an integer in Script.
 pub const MAX_SCRIPTNUM_VALUE: u32 = 0x80000000; // 2^31
-/// Number of blocks needed for an output from a coinbase transaction to be spendable.
-pub const COINBASE_MATURITY: u32 = 100;
 
 /// The maximum value allowed in an output (useful for sanity checking,
 /// since keeping everything below this value should prevent overflows
@@ -262,7 +260,7 @@ fn chain_hash_and_genesis_block(network: Network) {
         // The genesis block hash is a double-sha256 and it is displayed backwards.
         let genesis_hash = genesis_block(network).block_hash();
         // We abuse the sha256 hash here so we get a LowerHex impl that does not print the hex backwards.
-        let hash = sha256::Hash::from_slice(genesis_hash.as_byte_array()).unwrap();
+        let hash = sha256::Hash::from_slice(&genesis_hash.into_inner()).unwrap();
         let want = format!("{:02x}", hash);
 
         let chain_hash = ChainHash::using_genesis_block(network);
diff --git a/bitcoin/src/blockdata/fee_rate.rs b/bitcoin/src/blockdata/fee_rate.rs
index d413bb66..223b88e2 100644
--- a/bitcoin/src/blockdata/fee_rate.rs
+++ b/bitcoin/src/blockdata/fee_rate.rs
@@ -2,8 +2,6 @@
 
 use core::fmt;
 use core::ops::{Mul, Div};
-
-use crate::prelude::*;
 use crate::Amount;
 use super::Weight;
 
@@ -135,4 +133,4 @@ fn div(self, rhs: Weight) -> Self::Output {
     }
 }
 
-crate::parse::impl_parse_str_from_int_infallible!(FeeRate, u64, from_sat_per_kwu);
+crate::parse::impl_parse_str_through_int!(FeeRate);
diff --git a/bitcoin/src/blockdata/locktime/absolute.rs b/bitcoin/src/blockdata/locktime/absolute.rs
index 1f2fad50..eca049f6 100644
--- a/bitcoin/src/blockdata/locktime/absolute.rs
+++ b/bitcoin/src/blockdata/locktime/absolute.rs
@@ -9,6 +9,8 @@
 
 use core::{mem, fmt};
 use core::cmp::{PartialOrd, Ordering};
+use core::convert::TryFrom;
+use core::str::FromStr;
 
 use bitcoin_internals::write_err;
 
@@ -18,8 +20,8 @@
 use crate::consensus::encode::{self, Decodable, Encodable};
 use crate::error::ParseIntError;
 use crate::io::{self, Read, Write};
-use crate::parse::{impl_parse_str_from_int_infallible, impl_parse_str_from_int_fallible};
 use crate::prelude::*;
+use crate::parse::{self, impl_parse_str_through_int};
 use crate::string::FromHexStr;
 
 #[cfg(doc)]
@@ -274,7 +276,7 @@ pub fn to_consensus_u32(self) -> u32 {
     }
 }
 
-impl_parse_str_from_int_infallible!(LockTime, u32, from_consensus);
+impl_parse_str_through_int!(LockTime, from_consensus);
 
 impl From<Height> for LockTime {
     #[inline]
@@ -449,8 +451,6 @@ pub fn to_consensus_u32(self) -> u32 {
     }
 }
 
-impl_parse_str_from_int_fallible!(Height, u32, from_consensus, Error);
-
 impl fmt::Display for Height {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.0, f)
@@ -467,6 +467,37 @@ fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Se
     }
 }
 
+
+impl FromStr for Height {
+    type Err = Error;
+
+    #[inline]
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let n = parse::int(s)?;
+        Height::from_consensus(n)
+    }
+}
+
+impl TryFrom<&str> for Height {
+    type Error = Error;
+
+    #[inline]
+    fn try_from(s: &str) -> Result<Self, Self::Error> {
+        let n = parse::int(s)?;
+        Height::from_consensus(n)
+    }
+}
+
+impl TryFrom<String> for Height {
+    type Error = Error;
+
+    #[inline]
+    fn try_from(s: String) -> Result<Self, Self::Error> {
+        let n = parse::int(s)?;
+        Height::from_consensus(n)
+    }
+}
+
 /// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value.
 ///
 /// Note that there is no manipulation of the inner value during construction or when using
@@ -533,8 +564,6 @@ pub fn to_consensus_u32(self) -> u32 {
     }
 }
 
-impl_parse_str_from_int_fallible!(Time, u32, from_consensus, Error);
-
 impl fmt::Display for Time {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.0, f)
@@ -551,6 +580,36 @@ fn from_hex_str_no_prefix<S: AsRef<str> + Into<String>>(s: S) -> Result<Self, Se
     }
 }
 
+impl FromStr for Time {
+    type Err = Error;
+
+    #[inline]
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let n = parse::int(s)?;
+        Time::from_consensus(n)
+    }
+}
+
+impl TryFrom<&str> for Time {
+    type Error = Error;
+
+    #[inline]
+    fn try_from(s: &str) -> Result<Self, Self::Error> {
+        let n = parse::int(s)?;
+        Time::from_consensus(n)
+    }
+}
+
+impl TryFrom<String> for Time {
+    type Error = Error;
+
+    #[inline]
+    fn try_from(s: String) -> Result<Self, Self::Error> {
+        let n = parse::int(s)?;
+        Time::from_consensus(n)
+    }
+}
+
 /// Returns true if `n` is a block height i.e., less than 500,000,000.
 fn is_block_height(n: u32) -> bool {
     n < LOCK_TIME_THRESHOLD
diff --git a/bitcoin/src/blockdata/locktime/relative.rs b/bitcoin/src/blockdata/locktime/relative.rs
index 0188a9b4..c61519c4 100644
--- a/bitcoin/src/blockdata/locktime/relative.rs
+++ b/bitcoin/src/blockdata/locktime/relative.rs
@@ -13,9 +13,6 @@
 #[cfg(all(test, mutate))]
 use mutagen::mutate;
 
-use crate::parse::impl_parse_str_from_int_infallible;
-use crate::prelude::*;
-
 #[cfg(doc)]
 use crate::relative;
 
@@ -239,8 +236,6 @@ fn from(value: u16) -> Self {
     }
 }
 
-impl_parse_str_from_int_infallible!(Height, u16, from);
-
 impl fmt::Display for Height {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.0, f)
@@ -305,8 +300,6 @@ pub fn value(self) -> u16 {
     }
 }
 
-impl_parse_str_from_int_infallible!(Time, u16, from_512_second_intervals);
-
 impl fmt::Display for Time {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         fmt::Display::fmt(&self.0, f)
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 792906c5..e6097564 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -34,7 +34,7 @@
 use crate::hash_types::{Txid, Wtxid};
 use crate::VarInt;
 use crate::internal_macros::impl_consensus_encoding;
-use crate::parse::impl_parse_str_from_int_infallible;
+use crate::parse::impl_parse_str_through_int;
 use super::Weight;
 
 #[cfg(doc)]
@@ -500,7 +500,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
-impl_parse_str_from_int_infallible!(Sequence, u32, from_consensus);
+impl_parse_str_through_int!(Sequence);
 
 /// Bitcoin transaction output.
 ///
@@ -589,8 +589,8 @@ impl<E> EncodeSigningDataResult<E> {
     ///
     /// ```rust
     /// # use bitcoin::consensus::deserialize;
-    /// # use bitcoin::sighash::{LegacySighash, SighashCache};
     /// # use bitcoin::Transaction;
+    /// # use bitcoin::crypto::sighash::{LegacySighash, SighashCache};
     /// # use bitcoin_hashes::{Hash, hex::FromHex};
     /// # let mut writer = LegacySighash::engine();
     /// # let input_index = 0;
@@ -1278,12 +1278,11 @@ pub struct InputWeightPrediction {
 
 impl InputWeightPrediction {
     /// Computes the prediction for a single input.
-    pub fn new<T>(input_script_len: usize, witness_element_lengths: T) -> Self
-        where T :IntoIterator, T::Item:Borrow<usize>,
+    pub fn new<I>(input_script_len: usize, witness_element_lengths: I) -> Self
+        where I: IntoIterator<Item = usize>,
     {
         let (count, total_size) = witness_element_lengths.into_iter()
             .fold((0, 0), |(count, total_size), elem_len| {
-                let elem_len = *elem_len.borrow();
                 let elem_size = elem_len + VarInt(elem_len as u64).len();
                 (count + 1, total_size + elem_size)
             });
diff --git a/bitcoin/src/blockdata/weight.rs b/bitcoin/src/blockdata/weight.rs
index 7bccaf2c..ac529090 100644
--- a/bitcoin/src/blockdata/weight.rs
+++ b/bitcoin/src/blockdata/weight.rs
@@ -3,8 +3,6 @@
 use core::fmt;
 use core::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign};
 
-use crate::prelude::*;
-
 /// Represents block weight - the weight of a transaction or block.
 ///
 /// This is an integer newtype representing weigth in `wu`. It provides protection against mixing
@@ -197,4 +195,4 @@ fn sum<I>(iter: I) -> Self where I: Iterator<Item = &'a Weight> {
     }
 }
 
-crate::parse::impl_parse_str_from_int_infallible!(Weight, u64, from_wu);
+crate::parse::impl_parse_str_through_int!(Weight);
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index 89beca6d..11086f40 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -762,37 +762,37 @@ fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
 
 impl Encodable for sha256d::Hash {
     fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        self.as_byte_array().consensus_encode(w)
+        self.into_inner().consensus_encode(w)
     }
 }
 
 impl Decodable for sha256d::Hash {
     fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
-        Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
+        Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(r)?))
     }
 }
 
 impl Encodable for sha256::Hash {
     fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        self.as_byte_array().consensus_encode(w)
+        self.into_inner().consensus_encode(w)
     }
 }
 
 impl Decodable for sha256::Hash {
     fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
-        Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
+        Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(r)?))
     }
 }
 
 impl Encodable for TapLeafHash {
     fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        self.as_byte_array().consensus_encode(w)
+        self.into_inner().consensus_encode(w)
     }
 }
 
 impl Decodable for TapLeafHash {
     fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
-        Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
+        Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(r)?))
     }
 }
 
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index e1646864..83af9989 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -129,8 +129,8 @@ pub fn pubkey_hash(&self) -> PubkeyHash {
     /// Returns bitcoin 160-bit hash of the public key for witness program
     pub fn wpubkey_hash(&self) -> Option<WPubkeyHash> {
         if self.compressed {
-            Some(WPubkeyHash::from_byte_array(
-                hash160::Hash::hash(&self.inner.serialize()).to_byte_array()
+            Some(WPubkeyHash::from_inner(
+                hash160::Hash::hash(&self.inner.serialize()).into_inner()
             ))
         } else {
             // We can't create witness pubkey hashes for an uncompressed
@@ -578,7 +578,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 /// # Examples
 /// ```
 /// # #[cfg(feature = "rand-std")] {
-/// # use bitcoin::key::{KeyPair, TweakedKeyPair, TweakedPublicKey};
+/// # use bitcoin::crypto::key::{KeyPair, 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/crypto/mod.rs b/bitcoin/src/crypto/mod.rs
index a567fc68..4c4d5129 100644
--- a/bitcoin/src/crypto/mod.rs
+++ b/bitcoin/src/crypto/mod.rs
@@ -9,5 +9,4 @@
 pub mod ecdsa;
 pub mod key;
 pub mod sighash;
-// Contents re-exported in `bitcoin::taproot`.
-pub(crate) mod taproot;
+pub mod taproot;
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index 5390bc82..4b85e1b1 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -42,30 +42,27 @@
 macro_rules! impl_thirty_two_byte_hash {
     ($ty:ident) => {
         impl secp256k1::ThirtyTwoByteHash for $ty {
-            fn into_32(self) -> [u8; 32] { self.to_byte_array() }
+            fn into_32(self) -> [u8; 32] { self.into_inner() }
         }
     }
 }
 
-hash_newtype! {
-    /// Hash of a transaction according to the legacy signature algorithm.
-    #[hash_newtype(forward)]
-    pub struct LegacySighash(sha256d::Hash);
-
-    /// Hash of a transaction according to the segwit version 0 signature algorithm.
-    #[hash_newtype(forward)]
-    pub struct SegwitV0Sighash(sha256d::Hash);
-}
-
+#[rustfmt::skip]
+hash_newtype!(LegacySighash, sha256d::Hash, 32,
+    doc="Hash of a transaction according to the legacy signature algorithm", false);
 impl_thirty_two_byte_hash!(LegacySighash);
+
+#[rustfmt::skip]
+hash_newtype!(SegwitV0Sighash, sha256d::Hash, 32,
+    doc="Hash of a transaction according to the segwit version 0 signature algorithm", false);
 impl_thirty_two_byte_hash!(SegwitV0Sighash);
 
+#[rustfmt::skip]
 sha256t_hash_newtype!(TapSighash, TapSighashTag, MIDSTATE_TAPSIGHASH, 64,
     doc="Taproot-tagged hash with tag \"TapSighash\".
 
-This hash type is used for computing taproot signature hash.", forward
+This hash type is used for computing taproot signature hash.", false
 );
-
 impl_thirty_two_byte_hash!(TapSighash);
 
 /// Efficiently calculates signature hash message for legacy, segwit and taproot inputs.
@@ -679,7 +676,7 @@ pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
         //         ss += bytes([0])
         //         ss += struct.pack("<i", codeseparator_pos)
         if let Some((hash, code_separator_pos)) = leaf_hash_code_separator {
-            hash.as_byte_array().consensus_encode(&mut writer)?;
+            hash.into_inner().consensus_encode(&mut writer)?;
             KEY_VERSION_0.consensus_encode(&mut writer)?;
             code_separator_pos.consensus_encode(&mut writer)?;
         }
@@ -991,7 +988,7 @@ pub fn legacy_signature_hash(
             .legacy_encode_signing_data_to(&mut enc, input_index, script_pubkey, sighash_type)
             .is_sighash_single_bug()?
         {
-            Ok(LegacySighash::from_byte_array(UINT256_ONE))
+            Ok(LegacySighash::from_inner(UINT256_ONE))
         } else {
             Ok(LegacySighash::from_engine(enc))
         }
@@ -1202,7 +1199,7 @@ fn test_tap_sighash_hash() {
         let mut enc = TapSighash::engine();
         enc.input(&bytes);
         let hash = TapSighash::from_engine(enc);
-        assert_eq!(expected, hash.to_byte_array());
+        assert_eq!(expected, hash.into_inner());
     }
 
     #[test]
@@ -1436,7 +1433,7 @@ fn test_taproot_sighash(
             .taproot_signature_hash(input_index, &prevouts, annex, leaf_hash, sighash_type)
             .unwrap();
         let expected = Vec::from_hex(expected_hash).unwrap();
-        assert_eq!(expected, hash.to_byte_array());
+        assert_eq!(expected, hash.into_inner());
     }
 
     #[cfg(feature = "serde")]
@@ -1696,15 +1693,15 @@ fn bip143_p2wpkh() {
         let cache = cache.segwit_cache();
         // Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
         assert_eq!(
-            cache.prevouts.as_byte_array(),
+            cache.prevouts.into_inner(),
             &Vec::from_hex("96b827c8483d4e9b96712b6713a7b68d6e8003a781feba36c31143470b4efd37").unwrap()[..],
         );
         assert_eq!(
-            cache.sequences.as_byte_array(),
+            cache.sequences.into_inner(),
             &Vec::from_hex("52b0a642eea2fb7ae638c36f6252b6750293dbe574a806984b8e4d8548339a3b").unwrap()[..],
         );
         assert_eq!(
-            cache.outputs.as_byte_array(),
+            cache.outputs.into_inner(),
             &Vec::from_hex("863ef3e1a92afbfdb97f31ad0fc7683ee943e9abcf2501590ff8f6551f47e5e5").unwrap()[..],
         );
     }
@@ -1732,15 +1729,15 @@ fn bip143_p2wpkh_nested_in_p2sh() {
         let cache = cache.segwit_cache();
         // Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
         assert_eq!(
-            cache.prevouts.as_byte_array(),
+            cache.prevouts.into_inner(),
             &Vec::from_hex("b0287b4a252ac05af83d2dcef00ba313af78a3e9c329afa216eb3aa2a7b4613a").unwrap()[..],
         );
         assert_eq!(
-            cache.sequences.as_byte_array(),
+            cache.sequences.into_inner(),
             &Vec::from_hex("18606b350cd8bf565266bc352f0caddcf01e8fa789dd8a15386327cf8cabe198").unwrap()[..],
         );
         assert_eq!(
-            cache.outputs.as_byte_array(),
+            cache.outputs.into_inner(),
             &Vec::from_hex("de984f44532e2173ca0d64314fcefe6d30da6f8cf27bafa706da61df8a226c83").unwrap()[..],
         );
     }
@@ -1773,15 +1770,15 @@ fn bip143_p2wsh_nested_in_p2sh() {
         let cache = cache.segwit_cache();
         // Parse hex into Vec because BIP143 test vector displays forwards but our sha256d::Hash displays backwards.
         assert_eq!(
-            cache.prevouts.as_byte_array(),
+            cache.prevouts.into_inner(),
             &Vec::from_hex("74afdc312af5183c4198a40ca3c1a275b485496dd3929bca388c4b5e31f7aaa0").unwrap()[..],
         );
         assert_eq!(
-            cache.sequences.as_byte_array(),
+            cache.sequences.into_inner(),
             &Vec::from_hex("3bb13029ce7b1f559ef5e747fcac439f1455a2ec7c5f09b72290795e70665044").unwrap()[..],
         );
         assert_eq!(
-            cache.outputs.as_byte_array(),
+            cache.outputs.into_inner(),
             &Vec::from_hex("bc4d309071414bed932f98832b27b4d76dad7e6c1346f487a8fdbb8eb90307cc").unwrap()[..],
         );
     }
diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs
index 29bc29de..31b7cb84 100644
--- a/bitcoin/src/hash_types.rs
+++ b/bitcoin/src/hash_types.rs
@@ -21,7 +21,7 @@ fn consensus_encode<W: $crate::io::Write + ?Sized>(&self, w: &mut W) -> Result<u
         impl $crate::consensus::Decodable for $hashtype {
             fn consensus_decode<R: $crate::io::Read + ?Sized>(r: &mut R) -> Result<Self, $crate::consensus::encode::Error> {
                 use $crate::hashes::Hash;
-                Ok(Self::from_byte_array(<<$hashtype as $crate::hashes::Hash>::Bytes>::consensus_decode(r)?))
+                Ok(Self::from_inner(<<$hashtype as $crate::hashes::Hash>::Inner>::consensus_decode(r)?))
             }
         }
     };
@@ -34,14 +34,14 @@ macro_rules! impl_asref_push_bytes {
             impl AsRef<$crate::blockdata::script::PushBytes> for $hashtype {
                 fn as_ref(&self) -> &$crate::blockdata::script::PushBytes {
                     use $crate::hashes::Hash;
-                    self.as_byte_array().into()
+                    self.as_inner().as_ref()
                 }
             }
 
             impl From<$hashtype> for $crate::blockdata::script::PushBytesBuf {
                 fn from(hash: $hashtype) -> Self {
                     use $crate::hashes::Hash;
-                    hash.as_byte_array().into()
+                    hash.as_inner().into()
                 }
             }
         )*
@@ -55,44 +55,30 @@ fn from(hash: $hashtype) -> Self {
 mod newtypes {
     use crate::hashes::{sha256, sha256d, hash160, hash_newtype};
 
-    hash_newtype! {
-        /// A bitcoin transaction hash/transaction ID.
-        ///
-        /// For compatibility with the existing Bitcoin infrastructure and historical
-        /// and current versions of the Bitcoin Core software itself, this and
-        /// other [`sha256d::Hash`] types, are serialized in reverse
-        /// byte order when converted to a hex string via [`std::fmt::Display`] trait operations.
-        /// See [`hashes::Hash::DISPLAY_BACKWARD`] for more details.
-        pub struct Txid(sha256d::Hash); 
-
-        /// A bitcoin witness transaction ID.
-        pub struct Wtxid(sha256d::Hash);
-        /// A bitcoin block hash.
-        pub struct BlockHash(sha256d::Hash);
-
-        /// A hash of a public key.
-        pub struct PubkeyHash(hash160::Hash);
-        /// A hash of Bitcoin Script bytecode.
-        pub struct ScriptHash(hash160::Hash);
-        /// SegWit version of a public key hash.
-        pub struct WPubkeyHash(hash160::Hash);
-        /// SegWit version of a Bitcoin Script bytecode hash.
-        pub struct WScriptHash(sha256::Hash);
-
-        /// A hash of the Merkle tree branch or root for transactions
-        pub struct TxMerkleNode(sha256d::Hash);
-        /// A hash corresponding to the Merkle tree root for witness data
-        pub struct WitnessMerkleNode(sha256d::Hash);
-        /// A hash corresponding to the witness structure commitment in the coinbase transaction
-        pub struct WitnessCommitment(sha256d::Hash);
-        /// XpubIdentifier as defined in BIP-32.
-        pub struct XpubIdentifier(hash160::Hash);
-
-        /// Filter hash, as defined in BIP-157
-        pub struct FilterHash(sha256d::Hash);
-        /// Filter header, as defined in BIP-157
-        pub struct FilterHeader(sha256d::Hash);
-    }
+    hash_newtype!(
+        Txid, sha256d::Hash, 32, doc="A bitcoin transaction hash/transaction ID.
+
+For compatibility with the existing Bitcoin infrastructure and historical
+and current versions of the Bitcoin Core software itself, this and
+other [`sha256d::Hash`] types, are serialized in reverse
+byte order when converted to a hex string via [`std::fmt::Display`] trait operations.
+See [`hashes::Hash::DISPLAY_BACKWARD`] for more details.
+");
+    hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID.");
+    hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash.");
+
+    hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key.");
+    hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode.");
+    hash_newtype!(WPubkeyHash, hash160::Hash, 20, doc="SegWit version of a public key hash.");
+    hash_newtype!(WScriptHash, sha256::Hash, 32, doc="SegWit version of a Bitcoin Script bytecode hash.");
+
+    hash_newtype!(TxMerkleNode, sha256d::Hash, 32, doc="A hash of the Merkle tree branch or root for transactions");
+    hash_newtype!(WitnessMerkleNode, sha256d::Hash, 32, doc="A hash corresponding to the Merkle tree root for witness data");
+    hash_newtype!(WitnessCommitment, sha256d::Hash, 32, doc="A hash corresponding to the witness structure commitment in the coinbase transaction");
+    hash_newtype!(XpubIdentifier, hash160::Hash, 20, doc="XpubIdentifier as defined in BIP-32.");
+
+    hash_newtype!(FilterHash, sha256d::Hash, 32, doc="Filter hash, as defined in BIP-157");
+    hash_newtype!(FilterHeader, sha256d::Hash, 32, doc="Filter header, as defined in BIP-157");
 
     impl_hashencode!(Txid);
     impl_hashencode!(Wtxid);
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 75a3b69b..309ae9e8 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -92,8 +92,7 @@
 pub mod bip32;
 pub mod blockdata;
 pub mod consensus;
-// Private until we either make this a crate or flatten it - still to be decided.
-pub(crate) mod crypto;
+pub mod crypto;
 pub mod error;
 pub mod hash_types;
 pub mod merkle_tree;
@@ -128,7 +127,7 @@
 pub use crate::blockdata::{constants, opcodes};
 pub use crate::consensus::encode::VarInt;
 pub use crate::crypto::key::{self, PrivateKey, PublicKey};
-pub use crate::crypto::{ecdsa, sighash};
+pub use crate::crypto::sighash;
 pub use crate::error::Error;
 pub use crate::hash_types::{Txid, Wtxid, BlockHash, PubkeyHash,  ScriptHash, WPubkeyHash, WScriptHash};
 pub use crate::merkle_tree::MerkleBlock;
diff --git a/bitcoin/src/merkle_tree/block.rs b/bitcoin/src/merkle_tree/block.rs
index a094da97..e3eeeefb 100644
--- a/bitcoin/src/merkle_tree/block.rs
+++ b/bitcoin/src/merkle_tree/block.rs
@@ -305,7 +305,7 @@ pub fn extract_matches(
         if hash_used != self.hashes.len() as u32 {
             return Err(NotAllHashesConsumed);
         }
-        Ok(TxMerkleNode::from_byte_array(hash_merkle_root.to_byte_array()))
+        Ok(TxMerkleNode::from_inner(hash_merkle_root.into_inner()))
     }
 
     /// Helper function to efficiently calculate the number of nodes at given height
@@ -319,7 +319,7 @@ fn calc_tree_width(&self, height: u32) -> u32 {
     fn calc_hash(&self, height: u32, pos: u32, txids: &[Txid]) -> TxMerkleNode {
         if height == 0 {
             // Hash at height 0 is the txid itself
-            TxMerkleNode::from_byte_array(txids[pos as usize].to_byte_array())
+            TxMerkleNode::from_inner(txids[pos as usize].into_inner())
         } else {
             // Calculate left hash
             let left = self.calc_hash(height - 1, pos * 2, txids);
@@ -384,7 +384,7 @@ fn traverse_and_extract(
             *hash_used += 1;
             if height == 0 && parent_of_match {
                 // in case of height 0, we have a matched txid
-                matches.push(Txid::from_byte_array(hash.to_byte_array()));
+                matches.push(Txid::from_inner(hash.into_inner()));
                 indexes.push(pos);
             }
             Ok(hash)
@@ -573,7 +573,7 @@ fn pmt_test(tx_count: usize) {
             .collect::<Vec<_>>();
 
         // Calculate the merkle root and height
-        let hashes = tx_ids.iter().map(|t| t.to_raw_hash());
+        let hashes = tx_ids.iter().map(|t| t.as_hash());
         let merkle_root_1: TxMerkleNode =
             merkle_tree::calculate_root(hashes).expect("hashes is not empty").into();
         let mut height = 1;
@@ -741,7 +741,7 @@ fn damage(&mut self, rng: &mut ThreadRng) {
             let n = rng.gen_range(0..self.hashes.len());
             let bit = rng.gen::<u8>();
             let hashes = &mut self.hashes;
-            let mut hash = hashes[n].to_byte_array();
+            let mut hash = hashes[n].into_inner();
             hash[(bit >> 3) as usize] ^= 1 << (bit & 7);
             hashes[n] = TxMerkleNode::from_slice(&hash).unwrap();
         }
diff --git a/bitcoin/src/merkle_tree/mod.rs b/bitcoin/src/merkle_tree/mod.rs
index 678feb1e..b7b515f2 100644
--- a/bitcoin/src/merkle_tree/mod.rs
+++ b/bitcoin/src/merkle_tree/mod.rs
@@ -122,7 +122,7 @@ fn both_merkle_root_functions_return_the_same_result() {
         let block: Block = deserialize(&segwit_block[..]).expect("Failed to deserialize block");
         assert!(block.check_merkle_root()); // Sanity check.
 
-        let hashes_iter = block.txdata.iter().map(|obj| obj.txid().to_raw_hash());
+        let hashes_iter = block.txdata.iter().map(|obj| obj.txid().as_hash());
 
         let mut hashes_array: [sha256d::Hash; 15] = [Hash::all_zeros(); 15];
         for (i, hash) in hashes_iter.clone().enumerate() {
diff --git a/bitcoin/src/network/constants.rs b/bitcoin/src/network/constants.rs
index e3bf8a65..7172207d 100644
--- a/bitcoin/src/network/constants.rs
+++ b/bitcoin/src/network/constants.rs
@@ -30,14 +30,12 @@
 use core::convert::TryFrom;
 use core::str::FromStr;
 use core::{fmt, ops};
-use core::fmt::Display;
 
 use bitcoin_internals::{debug_from_display, write_err};
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Serialize};
 
 use crate::consensus::encode::{self, Decodable, Encodable};
-use crate::constants::ChainHash;
 use crate::error::impl_std_error;
 use crate::hashes::hex::{Error, FromHex};
 use crate::io;
@@ -143,34 +141,6 @@ pub fn from_core_arg(core_arg: &str) -> Result<Self, ParseNetworkError> {
         };
         Ok(network)
     }
-
-    /// Return the network's chain hash (genesis block hash).
-    ///
-    /// # Examples
-    ///
-    /// ```rust
-    /// use bitcoin::network::constants::Network;
-    /// use bitcoin::blockdata::constants::ChainHash;
-    ///
-    /// let network = Network::Bitcoin;
-    /// assert_eq!(network.chain_hash(), ChainHash::BITCOIN);
-    /// ```
-    pub fn chain_hash(self) -> ChainHash {
-        ChainHash::using_genesis_block(self)
-    }
-
-    /// Creates a `Network` from the chain hash (genesis block hash).
-    ///
-    /// # Examples
-    ///
-    /// ```rust
-    /// use bitcoin::network::constants::Network;
-    /// use bitcoin::blockdata::constants::ChainHash;
-    /// use std::convert::TryFrom;
-    ///
-    /// assert_eq!(Ok(Network::Bitcoin), Network::try_from(ChainHash::BITCOIN));
-    /// ```
-    pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> { Network::try_from(chain_hash).ok() }
 }
 
 /// An error in parsing network string.
@@ -216,33 +186,6 @@ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
     }
 }
 
-/// Error in parsing network from chain hash.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct UnknownChainHash(ChainHash);
-
-impl Display for UnknownChainHash {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "unknown chain hash: {}", self.0)
-    }
-}
-
-impl_std_error!(UnknownChainHash);
-
-impl TryFrom<ChainHash> for Network {
-    type Error = UnknownChainHash;
-
-    fn try_from(chain_hash: ChainHash) -> Result<Self, Self::Error> {
-        match chain_hash {
-            // Note: any new network entries must be matched against here.
-            ChainHash::BITCOIN => Ok(Network::Bitcoin),
-            ChainHash::TESTNET => Ok(Network::Testnet),
-            ChainHash::SIGNET => Ok(Network::Signet),
-            ChainHash::REGTEST => Ok(Network::Regtest),
-            _ => Err(UnknownChainHash(chain_hash)),
-        }
-    }
-}
-
 /// Network magic bytes to identify the cryptocurrency network the message was intended for.
 #[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
 pub struct Magic([u8; 4]);
@@ -296,7 +239,7 @@ fn from(network: Network) -> Magic {
     }
 }
 
-/// Error in creating a Network from Magic bytes.
+/// Error in parsing magic from string.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct UnknownMagic(Magic);
 
diff --git a/bitcoin/src/network/message.rs b/bitcoin/src/network/message.rs
index 8e20de6a..311a0b37 100644
--- a/bitcoin/src/network/message.rs
+++ b/bitcoin/src/network/message.rs
@@ -582,7 +582,7 @@ fn full_round_ser_der_raw_network_message_test() {
                 flags: BloomFlags::All,
             }),
             NetworkMessage::FilterAdd(FilterAdd { data: script.as_bytes().to_vec() }),
-            NetworkMessage::FilterAdd(FilterAdd { data: hash([29u8; 32]).as_byte_array().to_vec() }),
+            NetworkMessage::FilterAdd(FilterAdd { data: hash([29u8; 32]).into_inner().to_vec() }),
             NetworkMessage::FilterClear,
             NetworkMessage::GetCFilters(GetCFilters {
                 filter_type: 2,
diff --git a/bitcoin/src/network/mod.rs b/bitcoin/src/network/mod.rs
index a12b9995..0450156f 100644
--- a/bitcoin/src/network/mod.rs
+++ b/bitcoin/src/network/mod.rs
@@ -33,5 +33,3 @@
 #[cfg(feature = "std")]
 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
 pub mod message_network;
-
-pub use self::constants::Magic;
diff --git a/bitcoin/src/parse.rs b/bitcoin/src/parse.rs
index 5c997562..07ddae82 100644
--- a/bitcoin/src/parse.rs
+++ b/bitcoin/src/parse.rs
@@ -95,78 +95,37 @@ pub(crate) fn hex_u32<S: AsRef<str> + Into<String>>(s: S) -> Result<u32, ParseIn
 
 impl_std_error!(ParseIntError, source);
 
-/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using infallible
-/// conversion function `fn`.
-macro_rules! impl_tryfrom_str_from_int_infallible {
-    ($($from:ty, $to:ident, $inner:ident, $fn:ident);*) => {
+/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using `fn`
+macro_rules! impl_tryfrom_str_through_int_single {
+    ($($from:ty, $to:ident $(, $fn:ident)?);*) => {
         $(
         impl core::convert::TryFrom<$from> for $to {
             type Error = $crate::error::ParseIntError;
 
             fn try_from(s: $from) -> Result<Self, Self::Error> {
-                $crate::parse::int::<$inner, $from>(s).map($to::$fn)
+                $crate::parse::int(s).map($to $(:: $fn)?)
             }
         }
         )*
     }
 }
-pub(crate) use impl_tryfrom_str_from_int_infallible;
+pub(crate) use impl_tryfrom_str_through_int_single;
 
-/// Implements `FromStr` and `TryFrom<{&str, String, Box<str>}> for $to` using `parse::int`, mapping
-/// the output using infallible conversion function `fn`.
+/// Implements `FromStr` and `TryFrom<{&str, String, Box<str>}> for $to` using `parse::int`, mapping the output using `fn`
 ///
 /// The `Error` type is `ParseIntError`
-macro_rules! impl_parse_str_from_int_infallible {
-    ($to:ident, $inner:ident, $fn:ident) => {
-        $crate::parse::impl_tryfrom_str_from_int_infallible!(&str, $to, $inner, $fn; String, $to, $inner, $fn; Box<str>, $to, $inner, $fn);
+macro_rules! impl_parse_str_through_int {
+    ($to:ident $(, $fn:ident)?) => {
+        $crate::parse::impl_tryfrom_str_through_int_single!(&str, $to $(, $fn)?; alloc::string::String, $to $(, $fn)?; alloc::boxed::Box<str>, $to $(, $fn)?);
 
         impl core::str::FromStr for $to {
             type Err = $crate::error::ParseIntError;
 
             fn from_str(s: &str) -> Result<Self, Self::Err> {
-                $crate::parse::int::<$inner, &str>(s).map($to::$fn)
+                $crate::parse::int(s).map($to $(:: $fn)?)
             }
         }
 
     }
 }
-pub(crate) use impl_parse_str_from_int_infallible;
-
-/// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using fallible
-/// conversion function `fn`.
-macro_rules! impl_tryfrom_str_from_int_fallible {
-    ($($from:ty, $to:ident, $inner:ident, $fn:ident, $err:ident);*) => {
-        $(
-        impl core::convert::TryFrom<$from> for $to {
-            type Error = $err;
-
-            fn try_from(s: $from) -> Result<Self, Self::Error> {
-                let u = $crate::parse::int::<$inner, $from>(s)?;
-                $to::$fn(u)
-            }
-        }
-        )*
-    }
-}
-pub(crate) use impl_tryfrom_str_from_int_fallible;
-
-/// Implements `FromStr` and `TryFrom<{&str, String, Box<str>}> for $to` using `parse::int`, mapping
-/// the output using fallible conversion function `fn`.
-///
-/// The `Error` type is `ParseIntError`
-macro_rules! impl_parse_str_from_int_fallible {
-    ($to:ident, $inner:ident, $fn:ident, $err:ident) => {
-        $crate::parse::impl_tryfrom_str_from_int_fallible!(&str, $to, $inner, $fn, $err; String, $to, $inner, $fn, $err; Box<str>, $to, $inner, $fn, $err);
-
-        impl core::str::FromStr for $to {
-            type Err = $err;
-
-            fn from_str(s: &str) -> Result<Self, Self::Err> {
-                let u = $crate::parse::int::<$inner, &str>(s)?;
-                $to::$fn(u)
-            }
-        }
-
-    }
-}
-pub(crate) use impl_parse_str_from_int_fallible;
+pub(crate) use impl_parse_str_through_int;
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index bdfc2e80..471f247e 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -196,7 +196,7 @@ pub fn to_compact_lossy(self) -> CompactTarget {
     #[cfg_attr(all(test, mutate), mutate)]
     pub fn is_met_by(&self, hash: BlockHash) -> bool {
         use crate::hashes::Hash;
-        let hash = U256::from_le_bytes(hash.to_byte_array());
+        let hash = U256::from_le_bytes(hash.into_inner());
         hash <= self.0
     }
 
@@ -1492,7 +1492,7 @@ fn target_is_met_by_for_target_equals_hash() {
         use crate::hashes::Hash;
 
         let hash = BlockHash::from_str("ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c").expect("failed to parse block hash");
-        let target = Target(U256::from_le_bytes(hash.to_byte_array()));
+        let target = Target(U256::from_le_bytes(hash.into_inner()));
         assert!(target.is_met_by(hash));
     }
 
diff --git a/bitcoin/src/psbt/macros.rs b/bitcoin/src/psbt/macros.rs
index 77da1679..cdb4e86b 100644
--- a/bitcoin/src/psbt/macros.rs
+++ b/bitcoin/src/psbt/macros.rs
@@ -169,7 +169,7 @@ macro_rules! impl_psbt_hash_serialize {
     ($hash_type:ty) => {
         impl $crate::psbt::serialize::Serialize for $hash_type {
             fn serialize(&self) -> $crate::prelude::Vec<u8> {
-                self.as_byte_array().to_vec()
+                self.into_inner().to_vec()
             }
         }
     };
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index 0c1b568c..493e2c70 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -309,9 +309,9 @@ fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
 impl Serialize for (XOnlyPublicKey, TapLeafHash) {
     fn serialize(&self) -> Vec<u8> {
         let ser_pk = self.0.serialize();
-        let mut buf = Vec::with_capacity(ser_pk.len() + self.1.as_byte_array().len());
+        let mut buf = Vec::with_capacity(ser_pk.len() + self.1.as_inner().len());
         buf.extend(&ser_pk);
-        buf.extend(self.1.as_byte_array());
+        buf.extend(self.1.as_inner());
         buf
     }
 }
diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs
index e16a41b2..06bfe9e0 100644
--- a/bitcoin/src/taproot.rs
+++ b/bitcoin/src/taproot.rs
@@ -13,13 +13,12 @@
 use secp256k1::{self, Scalar, Secp256k1};
 
 use crate::consensus::Encodable;
-use crate::hashes::{sha256t_hash_newtype, Hash, HashEngine};
 use crate::crypto::key::{TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey};
+use crate::hashes::{sha256t_hash_newtype, Hash, HashEngine};
 use crate::prelude::*;
 use crate::{io, Script, ScriptBuf};
 
-// Re-export these so downstream only has to use one `taproot` module.
-pub use crate::crypto::taproot::{Signature, Error};
+pub use crate::crypto::sighash::{TapSighash, TapSighashTag};
 
 /// The SHA-256 midstate value for the TapLeaf hash.
 const MIDSTATE_TAPLEAF: [u8; 32] = [
@@ -47,16 +46,16 @@
 sha256t_hash_newtype!(TapLeafHash, TapLeafTag, MIDSTATE_TAPLEAF, 64,
     doc="Taproot-tagged hash with tag \"TapLeaf\".
 
-This is used for computing tapscript script spend hash.", forward
+This is used for computing tapscript script spend hash.", false
 );
 #[rustfmt::skip]
 sha256t_hash_newtype!(TapNodeHash, TapBranchTag, MIDSTATE_TAPBRANCH, 64,
-    doc="Tagged hash used in taproot trees; see BIP-340 for tagging rules", forward
+    doc="Tagged hash used in taproot trees; see BIP-340 for tagging rules", false
 );
 #[rustfmt::skip]
 sha256t_hash_newtype!(TapTweakHash, TapTweakTag, MIDSTATE_TAPTWEAK, 64,
     doc="Taproot-tagged hash with tag \"TapTweak\".
-    This hash type is used while computing the tweaked public key", forward
+    This hash type is used while computing the tweaked public key", false
 );
 
 impl TapTweakHash {
@@ -80,7 +79,7 @@ pub fn from_key_and_tweak(
     /// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API.
     pub fn to_scalar(self) -> Scalar {
         // This is statistically extremely unlikely to panic.
-        Scalar::from_be_bytes(self.to_byte_array()).expect("hash value greater than curve order")
+        Scalar::from_be_bytes(self.into_inner()).expect("hash value greater than curve order")
     }
 }
 
@@ -123,7 +122,7 @@ pub fn from_script(script: &Script, ver: LeafVersion) -> TapNodeHash {
 }
 
 impl From<TapLeafHash> for TapNodeHash {
-    fn from(leaf: TapLeafHash) -> TapNodeHash { TapNodeHash::from_byte_array(leaf.to_byte_array()) }
+    fn from(leaf: TapLeafHash) -> TapNodeHash { TapNodeHash::from_inner(leaf.into_inner()) }
 }
 
 /// Maximum depth of a taproot tree script spend path.
@@ -691,7 +690,7 @@ pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
 
     /// Serializes `self` as bytes.
     pub fn serialize(&self) -> Vec<u8> {
-        self.0.iter().flat_map(|e| e.as_byte_array()).copied().collect::<Vec<u8>>()
+        self.0.iter().flat_map(|e| e.as_inner()).copied().collect::<Vec<u8>>()
     }
 
     /// Appends elements to proof.
@@ -1134,7 +1133,6 @@ mod test {
     use crate::hashes::hex::FromHex;
     use crate::hashes::sha256t::Tag;
     use crate::hashes::{sha256, Hash, HashEngine};
-    use crate::sighash::{TapSighash, TapSighashTag};
     use crate::{Address, Network};
     extern crate serde_json;
 
@@ -1154,10 +1152,10 @@ fn test_midstates() {
         use crate::crypto::sighash::MIDSTATE_TAPSIGHASH;
 
         // check midstate against hard-coded values
-        assert_eq!(MIDSTATE_TAPLEAF, tag_engine("TapLeaf").midstate().to_byte_array());
-        assert_eq!(MIDSTATE_TAPBRANCH, tag_engine("TapBranch").midstate().to_byte_array());
-        assert_eq!(MIDSTATE_TAPTWEAK, tag_engine("TapTweak").midstate().to_byte_array());
-        assert_eq!(MIDSTATE_TAPSIGHASH, tag_engine("TapSighash").midstate().to_byte_array());
+        assert_eq!(MIDSTATE_TAPLEAF, tag_engine("TapLeaf").midstate().into_inner());
+        assert_eq!(MIDSTATE_TAPBRANCH, tag_engine("TapBranch").midstate().into_inner());
+        assert_eq!(MIDSTATE_TAPTWEAK, tag_engine("TapTweak").midstate().into_inner());
+        assert_eq!(MIDSTATE_TAPSIGHASH, tag_engine("TapSighash").midstate().into_inner());
 
         // test that engine creation roundtrips
         assert_eq!(tag_engine("TapLeaf").midstate(), TapLeafTag::engine().midstate());
@@ -1169,12 +1167,12 @@ fn test_midstates() {
         fn empty_hash(tag_name: &str) -> [u8; 32] {
             let mut e = tag_engine(tag_name);
             e.input(&[]);
-            TapNodeHash::from_engine(e).to_byte_array()
+            TapNodeHash::from_engine(e).into_inner()
         }
-        assert_eq!(empty_hash("TapLeaf"), TapLeafHash::hash(&[]).to_byte_array());
-        assert_eq!(empty_hash("TapBranch"), TapNodeHash::hash(&[]).to_byte_array());
-        assert_eq!(empty_hash("TapTweak"), TapTweakHash::hash(&[]).to_byte_array());
-        assert_eq!(empty_hash("TapSighash"), TapSighash::hash(&[]).to_byte_array());
+        assert_eq!(empty_hash("TapLeaf"), TapLeafHash::hash(&[]).into_inner());
+        assert_eq!(empty_hash("TapBranch"), TapNodeHash::hash(&[]).into_inner());
+        assert_eq!(empty_hash("TapTweak"), TapTweakHash::hash(&[]).into_inner());
+        assert_eq!(empty_hash("TapSighash"), TapSighash::hash(&[]).into_inner());
     }
 
     #[test]
diff --git a/hashes/embedded/src/main.rs b/hashes/embedded/src/main.rs
index 98bb4bb3..22773ad2 100644
--- a/hashes/embedded/src/main.rs
+++ b/hashes/embedded/src/main.rs
@@ -18,9 +18,7 @@
 use cortex_m_semihosting::{debug, hprintln};
 use panic_halt as _;
 
-hash_newtype! {
-    struct TestType(sha256::Hash);
-}
+hash_newtype!(TestType, sha256::Hash, 32, doc = "test");
 
 // this is the allocator the application will use
 #[cfg(feature = "alloc")]
diff --git a/hashes/src/hash160.rs b/hashes/src/hash160.rs
index 51462441..8c6a114a 100644
--- a/hashes/src/hash160.rs
+++ b/hashes/src/hash160.rs
@@ -97,7 +97,7 @@ struct Test {
             }
             let manual_hash = Hash::from_engine(engine);
             assert_eq!(hash, manual_hash);
-            assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
diff --git a/hashes/src/hmac.rs b/hashes/src/hmac.rs
index 22afd7d1..3cd10728 100644
--- a/hashes/src/hmac.rs
+++ b/hashes/src/hmac.rs
@@ -192,7 +192,7 @@ fn borrow(&self) -> &[u8] {
 
 impl<T: Hash> Hash for Hmac<T> {
     type Engine = HmacEngine<T>;
-    type Bytes = T::Bytes;
+    type Inner = T::Inner;
 
     fn from_engine(mut e: HmacEngine<T>) -> Hmac<T> {
         let ihash = T::from_engine(e.iengine);
@@ -207,16 +207,16 @@ fn from_slice(sl: &[u8]) -> Result<Hmac<T>, Error> {
         T::from_slice(sl).map(Hmac)
     }
 
-    fn to_byte_array(self) -> Self::Bytes {
-        self.0.to_byte_array()
+    fn into_inner(self) -> Self::Inner {
+        self.0.into_inner()
     }
 
-    fn as_byte_array(&self) -> &Self::Bytes {
-        self.0.as_byte_array()
+    fn as_inner(&self) -> &Self::Inner {
+        self.0.as_inner()
     }
 
-    fn from_byte_array(bytes: T::Bytes) -> Self {
-        Hmac(T::from_byte_array(bytes))
+    fn from_inner(inner: T::Inner) -> Self {
+        Hmac(T::from_inner(inner))
     }
 
     fn all_zeros() -> Self {
@@ -237,8 +237,8 @@ fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
 impl<'de, T: Hash + Deserialize<'de>> Deserialize<'de> for Hmac<T> {
     fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Hmac<T>, D::Error> {
-        let bytes = Deserialize::deserialize(d)?;
-        Ok(Hmac(bytes))
+        let inner = Deserialize::deserialize(d)?;
+        Ok(Hmac(inner))
     }
 }
 
@@ -365,7 +365,7 @@ struct Test {
             engine.input(&test.input);
             let hash = Hmac::<sha256::Hash>::from_engine(engine);
             assert_eq!(&hash[..], &test.output[..]);
-            assert_eq!(hash.as_byte_array(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs
index be993d23..8c5a2f0a 100644
--- a/hashes/src/internal_macros.rs
+++ b/hashes/src/internal_macros.rs
@@ -97,7 +97,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
                 } else {
                     FromHex::from_byte_iter(HexIterator::new(s)?)?
                 };
-                Ok(Self::from_byte_array(inner))
+                Ok(Self::from_inner(inner))
             }
         }
 
@@ -122,7 +122,7 @@ fn index(&self, index: I) -> &Self::Output {
 
         impl<$($gen: $gent),*> crate::Hash for Hash<$($gen),*> {
             type Engine = HashEngine;
-            type Bytes = [u8; $bits / 8];
+            type Inner = [u8; $bits / 8];
 
             const LEN: usize = $bits / 8;
             const DISPLAY_BACKWARD: bool = $reverse;
@@ -145,16 +145,16 @@ fn from_slice(sl: &[u8]) -> Result<Hash<$($gen),*>, Error> {
                 }
             }
 
-            fn to_byte_array(self) -> Self::Bytes {
+            fn into_inner(self) -> Self::Inner {
                 self.0
             }
 
-            fn as_byte_array(&self) -> &Self::Bytes {
+            fn as_inner(&self) -> &Self::Inner {
                 &self.0
             }
 
-            fn from_byte_array(bytes: Self::Bytes) -> Self {
-                Self::internal_new(bytes)
+            fn from_inner(inner: Self::Inner) -> Self {
+                Self::internal_new(inner)
             }
 
             fn all_zeros() -> Self {
diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs
index 5c218087..66b3a75d 100644
--- a/hashes/src/lib.rs
+++ b/hashes/src/lib.rs
@@ -171,7 +171,7 @@ pub trait Hash: Copy + Clone + PartialEq + Eq + PartialOrd + Ord +
     type Engine: HashEngine;
 
     /// The byte array that represents the hash internally.
-    type Bytes: hex::FromHex + Copy;
+    type Inner: hex::FromHex;
 
     /// Constructs a new engine.
     fn engine() -> Self::Engine {
@@ -199,14 +199,14 @@ fn hash(data: &[u8]) -> Self {
     /// true for `Sha256dHash`, so here we are.
     const DISPLAY_BACKWARD: bool = false;
 
-    /// Returns the underlying byte array.
-    fn to_byte_array(self) -> Self::Bytes;
+    /// Unwraps the hash and returns the underlying byte array.
+    fn into_inner(self) -> Self::Inner;
 
-    /// Returns a reference to the underlying byte array.
-    fn as_byte_array(&self) -> &Self::Bytes;
+    /// Unwraps the hash and returns a reference to the underlying byte array.
+    fn as_inner(&self) -> &Self::Inner;
 
     /// Constructs a hash from the underlying byte array.
-    fn from_byte_array(bytes: Self::Bytes) -> Self;
+    fn from_inner(inner: Self::Inner) -> Self;
 
     /// Returns an all zero hash.
     ///
@@ -220,22 +220,17 @@ fn hash(data: &[u8]) -> Self {
 mod tests {
     use crate::{Hash, sha256d};
 
-    hash_newtype! {
-        /// A test newtype
-        struct TestNewtype(sha256d::Hash);
-
-        /// A test newtype
-        struct TestNewtype2(sha256d::Hash);
-    }
+    hash_newtype!(TestNewtype, sha256d::Hash, 32, doc="A test newtype");
+    hash_newtype!(TestNewtype2, sha256d::Hash, 32, doc="A test newtype");
 
     #[test]
     fn convert_newtypes() {
         let h1 = TestNewtype::hash(&[]);
-        let h2: TestNewtype2 = h1.to_raw_hash().into();
+        let h2: TestNewtype2 = h1.as_hash().into();
         assert_eq!(&h1[..], &h2[..]);
 
         let h = sha256d::Hash::hash(&[]);
         let h2: TestNewtype = h.to_string().parse().unwrap();
-        assert_eq!(h2.to_raw_hash(), h);
+        assert_eq!(h2.as_hash(), h);
     }
 }
diff --git a/hashes/src/ripemd160.rs b/hashes/src/ripemd160.rs
index 591706b8..e27f87af 100644
--- a/hashes/src/ripemd160.rs
+++ b/hashes/src/ripemd160.rs
@@ -480,7 +480,7 @@ struct Test {
             }
             let manual_hash = ripemd160::Hash::from_engine(engine);
             assert_eq!(hash, manual_hash);
-            assert_eq!(hash.as_byte_array(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
diff --git a/hashes/src/sha1.rs b/hashes/src/sha1.rs
index 7fae399e..144cd3ac 100644
--- a/hashes/src/sha1.rs
+++ b/hashes/src/sha1.rs
@@ -206,7 +206,7 @@ struct Test {
             }
             let manual_hash = sha1::Hash::from_engine(engine);
             assert_eq!(hash, manual_hash);
-            assert_eq!(hash.as_byte_array(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
diff --git a/hashes/src/sha256.rs b/hashes/src/sha256.rs
index 5155615b..357cec8f 100644
--- a/hashes/src/sha256.rs
+++ b/hashes/src/sha256.rs
@@ -46,12 +46,12 @@ fn from_engine(mut e: HashEngine) -> Hash {
     e.input(&(8 * data_len).to_be_bytes());
     debug_assert_eq!(e.length % BLOCK_SIZE, 0);
 
-    Hash(e.midstate().to_byte_array())
+    Hash(e.midstate().into_inner())
 }
 
 #[cfg(fuzzing)]
 fn from_engine(e: HashEngine) -> Hash {
-    let mut hash = e.midstate().to_byte_array();
+    let mut hash = e.midstate().into_inner();
     if hash == [0; 32] {
         // Assume sha256 is secure and never generate 0-hashes (which represent invalid
         // secp256k1 secret keys, causing downstream application breakage).
@@ -111,7 +111,7 @@ fn n_bytes_hashed(&self) -> usize {
 impl Hash {
     /// Iterate the sha256 algorithm to turn a sha256 hash into a sha256d hash
     pub fn hash_again(&self) -> sha256d::Hash {
-        crate::Hash::from_byte_array(<Self as crate::Hash>::hash(&self.0).0)
+        crate::Hash::from_inner(<Self as crate::Hash>::hash(&self.0).0)
     }
 }
 
@@ -149,7 +149,7 @@ impl Midstate {
     const DISPLAY_BACKWARD: bool = true;
 
     /// Construct a new [`Midstate`] from the inner value.
-    pub fn from_byte_array(inner: [u8; 32]) -> Self {
+    pub fn from_inner(inner: [u8; 32]) -> Self {
         Midstate(inner)
     }
 
@@ -165,7 +165,7 @@ pub fn from_slice(sl: &[u8]) -> Result<Midstate, Error> {
     }
 
     /// Unwraps the [`Midstate`] and returns the underlying byte array.
-    pub fn to_byte_array(self) -> [u8; 32] {
+    pub fn into_inner(self) -> [u8; 32] {
         self.0
     }
 }
@@ -176,7 +176,7 @@ fn from_byte_iter<I>(iter: I) -> Result<Self, hex::Error>
         I: Iterator<Item = Result<u8, hex::Error>> + ExactSizeIterator + DoubleEndedIterator,
     {
         // DISPLAY_BACKWARD is true
-        Ok(Midstate::from_byte_array(hex::FromHex::from_byte_iter(iter.rev())?))
+        Ok(Midstate::from_inner(hex::FromHex::from_byte_iter(iter.rev())?))
     }
 }
 
@@ -381,7 +381,7 @@ struct Test {
             }
             let manual_hash = sha256::Hash::from_engine(engine);
             assert_eq!(hash, manual_hash);
-            assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
@@ -402,7 +402,7 @@ fn midstate() {
         assert_eq!(
             engine.midstate(),
             // RPC output
-            sha256::Midstate::from_byte_array([
+            sha256::Midstate::from_inner([
                 0x0b, 0xcf, 0xe0, 0xe5, 0x4e, 0x6c, 0xc7, 0xd3,
                 0x4f, 0x4f, 0x7c, 0x1d, 0xf0, 0xb0, 0xf5, 0x03,
                 0xf2, 0xf7, 0x12, 0x91, 0x2a, 0x06, 0x05, 0xb4,
@@ -455,7 +455,7 @@ fn engine_with_state() {
             0xd5, 0x69, 0x09, 0x59,
         ];
         let midstate_engine =
-            sha256::HashEngine::from_midstate(sha256::Midstate::from_byte_array(MIDSTATE), 64);
+            sha256::HashEngine::from_midstate(sha256::Midstate::from_inner(MIDSTATE), 64);
         let hash = sha256::Hash::from_engine(midstate_engine);
         assert_eq!(hash, sha256::Hash(HASH_EXPECTED));
     }
diff --git a/hashes/src/sha256d.rs b/hashes/src/sha256d.rs
index ec227a9f..bbf9f77a 100644
--- a/hashes/src/sha256d.rs
+++ b/hashes/src/sha256d.rs
@@ -89,7 +89,7 @@ struct Test {
             let sha2d_hash = sha2_hash.hash_again();
             assert_eq!(hash, sha2d_hash);
 
-            assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs
index c7f4d3bf..e52e0ae0 100644
--- a/hashes/src/sha256t.rs
+++ b/hashes/src/sha256t.rs
@@ -88,7 +88,7 @@ fn hash<H: core::hash::Hasher>(&self, h: &mut H) {
 fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
     use crate::Hash as _;
 
-    Hash::from_byte_array(sha256::Hash::from_engine(e).to_byte_array())
+    Hash::from_inner(sha256::Hash::from_engine(e).into_inner())
 }
 
 /// Macro used to define a newtype tagged hash.
@@ -97,11 +97,11 @@ fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
 /// - a sha256t::Hash type alias.
 #[macro_export]
 macro_rules! sha256t_hash_newtype {
-    ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $direction:tt) => {
-        sha256t_hash_newtype!($newtype, $tag, $midstate, $midstate_len, $docs, $direction, stringify!($newtype));
+    ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $reverse: expr) => {
+        sha256t_hash_newtype!($newtype, $tag, $midstate, $midstate_len, $docs, $reverse, stringify!($newtype));
     };
 
-    ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $direction:tt, $sname:expr) => {
+    ($newtype:ident, $tag:ident, $midstate:ident, $midstate_len:expr, $docs:meta, $reverse: expr, $sname:expr) => {
         #[doc = "The tag used for ["]
         #[doc = $sname]
         #[doc = "]"]
@@ -110,16 +110,12 @@ macro_rules! sha256t_hash_newtype {
 
         impl $crate::sha256t::Tag for $tag {
             fn engine() -> $crate::sha256::HashEngine {
-                let midstate = $crate::sha256::Midstate::from_byte_array($midstate);
+                let midstate = $crate::sha256::Midstate::from_inner($midstate);
                 $crate::sha256::HashEngine::from_midstate(midstate, $midstate_len)
             }
         }
 
-        $crate::hash_newtype! {
-            #[$docs]
-            #[hash_newtype($direction)]
-            pub struct $newtype($crate::sha256t::Hash<$tag>);
-        }
+        $crate::hash_newtype!($newtype, $crate::sha256t::Hash<$tag>, 32, $docs, $reverse);
     };
 }
 
@@ -141,7 +137,7 @@ mod tests {
     impl sha256t::Tag for TestHashTag {
         fn engine() -> sha256::HashEngine {
             // The TapRoot TapLeaf midstate.
-            let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE);
+            let midstate = sha256::Midstate::from_inner(TEST_MIDSTATE);
             sha256::HashEngine::from_midstate(midstate, 64)
         }
     }
@@ -150,7 +146,7 @@ fn engine() -> sha256::HashEngine {
     #[cfg(feature = "alloc")]
     pub type TestHash = sha256t::Hash<TestHashTag>;
 
-    sha256t_hash_newtype!(NewTypeHash, NewTypeTag, TEST_MIDSTATE, 64, doc="test hash", backward);
+    sha256t_hash_newtype!(NewTypeHash, NewTypeTag, TEST_MIDSTATE, 64, doc="test hash", true);
 
     #[test]
     #[cfg(feature = "alloc")]
diff --git a/hashes/src/sha512.rs b/hashes/src/sha512.rs
index 9feaa3a5..e6618f36 100644
--- a/hashes/src/sha512.rs
+++ b/hashes/src/sha512.rs
@@ -379,7 +379,7 @@ struct Test {
             }
             let manual_hash = sha512::Hash::from_engine(engine);
             assert_eq!(hash, manual_hash);
-            assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 
diff --git a/hashes/src/sha512_256.rs b/hashes/src/sha512_256.rs
index ad146146..02b9fa94 100644
--- a/hashes/src/sha512_256.rs
+++ b/hashes/src/sha512_256.rs
@@ -165,7 +165,7 @@ struct Test {
             }
             let manual_hash = sha512_256::Hash::from_engine(engine);
             assert_eq!(hash, manual_hash);
-            assert_eq!(hash.to_byte_array()[..].as_ref(), test.output.as_slice());
+            assert_eq!(hash.into_inner()[..].as_ref(), test.output.as_slice());
         }
     }
 }
diff --git a/hashes/src/util.rs b/hashes/src/util.rs
index c00f89be..ca007300 100644
--- a/hashes/src/util.rs
+++ b/hashes/src/util.rs
@@ -110,120 +110,32 @@ fn input(&mut self, inp: &[u8]) {
 
 
 /// Creates a new newtype around a [`Hash`] type.
-///
-/// The syntax is similar to the usual tuple struct syntax:
-///
-/// ```
-/// # use bitcoin_hashes::{hash_newtype, sha256};
-/// hash_newtype! {
-///     /// Hash of `Foo`.
-///     pub struct MyNewtype(pub sha256::Hash);
-/// }
-/// ```
-///
-/// You can use any valid visibility specifier in place of `pub` or you can omit either or both, if
-/// you want the type or its field to be private.
-///
-/// Whether the hash is reversed or not when displaying depends on the inner type. However you can
-/// override it like this:
-///
-/// ```
-/// # use bitcoin_hashes::{hash_newtype, sha256};
-/// hash_newtype! {
-///     #[hash_newtype(backward)]
-///     struct MyNewtype(sha256::Hash);
-/// }
-/// ```
-///
-/// This will display the hash backwards regardless of what the inner type does. Use `forward`
-/// instead of `backward` to force displaying forward.
-///
-/// You can add arbitrary doc comments or other attributes to the struct or it's field. Note that
-/// the macro already derives [`Copy`], [`Clone`], [`Eq`], [`PartialEq`],
-/// [`Hash`](core::hash::Hash), [`Ord`], [`PartialOrd`]. With the `serde` feature on, this also adds
-/// [`Serialize`](serde::Serialize) and [`Deserialize](serde::Deserialize) implementations.
-///
-/// You can also define multiple newtypes within one macro call:
-///
-/// ```
-/// # use bitcoin_hashes::{hash_newtype, sha256, hash160};
-///
-/// hash_newtype! {
-///     /// My custom type 1
-///     pub struct Newtype1(sha256::Hash);
-///
-///     /// My custom type 2
-///     struct Newtype2(hash160::Hash);
-/// }
-/// ```
-///
-/// Note: the macro is internally recursive. If you use too many attributes (> 256 tokens) you may
-/// hit recursion limit. If you have so many attributes for a good reason, just raising the limit
-/// should be OK. Note however that attribute-processing part has to use [TT muncher] which has
-/// quadratic complexity, so having many attributes may blow up compile time. This should be rare.
-///
-/// [TT muncher]: https://danielkeep.github.io/tlborm/book/pat-incremental-tt-munchers.html
-///
-// Ever heard of legendary comments warning developers to not touch the code? Yep, here's another
-// one. The following code is written the way it is for some specific reasons. If you think you can
-// simplify it, I suggest spending your time elsewhere.
-//
-// If you looks at the code carefully you might ask these questions:
-//
-// * Why are attributes using `tt` and not `meta`?!
-// * Why are the macros split like that?!
-// * Why use recursion instead of `$()*`?
-//
-// None of these are here by accident. For some reason unknown to me, if you accept an argument to
-// macro with any fragment specifier other than `tt` it will **not** match any of the rules
-// requiring a specific token. Yep, I tried it, I literally got error that `hash_newtype` doesn't
-// match `hash_newtype`. So all input attributes must be `tt`.
-//
-// Originally I wanted to define a bunch of macros that would filter-out hash_type attributes. Then
-// I remembered (by seeing compiler error) that calling macros is not allowed inside attributes.
-// And no, you can't bypass it by calling a helper macro and passing "output of another macro" into
-// it. The whole macro gets passed, not the resulting value. So we have to generate the entire
-// attributes. And you can't just place an attribute-producing macro above struct - they are
-// considered separate items. This is not C.
-//
-// Thus struct is generated in a separate macro together with attributes. And since the macro needs
-// attributes as the input and I didn't want to create confusion by using `#[]` syntax *after*
-// struct, I opted to use `{}` as a separator. Yes, a separator is required because an attribute
-// may be composed of multiple token trees - that's the point of "double repetition".
 #[macro_export]
 macro_rules! hash_newtype {
-    ($($(#[$($type_attrs:tt)*])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:tt])* $field_vis:vis $hash:path);)+) => {
-        $(
-        $($crate::hash_newtype_known_attrs!(#[ $($type_attrs)* ]);)*
-
-        $crate::hash_newtype_struct! {
-            $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash);
-
-            $({ $($type_attrs)* })*
-        }
+    ($newtype:ident, $hash:ty, $len:expr, $docs:meta) => {
+        $crate::hash_newtype!($newtype, $hash, $len, $docs, <$hash as $crate::Hash>::DISPLAY_BACKWARD);
+    };
+    ($newtype:ident, $hash:ty, $len:expr, $docs:meta, $reverse:expr) => {
+        #[$docs]
+        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+        #[repr(transparent)]
+        pub struct $newtype($hash);
 
-        $crate::hex_fmt_impl!(<$newtype as $crate::Hash>::DISPLAY_BACKWARD, $newtype);
-        $crate::serde_impl!($newtype, <$newtype as $crate::Hash>::LEN);
+        $crate::hex_fmt_impl!($reverse, $newtype);
+        $crate::serde_impl!($newtype, $len);
         $crate::borrow_slice_impl!($newtype);
 
         impl $newtype {
-            /// Creates this wrapper type from the inner hash type.
-            #[allow(unused)] // the user of macro may not need this
-            pub fn from_raw_hash(inner: $hash) -> $newtype {
+            /// Creates this type from the inner hash type.
+            pub fn from_hash(inner: $hash) -> $newtype {
                 $newtype(inner)
             }
 
-            /// Returns the inner hash (sha256, sh256d etc.).
-            #[allow(unused)] // the user of macro may not need this
-            pub fn to_raw_hash(self) -> $hash {
+            /// Converts this type into the inner hash type.
+            pub fn as_hash(&self) -> $hash {
+                // Hashes implement Copy so don't need into_hash.
                 self.0
             }
-
-            /// Returns a reference to the inner hash (sha256, sh256d etc.).
-            #[allow(unused)] // the user of macro may not need this
-            pub fn as_raw_hash(&self) -> &$hash {
-                &self.0
-            }
         }
 
         impl $crate::_export::_core::convert::From<$hash> for $newtype {
@@ -241,10 +153,10 @@ fn from(hashtype: $newtype) -> $hash {
 
         impl $crate::Hash for $newtype {
             type Engine = <$hash as $crate::Hash>::Engine;
-            type Bytes = <$hash as $crate::Hash>::Bytes;
+            type Inner = <$hash as $crate::Hash>::Inner;
 
             const LEN: usize = <$hash as $crate::Hash>::LEN;
-            const DISPLAY_BACKWARD: bool = $crate::hash_newtype_get_direction!($hash, $(#[$($type_attrs)*])*);
+            const DISPLAY_BACKWARD: bool = $reverse;
 
             fn engine() -> Self::Engine {
                 <$hash as $crate::Hash>::engine()
@@ -260,18 +172,18 @@ fn from_slice(sl: &[u8]) -> Result<$newtype, $crate::Error> {
             }
 
             #[inline]
-            fn from_byte_array(bytes: Self::Bytes) -> Self {
-                $newtype(<$hash as $crate::Hash>::from_byte_array(bytes))
+            fn from_inner(inner: Self::Inner) -> Self {
+                $newtype(<$hash as $crate::Hash>::from_inner(inner))
             }
 
             #[inline]
-            fn to_byte_array(self) -> Self::Bytes {
-                self.0.to_byte_array()
+            fn into_inner(self) -> Self::Inner {
+                self.0.into_inner()
             }
 
             #[inline]
-            fn as_byte_array(&self) -> &Self::Bytes {
-                self.0.as_byte_array()
+            fn as_inner(&self) -> &Self::Inner {
+                self.0.as_inner()
             }
 
             #[inline]
@@ -287,18 +199,18 @@ fn from_str(s: &str) -> $crate::_export::_core::result::Result<$newtype, Self::E
                 use $crate::hex::{HexIterator, FromHex};
                 use $crate::Hash;
 
-                let inner: <$hash as Hash>::Bytes = if <Self as $crate::Hash>::DISPLAY_BACKWARD {
+                let inner: <$hash as Hash>::Inner = if $reverse {
                     FromHex::from_byte_iter(HexIterator::new(s)?.rev())?
                 } else {
                     FromHex::from_byte_iter(HexIterator::new(s)?)?
                 };
-                Ok($newtype(<$hash>::from_byte_array(inner)))
+                Ok($newtype(<$hash>::from_inner(inner)))
             }
         }
 
-        impl $crate::_export::_core::convert::AsRef<[u8; <$hash as $crate::Hash>::LEN]> for $newtype {
-            fn as_ref(&self) -> &[u8; <$hash as $crate::Hash>::LEN] {
-                AsRef::<[u8; <$hash as $crate::Hash>::LEN]>::as_ref(&self.0)
+        impl $crate::_export::_core::convert::AsRef<[u8; $len]> for $newtype {
+            fn as_ref(&self) -> &[u8; $len] {
+                AsRef::<[u8; $len]>::as_ref(&self.0)
             }
         }
 
@@ -310,96 +222,9 @@ fn index(&self, index: I) -> &Self::Output {
                 &self.0[index]
             }
         }
-        )+
-    };
-}
-
-// Generates the struct only (no impls)
-//
-// This is a separate macro to make it more readable and have a separate interface that allows for
-// two groups of type attributes: processed and not-yet-processed ones (think about it like
-// computation via recursion). The macro recursively matches unprocessed attributes, popping them
-// one at a time and either ignoring them (`hash_newtype`) or appending them to the list of
-// processed attributes to be added to the struct.
-//
-// Once the list of not-yet-processed attributes is empty the struct is generated with processed
-// attributes added.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! hash_newtype_struct {
-    ($(#[$other_attrs:meta])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:meta])* $field_vis:vis $hash:path);) => {
-        $(#[$other_attrs])*
-        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-        $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash);
-    };
-    ($(#[$other_attrs:meta])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:meta])* $field_vis:vis $hash:path); { hash_newtype($($ignore:tt)*) } $($type_attrs:tt)*) => {
-        $crate::hash_newtype_struct! {
-            $(#[$other_attrs])*
-            $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash);
-
-            $($type_attrs)*
-        }
-    };
-    ($(#[$other_attrs:meta])* $type_vis:vis struct $newtype:ident($(#[$field_attrs:meta])* $field_vis:vis $hash:path); { $other_attr:meta } $($type_attrs:tt)*) => {
-        $crate::hash_newtype_struct! {
-            $(#[$other_attrs])*
-            #[$other_attr]
-            $type_vis struct $newtype($(#[$field_attrs])* $field_vis $hash);
-
-            $($type_attrs)*
-        }
     };
 }
 
-// Extracts `hash_newtype(forward)` and `hash_newtype(backward)` attributes if any and turns them
-// into bool, defaulting to `DISPLAY_BACKWARD` of the wrapped type if the attribute is omitted.
-//
-// Once an appropriate attribute is found we pass the remaining ones into another macro to detect
-// duplicates/conflicts and report an error.
-//
-// FYI, no, we can't use a helper macro to first filter all `hash_newtype` attributes. We would be
-// attempting to match on macros instead. So we must write `hashe_newtype` in each branch.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! hash_newtype_get_direction {
-    ($hash:ty, ) => { <$hash as $crate::Hash>::DISPLAY_BACKWARD };
-    ($hash:ty, #[hash_newtype(forward)] $($others:tt)*) => { { $crate::hash_newtype_forbid_direction!(forward, $($others)*); false } };
-    ($hash:ty, #[hash_newtype(backward)] $($others:tt)*) => { { $crate::hash_newtype_forbid_direction!(backward, $($others)*); true } };
-    ($hash:ty, #[$($ignore:tt)*]  $($others:tt)*) => { $crate::hash_newtype_get_direction!($hash, $($others)*) };
-}
-
-// Reports an error if any of the attributes is `hash_newtype($direction)`.
-//
-// This is used for detection of duplicates/conflicts, see the macro above.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! hash_newtype_forbid_direction {
-    ($direction:ident, ) => {};
-    ($direction:ident, #[hash_newtype(forward)] $(others:tt)*) => {
-        compile_error!(concat!("Cannot set display direction to forward: ", stringify!($direction), " was already specified"));
-    };
-    ($direction:ident, #[hash_newtype(backward)] $(others:tt)*) => {
-        compile_error!(concat!("Cannot set display direction to backward: ", stringify!($direction), " was already specified"));
-    };
-    ($direction:ident, #[$($ignore:tt)*] $(#[$others:tt])*) => {
-        $crate::hash_newtype_forbid_direction!($direction, $(#[$others])*)
-    };
-}
-
-// Checks (at compile time) that all `hash_newtype` attributes are known.
-//
-// An unknown attribute could be a typo that could cause problems - e.g. wrong display direction if
-// it's missing. To prevent this, we call this macro above. The macro produces nothing unless an
-// unknown attribute is found in which case it produces `compile_error!`.
-#[doc(hidden)]
-#[macro_export]
-macro_rules! hash_newtype_known_attrs {
-    (#[hash_newtype(forward)]) => {};
-    (#[hash_newtype(backward)]) => {};
-    (#[hash_newtype($($unknown:tt)*)]) => { compile_error!(concat!("Unrecognized attribute ", stringify!($($unknown)*))); };
-    ($($ignore:tt)*) => {};
-}
-
 #[cfg(feature = "schemars")]
 #[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
 pub mod json_hex_string {
@@ -432,14 +257,14 @@ mod test {
     fn hash_as_ref_array() {
         let hash = sha256::Hash::hash(&[3, 50]);
         let r = AsRef::<[u8; 32]>::as_ref(&hash);
-        assert_eq!(r, hash.as_byte_array());
+        assert_eq!(r, hash.as_inner());
     }
 
     #[test]
     fn hash_as_ref_slice() {
         let hash = sha256::Hash::hash(&[3, 50]);
         let r = AsRef::<[u8]>::as_ref(&hash);
-        assert_eq!(r, hash.as_byte_array());
+        assert_eq!(r, hash.as_inner());
     }
 
     #[test]
@@ -448,13 +273,10 @@ fn hash_borrow() {
 
         let hash = sha256::Hash::hash(&[3, 50]);
         let borrowed: &[u8] = hash.borrow();
-        assert_eq!(borrowed, hash.as_byte_array());
+        assert_eq!(borrowed, hash.as_inner());
     }
 
-    hash_newtype! {
-        /// Test hash.
-        struct TestHash(crate::sha256d::Hash);
-    }
+    hash_newtype!(TestHash, crate::sha256d::Hash, 32, doc="Test hash.");
 
     #[test]
     fn display() {
@@ -488,13 +310,13 @@ fn lower_hex_alternate() {
     fn inner_hash_as_ref_array() {
         let hash = TestHash::all_zeros();
         let r = AsRef::<[u8; 32]>::as_ref(&hash);
-        assert_eq!(r, hash.as_byte_array());
+        assert_eq!(r, hash.as_inner());
     }
 
     #[test]
     fn inner_hash_as_ref_slice() {
         let hash = TestHash::all_zeros();
         let r = AsRef::<[u8]>::as_ref(&hash);
-        assert_eq!(r, hash.as_byte_array());
+        assert_eq!(r, hash.as_inner());
     }
 }


Test patch:
diff --git a/bitcoin/contrib/test.sh b/bitcoin/contrib/test.sh
index 8dffd2bb..4657f0aa 100755
--- a/bitcoin/contrib/test.sh
+++ b/bitcoin/contrib/test.sh
@@ -18,12 +18,6 @@ if cargo --version | grep nightly; then
     NIGHTLY=true
 fi
 
-# Pin dependencies as required if we are using MSRV toolchain.
-if cargo --version | grep "1\.41"; then
-    # 1.0.108 uses `matches!` macro so does not work with Rust 1.41.1, bad `syn` no biscuit.
-    cargo update -p syn --precise 1.0.107
-fi
-
 # We should not have any duplicate dependencies. This catches mistakes made upgrading dependencies
 # in one crate and not in another (e.g. upgrade bitcoin_hashes in bitcoin but not in secp).
 duplicate_dependencies=$(cargo tree  --target=all --all-features --duplicates | wc -l)
diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs
index 9c5636d3..254a2016 100644
--- a/bitcoin/tests/serde.rs
+++ b/bitcoin/tests/serde.rs
@@ -30,15 +30,16 @@
 use bitcoin::blockdata::locktime::{absolute, relative};
 use bitcoin::blockdata::witness::Witness;
 use bitcoin::consensus::encode::deserialize;
+use bitcoin::crypto::key::UntweakedPublicKey;
+use bitcoin::crypto::{ecdsa, taproot};
 use bitcoin::hashes::hex::FromHex;
 use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
-use bitcoin::key::UntweakedPublicKey;
 use bitcoin::psbt::raw::{self, Key, Pair, ProprietaryKey};
 use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
 use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
-use bitcoin::taproot::{self, ControlBlock, LeafVersion, TaprootBuilder, TaprootSpendInfo};
+use bitcoin::taproot::{ControlBlock, LeafVersion, TaprootBuilder, TaprootSpendInfo};
 use bitcoin::{
-    ecdsa, Address, Block, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence, Target,
+    Address, Block, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence, Target,
     Transaction, TxIn, TxOut, Txid, Work,
 };
 use secp256k1::Secp256k1;
diff --git a/hashes/extended_tests/schemars/src/main.rs b/hashes/extended_tests/schemars/src/main.rs
index 7ceee050..3bfadcec 100644
--- a/hashes/extended_tests/schemars/src/main.rs
+++ b/hashes/extended_tests/schemars/src/main.rs
@@ -125,7 +125,7 @@ fn test_hash() {
         impl sha256t::Tag for TestHashTag {
             fn engine() -> sha256::HashEngine {
                 // The TapRoot TapLeaf midstate.
-                let midstate = sha256::Midstate::from_byte_array(TEST_MIDSTATE);
+                let midstate = sha256::Midstate::from_inner(TEST_MIDSTATE);
                 sha256::HashEngine::from_midstate(midstate, 64)
             }
         }
@@ -139,7 +139,7 @@ fn engine() -> sha256::HashEngine {
             TEST_MIDSTATE,
             64,
             doc = "test hash",
-            backward
+            true
         );
         static HASH_BYTES: [u8; 32] = [
             0xef, 0x53, 0x7f, 0x25, 0xc8, 0x95, 0xbf, 0xa7, 0x82, 0x52, 0x65, 0x29, 0xa9, 0xb6,