Repository: rust-bitcoin/rust-bitcoin
Pull request #3674 (2024-11-28 03:21:24)

Close `amounts` error types
Close the two pubic enum error types in `units::amounts`. All the other structs are closed already because they either have private fields or marked `non_exhaustive`.



Patch:
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index ee854c75..c4ba294c 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -71,10 +71,6 @@ required-features = ["std", "bitcoinconsensus"]
 name = "ecdsa-psbt-simple"
 required-features = ["rand-std"]
 
-[[example]]
-name = "create-p2wpkh-address"
-required-features = ["rand-std"]
-
 [[example]]
 name = "sign-tx-segwit-v0"
 required-features = ["rand-std"]
diff --git a/bitcoin/examples/bip32.rs b/bitcoin/examples/bip32.rs
index eec8ecca..f2f36d40 100644
--- a/bitcoin/examples/bip32.rs
+++ b/bitcoin/examples/bip32.rs
@@ -1,3 +1,5 @@
+extern crate bitcoin;
+
 use std::{env, process};
 
 use bitcoin::address::{Address, KnownHrp};
diff --git a/bitcoin/examples/create-p2wpkh-address.rs b/bitcoin/examples/create-p2wpkh-address.rs
deleted file mode 100644
index b8ec758f..00000000
--- a/bitcoin/examples/create-p2wpkh-address.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-use bitcoin::secp256k1::{rand, Secp256k1};
-use bitcoin::{Address, CompressedPublicKey, Network, PrivateKey};
-
-/// Generate a P2WPKH (pay-to-witness-public-key-hash) address and print it
-/// along with the associated private key needed to transact.
-fn main() {
-    // Create new secp256k1 instance.
-    let secp = Secp256k1::new();
-
-    // Generate secp256k1 public and private key pair.
-    let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
-
-    // Create a Bitcoin private key to be used on the Bitcoin mainnet.
-    let private_key = PrivateKey::new(secret_key, Network::Bitcoin);
-
-    // Create a compressed Bitcoin public key from the secp256k1 public key.
-    let public_key = CompressedPublicKey(public_key);
-
-    // Create a Bitcoin P2WPKH address.
-    let address = Address::p2wpkh(public_key, Network::Bitcoin);
-
-    println!("Private Key: {}", private_key);
-    println!("Address: {}", address);
-}
diff --git a/bitcoin/examples/handshake.rs b/bitcoin/examples/handshake.rs
index db5a4fb1..8cfa6a9e 100644
--- a/bitcoin/examples/handshake.rs
+++ b/bitcoin/examples/handshake.rs
@@ -1,3 +1,5 @@
+extern crate bitcoin;
+
 use std::io::{BufReader, Write};
 use std::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, TcpStream};
 use std::time::{SystemTime, UNIX_EPOCH};
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 6a91bcc5..0ed381ef 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -920,14 +920,14 @@ pub const fn predict_weight_from_slices(
     inputs: &[InputWeightPrediction],
     output_script_lens: &[usize],
 ) -> Weight {
-    let mut input_weight = 0;
+    let mut partial_input_weight = 0;
     let mut inputs_with_witnesses = 0;
 
     // for loops not supported in const fn
     let mut i = 0;
     while i < inputs.len() {
         let prediction = inputs[i];
-        input_weight += prediction.total_weight().to_wu() as usize;
+        partial_input_weight += prediction.witness_weight().to_wu() as usize;
         inputs_with_witnesses += (prediction.witness_size > 0) as usize;
         i += 1;
     }
@@ -943,7 +943,7 @@ pub const fn predict_weight_from_slices(
 
     predict_weight_internal(
         inputs.len(),
-        input_weight,
+        partial_input_weight,
         inputs_with_witnesses,
         output_script_lens.len(),
         output_scripts_size,
@@ -1133,7 +1133,9 @@ pub const fn from_slice(input_script_len: usize, witness_element_lengths: &[usiz
     /// Computes the **signature weight** added to a transaction by an input with this weight prediction,
     /// not counting the prevout (txid, index), sequence, potential witness flag bytes or the witness count varint.
     #[deprecated(since = "TBD", note = "use `InputWeightPrediction::witness_weight()` instead")]
-    pub const fn weight(&self) -> Weight { Self::witness_weight(self) }
+    pub const fn weight(&self) -> Weight {
+        Self::witness_weight(self)
+    }
 
     /// Computes the signature, prevout (txid, index), and sequence weights of this weight
     /// prediction.
@@ -1962,21 +1964,6 @@ fn weight_predictions() {
         );
     }
 
-    #[test]
-    fn weight_prediction_const_from_slices() {
-        let predict = [
-            InputWeightPrediction::P2WPKH_MAX,
-            InputWeightPrediction::NESTED_P2WPKH_MAX,
-            InputWeightPrediction::P2PKH_COMPRESSED_MAX,
-            InputWeightPrediction::P2PKH_UNCOMPRESSED_MAX,
-            InputWeightPrediction::P2TR_KEY_DEFAULT_SIGHASH,
-            InputWeightPrediction::P2TR_KEY_NON_DEFAULT_SIGHASH,
-        ];
-
-        let weight = predict_weight_from_slices(&predict, &[1]);
-        assert_eq!(weight, Weight::from_wu(2493));
-    }
-
     #[test]
     fn sequence_debug_output() {
         let seq = Sequence::from_seconds_floor(1000);
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index ccbac973..d5651de3 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -1862,7 +1862,6 @@ struct UtxoSpent {
             #[serde(rename = "scriptPubKey")]
             script_pubkey: ScriptBuf,
             #[serde(rename = "amountSats")]
-            #[serde(with = "crate::amount::serde::as_sat")]
             value: Amount,
         }
 
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index bb0e1f1c..9727d4db 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -237,8 +237,8 @@ pub mod parse {
     #[doc(inline)]
     pub use units::parse::{
         hex_check_unprefixed, hex_remove_prefix, hex_u128, hex_u128_unchecked, hex_u128_unprefixed,
-        hex_u32, hex_u32_unchecked, hex_u32_unprefixed, int, ParseIntError, PrefixedHexError,
-        UnprefixedHexError,
+        hex_u32, hex_u32_unchecked, hex_u32_unprefixed, int, ContainsPrefixError,
+        MissingPrefixError, ParseIntError, PrefixedHexError, UnprefixedHexError,
     };
 }
 
diff --git a/bitcoin/src/network/params.rs b/bitcoin/src/network/params.rs
index e322009b..eecc42cb 100644
--- a/bitcoin/src/network/params.rs
+++ b/bitcoin/src/network/params.rs
@@ -113,7 +113,7 @@ pub struct Params {
     /// Expected amount of time to mine one block.
     pub pow_target_spacing: u64,
     /// Difficulty recalculation interval.
-    pub pow_target_timespan: u32,
+    pub pow_target_timespan: u64,
     /// Determines whether minimal difficulty may be used for blocks or not.
     pub allow_min_difficulty_blocks: bool,
     /// Determines whether retargeting is disabled for this network or not.
@@ -261,7 +261,7 @@ pub const fn new(network: Network) -> Self {
 
     /// Calculates the number of blocks between difficulty adjustments.
     pub fn difficulty_adjustment_interval(&self) -> u64 {
-        u64::from(self.pow_target_timespan) / self.pow_target_spacing
+        self.pow_target_timespan / self.pow_target_spacing
     }
 }
 
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index b6d531cb..7566e73b 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -357,7 +357,7 @@ fn from_unprefixed_hex(s: &str) -> Result<CompactTarget, UnprefixedHexError> {
         /// ref: <https://github.com/bitcoin/bitcoin/blob/0503cbea9aab47ec0a87d34611e5453158727169/src/pow.cpp>
         ///
         /// Given the previous Target, represented as a [`CompactTarget`], the difficulty is adjusted
-        /// by taking the timespan between them, and multiplying the current [`CompactTarget`] by a factor
+        /// by taking the timespan between them, and multipling the current [`CompactTarget`] by a factor
         /// of the net timespan and expected timespan. The [`CompactTarget`] may not adjust by more than
         /// a factor of 4, or adjust beyond the maximum threshold for the network.
         ///
@@ -371,14 +371,12 @@ fn from_unprefixed_hex(s: &str) -> Result<CompactTarget, UnprefixedHexError> {
         /// Take the example of the first difficulty adjustment. Block 2016 introduces a new [`CompactTarget`],
         /// which takes the net timespan between Block 2015 and Block 0, and recomputes the difficulty.
         ///
-        /// To calculate the timespan, users should first convert their u32 timestamps to i64s before subtracting them
-        ///
         /// # Returns
         ///
         /// The expected [`CompactTarget`] recalculation.
         fn from_next_work_required(
             last: CompactTarget,
-            timespan: i64,
+            timespan: u64,
             params: impl AsRef<Params>,
         ) -> CompactTarget {
             let params = params.as_ref();
@@ -389,11 +387,11 @@ fn from_next_work_required(
             // ref: <https://github.com/bitcoin/bitcoin/blob/0503cbea9aab47ec0a87d34611e5453158727169/src/pow.cpp>
             let min_timespan = params.pow_target_timespan >> 2; // Lines 56/57
             let max_timespan = params.pow_target_timespan << 2; // Lines 58/59
-            let actual_timespan = timespan.clamp(min_timespan.into(), max_timespan.into());
+            let actual_timespan = timespan.clamp(min_timespan, max_timespan);
             let prev_target: Target = last.into();
             let maximum_retarget = prev_target.max_transition_threshold(params); // bnPowLimit
             let retarget = prev_target.0; // bnNew
-            let retarget = retarget.mul(u128::try_from(actual_timespan).expect("clamped value won't be negative").into());
+            let retarget = retarget.mul(actual_timespan.into());
             let retarget = retarget.div(params.pow_target_timespan.into());
             let retarget = Target(retarget);
             if retarget.ge(&maximum_retarget) {
@@ -1752,8 +1750,8 @@ fn compact_target_lower_hex_and_upper_hex() {
     fn compact_target_from_upwards_difficulty_adjustment() {
         let params = Params::new(crate::Network::Signet);
         let starting_bits = CompactTarget::from_consensus(503543726); // Genesis compact target on Signet
-        let start_time: i64 = 1598918400; // Genesis block unix time
-        let end_time: i64 = 1599332177; // Block 2015 unix time
+        let start_time: u64 = 1598918400; // Genesis block unix time
+        let end_time: u64 = 1599332177; // Block 2015 unix time
         let timespan = end_time - start_time; // Faster than expected
         let adjustment = CompactTarget::from_next_work_required(starting_bits, timespan, &params);
         let adjustment_bits = CompactTarget::from_consensus(503394215); // Block 2016 compact target
@@ -1764,8 +1762,8 @@ fn compact_target_from_upwards_difficulty_adjustment() {
     fn compact_target_from_downwards_difficulty_adjustment() {
         let params = Params::new(crate::Network::Signet);
         let starting_bits = CompactTarget::from_consensus(503394215); // Block 2016 compact target
-        let start_time: i64 = 1599332844; // Block 2016 unix time
-        let end_time: i64 = 1600591200; // Block 4031 unix time
+        let start_time: u64 = 1599332844; // Block 2016 unix time
+        let end_time: u64 = 1600591200; // Block 4031 unix time
         let timespan = end_time - start_time; // Slower than expected
         let adjustment = CompactTarget::from_next_work_required(starting_bits, timespan, &params);
         let adjustment_bits = CompactTarget::from_consensus(503397348); // Block 4032 compact target
@@ -1831,18 +1829,7 @@ fn compact_target_from_downwards_difficulty_adjustment_using_headers() {
     fn compact_target_from_maximum_upward_difficulty_adjustment() {
         let params = Params::new(crate::Network::Signet);
         let starting_bits = CompactTarget::from_consensus(503403001);
-        let timespan = params.pow_target_timespan / 5;
-        let got = CompactTarget::from_next_work_required(starting_bits, timespan.into(), params);
-        let want =
-            Target::from_compact(starting_bits).min_transition_threshold().to_compact_lossy();
-        assert_eq!(got, want);
-    }
-
-    #[test]
-    fn compact_target_from_maximum_upward_difficulty_adjustment_with_negative_timespan() {
-        let params = Params::new(crate::Network::Signet);
-        let starting_bits = CompactTarget::from_consensus(503403001);
-        let timespan: i64 = -i64::from(params.pow_target_timespan);
+        let timespan = (0.2 * params.pow_target_timespan as f64) as u64;
         let got = CompactTarget::from_next_work_required(starting_bits, timespan, params);
         let want =
             Target::from_compact(starting_bits).min_transition_threshold().to_compact_lossy();
@@ -1854,7 +1841,7 @@ fn compact_target_from_minimum_downward_difficulty_adjustment() {
         let params = Params::new(crate::Network::Signet);
         let starting_bits = CompactTarget::from_consensus(403403001); // High difficulty for Signet
         let timespan = 5 * params.pow_target_timespan; // Really slow.
-        let got = CompactTarget::from_next_work_required(starting_bits, timespan.into(), &params);
+        let got = CompactTarget::from_next_work_required(starting_bits, timespan, &params);
         let want =
             Target::from_compact(starting_bits).max_transition_threshold(params).to_compact_lossy();
         assert_eq!(got, want);
@@ -1865,7 +1852,7 @@ fn compact_target_from_adjustment_is_max_target() {
         let params = Params::new(crate::Network::Signet);
         let starting_bits = CompactTarget::from_consensus(503543726); // Genesis compact target on Signet
         let timespan = 5 * params.pow_target_timespan; // Really slow.
-        let got = CompactTarget::from_next_work_required(starting_bits, timespan.into(), &params);
+        let got = CompactTarget::from_next_work_required(starting_bits, timespan, &params);
         let want = params.max_attainable_target.to_compact_lossy();
         assert_eq!(got, want);
     }
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index e1e64dde..7bf5791f 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -256,7 +256,8 @@ pub fn combine(&mut self, other: Self) -> Result<(), Error> {
                     {
                         continue;
                     } else if derivation2.len() <= derivation1.len()
-                        && derivation2[..] == derivation1[derivation1.len() - derivation2.len()..]
+                        && derivation2[..]
+                                == derivation1[derivation1.len() - derivation2.len()..]
                     {
                         entry.insert((fingerprint1, derivation1));
                         continue;
@@ -2116,16 +2117,14 @@ fn combine_psbts_commutative() {
         assert_eq!(psbt1, psbt2);
     }
 
+
     // https://github.com/rust-bitcoin/rust-bitcoin/issues/3628
     #[test]
     fn test_combine_psbt_fuzz_3628() {
         let mut psbt1 = hex_psbt(include_str!("../../tests/data/psbt_fuzz1.hex")).unwrap();
         let psbt2 = hex_psbt(include_str!("../../tests/data/psbt_fuzz2.hex")).unwrap();
 
-        assert!(matches!(
-            psbt1.combine(psbt2).unwrap_err(),
-            Error::CombineInconsistentKeySources(_)
-        ));
+        assert!(matches!(psbt1.combine(psbt2).unwrap_err(), Error::CombineInconsistentKeySources(_)));
     }
 
     #[cfg(feature = "rand-std")]
diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs
index 44b13523..bc6206d3 100644
--- a/hashes/src/lib.rs
+++ b/hashes/src/lib.rs
@@ -5,7 +5,7 @@
 //! This library implements the hash functions needed by Bitcoin. As an ancillary thing, it exposes
 //! hexadecimal serialization and deserialization, since these are needed to display hashes.
 //!
-//! # Examples
+//! ## Commonly used operations
 //!
 //! Hashing a single byte slice or a string:
 //!
@@ -26,7 +26,7 @@
 //!
 //! let mut reader: &[u8] = b"hello"; // In real code, this could be a `File` or `TcpStream`.
 //! let mut engine = Sha256::engine();
-//! std::io::copy(&mut reader, &mut engine).expect("engine writes don't error");
+//! std::io::copy(&mut reader, &mut engine).unwrap();
 //! let _hash = Sha256::from_engine(engine);
 //! # }
 //! ```
diff --git a/nightly-version b/nightly-version
index fa09b099..c3f22bff 100644
--- a/nightly-version
+++ b/nightly-version
@@ -1,1 +1,1 @@
-nightly-2024-11-27
+nightly-2024-11-22
diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs
index 4a4c6f66..639f0c52 100644
--- a/primitives/src/transaction.rs
+++ b/primitives/src/transaction.rs
@@ -343,7 +343,6 @@ impl TxIn {
 #[cfg(feature = "alloc")]
 pub struct TxOut {
     /// The value of the output, in satoshis.
-    #[cfg_attr(feature = "serde", serde(with = "crate::amount::serde::as_sat"))]
     pub value: Amount,
     /// The script which must be satisfied for the output to be spent.
     pub script_pubkey: ScriptBuf,
diff --git a/units/src/amount/error.rs b/units/src/amount/error.rs
index 8c0e79fa..55939b5a 100644
--- a/units/src/amount/error.rs
+++ b/units/src/amount/error.rs
@@ -11,56 +11,56 @@
 
 /// An error during amount parsing amount with denomination.
 #[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum ParseError {
+pub struct ParseError(pub(crate) ParseErrorInner);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum ParseErrorInner {
     /// Invalid amount.
     Amount(ParseAmountError),
-
     /// Invalid denomination.
     Denomination(ParseDenominationError),
-
     /// The denomination was not identified.
     MissingDenomination(MissingDenominationError),
 }
 
 internals::impl_from_infallible!(ParseError);
+internals::impl_from_infallible!(ParseErrorInner);
 
 impl From<ParseAmountError> for ParseError {
-    fn from(e: ParseAmountError) -> Self { Self::Amount(e) }
+    fn from(e: ParseAmountError) -> Self { Self(ParseErrorInner::Amount(e)) }
 }
 
 impl From<ParseDenominationError> for ParseError {
-    fn from(e: ParseDenominationError) -> Self { Self::Denomination(e) }
+    fn from(e: ParseDenominationError) -> Self { Self(ParseErrorInner::Denomination(e)) }
 }
 
 impl From<OutOfRangeError> for ParseError {
-    fn from(e: OutOfRangeError) -> Self { Self::Amount(e.into()) }
+    fn from(e: OutOfRangeError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
 }
 
 impl From<TooPreciseError> for ParseError {
-    fn from(e: TooPreciseError) -> Self { Self::Amount(e.into()) }
+    fn from(e: TooPreciseError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
 }
 
 impl From<MissingDigitsError> for ParseError {
-    fn from(e: MissingDigitsError) -> Self { Self::Amount(e.into()) }
+    fn from(e: MissingDigitsError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
 }
 
 impl From<InputTooLargeError> for ParseError {
-    fn from(e: InputTooLargeError) -> Self { Self::Amount(e.into()) }
+    fn from(e: InputTooLargeError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
 }
 
 impl From<InvalidCharacterError> for ParseError {
-    fn from(e: InvalidCharacterError) -> Self { Self::Amount(e.into()) }
+    fn from(e: InvalidCharacterError) -> Self { Self(ParseErrorInner::Amount(e.into())) }
 }
 
 impl fmt::Display for ParseError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            ParseError::Amount(error) => write_err!(f, "invalid amount"; error),
-            ParseError::Denomination(error) => write_err!(f, "invalid denomination"; error),
-            // We consider this to not be a source because it currently doesn't contain useful
-            // information
-            ParseError::MissingDenomination(_) =>
+        match self.0 {
+            ParseErrorInner::Amount(ref e) => write_err!(f, "invalid amount"; e),
+            ParseErrorInner::Denomination(ref e) => write_err!(f, "invalid denomination"; e),
+            // We consider this to not be a source because it currently doesn't contain useful info.
+            ParseErrorInner::MissingDenomination(_) =>
                 f.write_str("the input doesn't contain a denomination"),
         }
     }
@@ -69,20 +69,21 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 #[cfg(feature = "std")]
 impl std::error::Error for ParseError {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            ParseError::Amount(error) => Some(error),
-            ParseError::Denomination(error) => Some(error),
-            // We consider this to not be a source because it currently doesn't contain useful
-            // information
-            ParseError::MissingDenomination(_) => None,
+        match self.0 {
+            ParseErrorInner::Amount(ref e) => Some(e),
+            ParseErrorInner::Denomination(ref e) => Some(e),
+            // We consider this to not be a source because it currently doesn't contain useful info.
+            ParseErrorInner::MissingDenomination(_) => None,
         }
     }
 }
 
 /// An error during amount parsing.
 #[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum ParseAmountError {
+pub struct ParseAmountError(pub(crate) ParseAmountErrorInner);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) enum ParseAmountErrorInner {
     /// The amount is too big or too small.
     OutOfRange(OutOfRangeError),
     /// Amount has higher precision than supported by the type.
@@ -96,28 +97,31 @@ pub enum ParseAmountError {
 }
 
 impl From<TooPreciseError> for ParseAmountError {
-    fn from(value: TooPreciseError) -> Self { Self::TooPrecise(value) }
+    fn from(value: TooPreciseError) -> Self { Self(ParseAmountErrorInner::TooPrecise(value)) }
 }
 
 impl From<MissingDigitsError> for ParseAmountError {
-    fn from(value: MissingDigitsError) -> Self { Self::MissingDigits(value) }
+    fn from(value: MissingDigitsError) -> Self { Self(ParseAmountErrorInner::MissingDigits(value)) }
 }
 
 impl From<InputTooLargeError> for ParseAmountError {
-    fn from(value: InputTooLargeError) -> Self { Self::InputTooLarge(value) }
+    fn from(value: InputTooLargeError) -> Self { Self(ParseAmountErrorInner::InputTooLarge(value)) }
 }
 
 impl From<InvalidCharacterError> for ParseAmountError {
-    fn from(value: InvalidCharacterError) -> Self { Self::InvalidCharacter(value) }
+    fn from(value: InvalidCharacterError) -> Self {
+        Self(ParseAmountErrorInner::InvalidCharacter(value))
+    }
 }
 
 internals::impl_from_infallible!(ParseAmountError);
+internals::impl_from_infallible!(ParseAmountErrorInner);
 
 impl fmt::Display for ParseAmountError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use ParseAmountError::*;
+        use ParseAmountErrorInner::*;
 
-        match *self {
+        match self.0 {
             OutOfRange(ref error) => write_err!(f, "amount out of range"; error),
             TooPrecise(ref error) => write_err!(f, "amount has a too high precision"; error),
             MissingDigits(ref error) => write_err!(f, "the input has too few digits"; error),
@@ -130,9 +134,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 #[cfg(feature = "std")]
 impl std::error::Error for ParseAmountError {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        use ParseAmountError::*;
+        use ParseAmountErrorInner::*;
 
-        match *self {
+        match self.0 {
             TooPrecise(ref error) => Some(error),
             InputTooLarge(ref error) => Some(error),
             OutOfRange(ref error) => Some(error),
@@ -199,7 +203,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 impl std::error::Error for OutOfRangeError {}
 
 impl From<OutOfRangeError> for ParseAmountError {
-    fn from(value: OutOfRangeError) -> Self { ParseAmountError::OutOfRange(value) }
+    fn from(value: OutOfRangeError) -> Self {
+        ParseAmountError(ParseAmountErrorInner::OutOfRange(value))
+    }
 }
 
 /// Error returned when the input string has higher precision than satoshis.
diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs
index f78b5c37..21eb9ed8 100644
--- a/units/src/amount/mod.rs
+++ b/units/src/amount/mod.rs
@@ -20,7 +20,7 @@
 use core::fmt;
 use core::str::FromStr;
 
-use self::error::MissingDigitsKind;
+use self::error::{MissingDigitsKind, ParseAmountErrorInner, ParseErrorInner};
 
 #[rustfmt::skip]                // Keep public re-exports separate.
 #[doc(inline)]
@@ -297,10 +297,12 @@ fn convert(self, is_signed: bool) -> ParseAmountError {
         match self {
             Self::Overflow { is_negative } =>
                 OutOfRangeError { is_signed, is_greater_than_max: !is_negative }.into(),
-            Self::TooPrecise(error) => ParseAmountError::TooPrecise(error),
-            Self::MissingDigits(error) => ParseAmountError::MissingDigits(error),
-            Self::InputTooLarge(len) => ParseAmountError::InputTooLarge(InputTooLargeError { len }),
-            Self::InvalidCharacter(error) => ParseAmountError::InvalidCharacter(error),
+            Self::TooPrecise(e) => ParseAmountError(ParseAmountErrorInner::TooPrecise(e)),
+            Self::MissingDigits(e) => ParseAmountError(ParseAmountErrorInner::MissingDigits(e)),
+            Self::InputTooLarge(len) =>
+                ParseAmountError(ParseAmountErrorInner::InputTooLarge(InputTooLargeError { len })),
+            Self::InvalidCharacter(e) =>
+                ParseAmountError(ParseAmountErrorInner::InvalidCharacter(e)),
         }
     }
 }
@@ -311,7 +313,7 @@ fn split_amount_and_denomination(s: &str) -> Result<(&str, Denomination), ParseE
     } else {
         let i = s
             .find(|c: char| c.is_alphabetic())
-            .ok_or(ParseError::MissingDenomination(MissingDenominationError))?;
+            .ok_or(ParseError(ParseErrorInner::MissingDenomination(MissingDenominationError)))?;
         (i, i)
     };
     Ok((&s[..i], s[j..].parse()?))
diff --git a/units/src/amount/signed.rs b/units/src/amount/signed.rs
index 77a36931..632a5626 100644
--- a/units/src/amount/signed.rs
+++ b/units/src/amount/signed.rs
@@ -10,6 +10,7 @@
 #[cfg(feature = "arbitrary")]
 use arbitrary::{Arbitrary, Unstructured};
 
+use super::error::{ParseAmountErrorInner, ParseErrorInner};
 use super::{
     parse_signed_to_satoshi, split_amount_and_denomination, Amount, Denomination, Display,
     DisplayStyle, OutOfRangeError, ParseAmountError, ParseError,
@@ -17,9 +18,9 @@
 
 /// A signed amount.
 ///
-/// The [`SignedAmount`] type can be used to express Bitcoin amounts that support arithmetic and
-/// conversion to various denominations. The `Amount` type does not implement `serde` traits but we
-/// do provide modules for serializing as satoshis or bitcoin.
+/// The [`SignedAmount`] type can be used to express Bitcoin amounts that support
+/// arithmetic and conversion to various denominations.
+///
 ///
 /// Warning!
 ///
@@ -29,21 +30,6 @@
 /// start with `checked_`.  The operations from [`core::ops`] that [`Amount`]
 /// implements will panic when overflow or underflow occurs.
 ///
-/// # Examples
-///
-/// ```
-/// # #[cfg(feature = "serde")] {
-/// use serde::{Serialize, Deserialize};
-/// use bitcoin_units::SignedAmount;
-///
-/// #[derive(Serialize, Deserialize)]
-/// struct Foo {
-///     // If you are using `rust-bitcoin` then `bitcoin::amount::serde::as_sat` also works.
-///     #[serde(with = "bitcoin_units::amount::serde::as_sat")]  // Also `serde::as_btc`.
-///     amount: SignedAmount,
-/// }
-/// # }
-/// ```
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct SignedAmount(i64);
 
@@ -107,12 +93,14 @@ pub const fn from_int_btc_const(btc: i64) -> SignedAmount {
     pub fn from_str_in(s: &str, denom: Denomination) -> Result<SignedAmount, ParseAmountError> {
         match parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(true))? {
             // (negative, amount)
-            (false, sat) if sat > i64::MAX as u64 =>
-                Err(ParseAmountError::OutOfRange(OutOfRangeError::too_big(true))),
+            (false, sat) if sat > i64::MAX as u64 => Err(ParseAmountError(
+                ParseAmountErrorInner::OutOfRange(OutOfRangeError::too_big(true)),
+            )),
             (false, sat) => Ok(SignedAmount(sat as i64)),
             (true, sat) if sat == i64::MIN.unsigned_abs() => Ok(SignedAmount(i64::MIN)),
-            (true, sat) if sat > i64::MIN.unsigned_abs() =>
-                Err(ParseAmountError::OutOfRange(OutOfRangeError::too_small())),
+            (true, sat) if sat > i64::MIN.unsigned_abs() => Err(ParseAmountError(
+                ParseAmountErrorInner::OutOfRange(OutOfRangeError::too_small()),
+            )),
             (true, sat) => Ok(SignedAmount(-(sat as i64))),
         }
     }
@@ -430,7 +418,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
         let result = SignedAmount::from_str_with_denomination(s);
 
         match result {
-            Err(ParseError::MissingDenomination(_)) => {
+            Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
                 let d = SignedAmount::from_str_in(s, Denomination::Satoshi);
 
                 if d == Ok(SignedAmount::ZERO) {
@@ -457,16 +445,6 @@ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
     }
 }
 
-impl<'a> core::iter::Sum<&'a SignedAmount> for SignedAmount {
-    fn sum<I>(iter: I) -> Self
-    where
-        I: Iterator<Item = &'a SignedAmount>,
-    {
-        let sats: i64 = iter.map(|amt| amt.0).sum();
-        SignedAmount::from_sat(sats)
-    }
-}
-
 #[cfg(feature = "arbitrary")]
 impl<'a> Arbitrary<'a> for SignedAmount {
     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
diff --git a/units/src/amount/unsigned.rs b/units/src/amount/unsigned.rs
index 99af7f70..37845c1c 100644
--- a/units/src/amount/unsigned.rs
+++ b/units/src/amount/unsigned.rs
@@ -7,9 +7,12 @@
 use core::str::FromStr;
 use core::{default, fmt, ops};
 
+#[cfg(feature = "serde")]
+use ::serde::{Deserialize, Serialize};
 #[cfg(feature = "arbitrary")]
 use arbitrary::{Arbitrary, Unstructured};
 
+use super::error::{ParseAmountErrorInner, ParseErrorInner};
 use super::{
     parse_signed_to_satoshi, split_amount_and_denomination, Denomination, Display, DisplayStyle,
     OutOfRangeError, ParseAmountError, ParseError, SignedAmount,
@@ -19,9 +22,9 @@
 
 /// An amount.
 ///
-/// The [`Amount`] type can be used to express Bitcoin amounts that support arithmetic and
-/// conversion to various denominations. The `Amount` type does not implement `serde` traits but we
-/// do provide modules for serializing as satoshis or bitcoin.
+/// The [`Amount`] type can be used to express Bitcoin amounts that support
+/// arithmetic and conversion to various denominations.
+///
 ///
 /// Warning!
 ///
@@ -34,22 +37,8 @@
 /// zero is considered an underflow and will cause a panic if you're not using
 /// the checked arithmetic methods.
 ///
-/// # Examples
-///
-/// ```
-/// # #[cfg(feature = "serde")] {
-/// use serde::{Serialize, Deserialize};
-/// use bitcoin_units::Amount;
-///
-/// #[derive(Serialize, Deserialize)]
-/// struct Foo {
-///     // If you are using `rust-bitcoin` then `bitcoin::amount::serde::as_sat` also works.
-///     #[serde(with = "bitcoin_units::amount::serde::as_sat")]  // Also `serde::as_btc`.
-///     amount: Amount,
-/// }
-/// # }
-/// ```
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub struct Amount(u64);
 
 impl Amount {
@@ -115,7 +104,9 @@ pub fn from_str_in(s: &str, denom: Denomination) -> Result<Amount, ParseAmountEr
         let (negative, satoshi) =
             parse_signed_to_satoshi(s, denom).map_err(|error| error.convert(false))?;
         if negative {
-            return Err(ParseAmountError::OutOfRange(OutOfRangeError::negative()));
+            return Err(ParseAmountError(ParseAmountErrorInner::OutOfRange(
+                OutOfRangeError::negative(),
+            )));
         }
         Ok(Amount::from_sat(satoshi))
     }
@@ -417,7 +408,7 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
         let result = Amount::from_str_with_denomination(s);
 
         match result {
-            Err(ParseError::MissingDenomination(_)) => {
+            Err(ParseError(ParseErrorInner::MissingDenomination(_))) => {
                 let d = Amount::from_str_in(s, Denomination::Satoshi);
 
                 if d == Ok(Amount::ZERO) {
@@ -444,16 +435,6 @@ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
     }
 }
 
-impl<'a> core::iter::Sum<&'a Amount> for Amount {
-    fn sum<I>(iter: I) -> Self
-    where
-        I: Iterator<Item = &'a Amount>,
-    {
-        let sats: u64 = iter.map(|amt| amt.0).sum();
-        Amount::from_sat(sats)
-    }
-}
-
 #[cfg(feature = "arbitrary")]
 impl<'a> Arbitrary<'a> for Amount {
     fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
diff --git a/units/src/block.rs b/units/src/block.rs
index aabb4c2c..7b407e33 100644
--- a/units/src/block.rs
+++ b/units/src/block.rs
@@ -231,23 +231,6 @@ impl ops::SubAssign<BlockInterval> for BlockInterval {
     fn sub_assign(&mut self, rhs: BlockInterval) { self.0 = self.to_u32() - rhs.to_u32(); }
 }
 
-impl core::iter::Sum for BlockInterval {
-    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
-        let sum = iter.map(|interval| interval.0).sum();
-        BlockInterval::from_u32(sum)
-    }
-}
-
-impl<'a> core::iter::Sum<&'a BlockInterval> for BlockInterval {
-    fn sum<I>(iter: I) -> Self
-    where
-        I: Iterator<Item = &'a BlockInterval>,
-    {
-        let sum = iter.map(|interval| interval.0).sum();
-        BlockInterval::from_u32(sum)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/units/src/fee_rate.rs b/units/src/fee_rate.rs
index c6f3f76f..4ed19f68 100644
--- a/units/src/fee_rate.rs
+++ b/units/src/fee_rate.rs
@@ -267,24 +267,6 @@ impl SubAssign<&FeeRate> for FeeRate {
     fn sub_assign(&mut self, rhs: &FeeRate) { self.0 -= rhs.0 }
 }
 
-impl core::iter::Sum for FeeRate {
-    fn sum<I>(iter: I) -> Self
-    where
-        I: Iterator<Item = Self>,
-    {
-        FeeRate::from_sat_per_kwu(iter.map(FeeRate::to_sat_per_kwu).sum())
-    }
-}
-
-impl<'a> core::iter::Sum<&'a FeeRate> for FeeRate {
-    fn sum<I>(iter: I) -> Self
-    where
-        I: Iterator<Item = &'a FeeRate>,
-    {
-        FeeRate::from_sat_per_kwu(iter.map(|f| FeeRate::to_sat_per_kwu(*f)).sum())
-    }
-}
-
 crate::impl_parse_str_from_int_infallible!(FeeRate, u64, from_sat_per_kwu);
 
 #[cfg(test)]
diff --git a/units/src/parse.rs b/units/src/parse.rs
index a08fd569..84917406 100644
--- a/units/src/parse.rs
+++ b/units/src/parse.rs
@@ -293,25 +293,18 @@ pub(crate) fn hex_remove_optional_prefix(s: &str) -> &str {
 
 /// Error returned when parsing an integer from a hex string that is supposed to contain a prefix.
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct PrefixedHexError(PrefixedHexErrorInner);
-
-/// Error returned when parsing an integer from a hex string that is supposed to contain a prefix.
-#[derive(Debug, Clone, Eq, PartialEq)]
-enum PrefixedHexErrorInner {
+pub enum PrefixedHexError {
     /// Hex string is missing prefix.
     MissingPrefix(MissingPrefixError),
     /// Error parsing integer from hex string.
     ParseInt(ParseIntError),
 }
 
-internals::impl_from_infallible!(PrefixedHexError);
-internals::impl_from_infallible!(PrefixedHexErrorInner);
-
 impl fmt::Display for PrefixedHexError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use PrefixedHexErrorInner::*;
+        use PrefixedHexError::*;
 
-        match self.0 {
+        match *self {
             MissingPrefix(ref e) => write_err!(f, "hex string is missing prefix"; e),
             ParseInt(ref e) => write_err!(f, "prefixed hex string invalid int"; e),
         }
@@ -321,9 +314,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 #[cfg(feature = "std")]
 impl std::error::Error for PrefixedHexError {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        use PrefixedHexErrorInner::*;
+        use PrefixedHexError::*;
 
-        match self.0 {
+        match *self {
             MissingPrefix(ref e) => Some(e),
             ParseInt(ref e) => Some(e),
         }
@@ -331,33 +324,27 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
 }
 
 impl From<MissingPrefixError> for PrefixedHexError {
-    fn from(e: MissingPrefixError) -> Self { Self(PrefixedHexErrorInner::MissingPrefix(e)) }
+    fn from(e: MissingPrefixError) -> Self { Self::MissingPrefix(e) }
 }
 
 impl From<ParseIntError> for PrefixedHexError {
-    fn from(e: ParseIntError) -> Self { Self(PrefixedHexErrorInner::ParseInt(e)) }
+    fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
 }
 
 /// Error returned when parsing an integer from a hex string that is not supposed to contain a prefix.
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct UnprefixedHexError(UnprefixedHexErrorInner);
-
-#[derive(Debug, Clone, Eq, PartialEq)]
-enum UnprefixedHexErrorInner {
+pub enum UnprefixedHexError {
     /// Hex string contains prefix.
     ContainsPrefix(ContainsPrefixError),
     /// Error parsing integer from string.
     ParseInt(ParseIntError),
 }
 
-internals::impl_from_infallible!(UnprefixedHexError);
-internals::impl_from_infallible!(UnprefixedHexErrorInner);
-
 impl fmt::Display for UnprefixedHexError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use UnprefixedHexErrorInner::*;
+        use UnprefixedHexError::*;
 
-        match self.0 {
+        match *self {
             ContainsPrefix(ref e) => write_err!(f, "hex string is contains prefix"; e),
             ParseInt(ref e) => write_err!(f, "hex string parse int"; e),
         }
@@ -367,9 +354,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 #[cfg(feature = "std")]
 impl std::error::Error for UnprefixedHexError {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        use UnprefixedHexErrorInner::*;
+        use UnprefixedHexError::*;
 
-        match self.0 {
+        match *self {
             ContainsPrefix(ref e) => Some(e),
             ParseInt(ref e) => Some(e),
         }
@@ -377,16 +364,16 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
 }
 
 impl From<ContainsPrefixError> for UnprefixedHexError {
-    fn from(e: ContainsPrefixError) -> Self { Self(UnprefixedHexErrorInner::ContainsPrefix(e)) }
+    fn from(e: ContainsPrefixError) -> Self { Self::ContainsPrefix(e) }
 }
 
 impl From<ParseIntError> for UnprefixedHexError {
-    fn from(e: ParseIntError) -> Self { Self(UnprefixedHexErrorInner::ParseInt(e)) }
+    fn from(e: ParseIntError) -> Self { Self::ParseInt(e) }
 }
 
 /// Error returned when a hex string is missing a prefix (e.g. `0x`).
 #[derive(Debug, Clone, Eq, PartialEq)]
-struct MissingPrefixError {
+pub struct MissingPrefixError {
     hex: InputString,
 }
 
@@ -406,7 +393,7 @@ impl std::error::Error for MissingPrefixError {}
 
 /// Error when hex string contains a prefix (e.g. 0x).
 #[derive(Debug, Clone, Eq, PartialEq)]
-struct ContainsPrefixError {
+pub struct ContainsPrefixError {
     hex: InputString,
 }
 


Test patch:
diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs
index 8d8932ce..7010bb19 100644
--- a/bitcoin/src/blockdata/script/tests.rs
+++ b/bitcoin/src/blockdata/script/tests.rs
@@ -485,6 +485,10 @@ fn script_json_serialize() {
 
 #[test]
 fn script_asm() {
+    assert_eq!(
+        ScriptBuf::from_hex("6363636363686868686800").unwrap().to_string(),
+        "OP_IF OP_IF OP_IF OP_IF OP_IF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_0"
+    );
     assert_eq!(
         ScriptBuf::from_hex("6363636363686868686800").unwrap().to_string(),
         "OP_IF OP_IF OP_IF OP_IF OP_IF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_ENDIF OP_0"
diff --git a/units/src/amount/tests.rs b/units/src/amount/tests.rs
index 00d54d72..24e06754 100644
--- a/units/src/amount/tests.rs
+++ b/units/src/amount/tests.rs
@@ -33,7 +33,9 @@ fn from_str_zero() {
         match s.parse::<Amount>() {
             Err(e) => assert_eq!(
                 e,
-                ParseError::Amount(ParseAmountError::OutOfRange(OutOfRangeError::negative()))
+                ParseError(ParseErrorInner::Amount(ParseAmountError(
+                    ParseAmountErrorInner::OutOfRange(OutOfRangeError::negative())
+                )))
             ),
             Ok(_) => panic!("unsigned amount from {}", s),
         }
@@ -321,7 +323,7 @@ fn parsing() {
     // more than 50 chars.
     assert_eq!(
         p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin),
-        Err(E::InputTooLarge(InputTooLargeError { len: 51 }))
+        Err(E(ParseAmountErrorInner::InputTooLarge(InputTooLargeError { len: 51 })))
     );
 }
 
@@ -731,10 +733,10 @@ struct T {
     // errors
     let t: Result<T, serde_json::Error> =
         serde_json::from_str("{\"amt\": 1000000.000000001, \"samt\": 1}");
-    assert!(t
-        .unwrap_err()
-        .to_string()
-        .contains(&ParseAmountError::TooPrecise(TooPreciseError { position: 16 }).to_string()));
+    assert!(t.unwrap_err().to_string().contains(
+        &ParseAmountError(ParseAmountErrorInner::TooPrecise(TooPreciseError { position: 16 }))
+            .to_string()
+    ));
     let t: Result<T, serde_json::Error> = serde_json::from_str("{\"amt\": -1, \"samt\": 1}");
     assert!(t.unwrap_err().to_string().contains(&OutOfRangeError::negative().to_string()));
 }
@@ -825,8 +827,8 @@ struct T {
 
 #[test]
 fn sum_amounts() {
-    assert_eq!(Amount::from_sat(0), [].iter().sum::<Amount>());
-    assert_eq!(SignedAmount::from_sat(0), [].iter().sum::<SignedAmount>());
+    assert_eq!(Amount::from_sat(0), [].into_iter().sum::<Amount>());
+    assert_eq!(SignedAmount::from_sat(0), [].into_iter().sum::<SignedAmount>());
 
     let amounts = [Amount::from_sat(42), Amount::from_sat(1337), Amount::from_sat(21)];
     let sum = amounts.into_iter().sum::<Amount>();
diff --git a/units/tests/api.rs b/units/tests/api.rs
index 8649e739..c69e20bb 100644
--- a/units/tests/api.rs
+++ b/units/tests/api.rs
@@ -81,31 +81,6 @@ impl Types {
     fn new() -> Self { Self { a: Enums::new(), b: Structs::max() } }
 }
 
-/// A struct that includes all public non-error non-helper structs.
-// C-COMMON-TRAITS excluding `Default` and `Display`. `Display` is done in `./str.rs`.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-struct CommonTraits {
-    a: Amount,
-    c: SignedAmount,
-    d: BlockHeight,
-    e: BlockInterval,
-    f: FeeRate,
-    g: absolute::Height,
-    h: absolute::Time,
-    i: relative::Height,
-    j: relative::Time,
-    k: Weight,
-}
-
-/// A struct that includes all types that implement `Default`.
-#[derive(Default)] // C-COMMON-TRAITS: `Default`
-struct Default {
-    a: Amount,
-    b: SignedAmount,
-    c: relative::Height,
-    d: relative::Time,
-}
-
 /// A struct that includes all public error types.
 // These derives are the policy of `rust-bitcoin` not Rust API guidelines.
 #[derive(Debug, Clone, PartialEq, Eq)] // All public types implement Debug (C-DEBUG).
@@ -138,6 +113,8 @@ struct Errors {
     z: locktime::absolute::ParseHeightError,
     _a: locktime::absolute::ParseTimeError,
     _b: locktime::relative::TimeOverflowError,
+    _c: parse::ContainsPrefixError,
+    _d: parse::MissingPrefixError,
     _e: parse::ParseIntError,
     _f: parse::PrefixedHexError,
     _g: parse::UnprefixedHexError,
@@ -196,7 +173,10 @@ fn api_can_use_all_types_from_module_locktime_relative() {
 
 #[test]
 fn api_can_use_all_types_from_module_parse() {
-    use bitcoin_units::parse::{ParseIntError, PrefixedHexError, UnprefixedHexError};
+    use bitcoin_units::parse::{
+        ContainsPrefixError, MissingPrefixError, ParseIntError, PrefixedHexError,
+        UnprefixedHexError,
+    };
 }
 
 #[test]
diff --git a/units/tests/str.rs b/units/tests/str.rs
deleted file mode 100644
index 89068bf9..00000000
--- a/units/tests/str.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: CC0-1.0
-
-//! Do basic regression tests on the `Display` and `FromStr` impls.
-
-use bitcoin_units::locktime::{absolute, relative};
-use bitcoin_units::{Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, Weight};
-
-macro_rules! check {
-    ($($test_name:ident, $ty:path, $val:path, $str:literal);* $(;)?) => {
-        $(
-            #[test]
-            fn $test_name() {
-                let got = format!("{}", $val);
-                assert_eq!(got, $str);
-
-                let got = $str.parse::<$ty>().unwrap();
-                assert_eq!(got, $val)
-            }
-        )*
-    }
-}
-
-check! {
-    amount_unsigned_one_sat, Amount, Amount::ONE_SAT, "0.00000001 BTC";
-    amount_unsigned_max_money, Amount, Amount::MAX_MONEY, "21000000 BTC";
-
-    amount_signed_one_sat, SignedAmount, SignedAmount::ONE_SAT, "0.00000001 BTC";
-    amount_signed_max_money, SignedAmount, SignedAmount::MAX_MONEY, "21000000 BTC";
-
-    block_height_min, BlockHeight, BlockHeight::MIN, "0";
-    block_height_max, BlockHeight, BlockHeight::MAX, "4294967295";
-
-    block_interval_min, BlockInterval, BlockInterval::MIN, "0";
-    block_interval_max, BlockInterval, BlockInterval::MAX, "4294967295";
-
-    fee_rate_min, FeeRate, FeeRate::MIN, "0";
-    fee_rate_max, FeeRate, FeeRate::MAX, "18446744073709551615";
-    fee_rate_dust, FeeRate, FeeRate::DUST, "750";
-
-    lock_time_absolute_min, absolute::Height, absolute::Height::MIN, "0";
-    lock_time_absolute_max, absolute::Height, absolute::Height::MAX, "499999999";
-
-    lock_time_relative_min, relative::Height, relative::Height::MIN, "0";
-    lock_time_relative_max, relative::Height, relative::Height::MAX, "65535";
-
-    weight_min, Weight, Weight::MIN, "0";
-    weight_max, Weight, Weight::MAX, "18446744073709551615";
-    weight_max_block, Weight, Weight::MAX_BLOCK, "4000000";
-}