Repository: rust-bitcoin/rust-bitcoin
Pull request #3858 (2025-01-05 01:27:23)

Bound `Address` parsing on `NetworkValidationUnchecked`
Currently it is not possible for downstream to use a generic on the `Address` type in structs in conjuncture with
derives (`serde::Deserialize` and `Display`) because our impls are only done for `NetworkUnchecked` (as they should be).

However, as observed by dpc, if we add a secondary marker trait and use it to bound the impls, implementing the new marker for `NetworkUnchecked` then downstream can use derives by way of

```
    #[derive(Serialize, Deserialize)]
    struct Foo<V>
        where V: NetworkValidation,
    {
        #[serde(bound(deserialize = "V: NetworkValidationUnchecked"))]
        address: Address<V>,
    }
```

This is cool as hell because the `Address` type is currently a royal PITA.

Patch 1 is trivial cleanup.

To get past a build error in `FromStr` I used this little trick
```rust
            // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
            Ok(Address(address.0, PhantomData::<U>))
```

Resolve: #3760
and
Close: #3856

tcharding: Mad props @dpc, your recent lesson on the cost of context switching made me think that much harder instead of posting a question when I was stuck with a build error (since it would require the reader to context switch to answer).


Patch:
diff --git a/api/io/all-features.txt b/api/io/all-features.txt
index 055e6305..af10dd88 100644
--- a/api/io/all-features.txt
+++ b/api/io/all-features.txt
@@ -242,8 +242,6 @@ pub fn bitcoin_io::FromStd<T>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::FromStd<T>::fill_buf(&mut self) -> std::io::error::Result<&[u8]>
 pub fn bitcoin_io::FromStd<T>::flush(&mut self) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::FromStd<T>::flush(&mut self) -> std::io::error::Result<()>
-pub fn bitcoin_io::FromStd<T>::get_mut(&mut self) -> &mut T
-pub fn bitcoin_io::FromStd<T>::get_ref(&self) -> &T
 pub fn bitcoin_io::FromStd<T>::inner(&self) -> &T
 pub fn bitcoin_io::FromStd<T>::inner_mut(&mut self) -> &mut T
 pub fn bitcoin_io::FromStd<T>::into_inner(self) -> T
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index 50453e6b..51a02c56 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -22,6 +22,22 @@
 //! let address = Address::p2pkh(&public_key, Network::Bitcoin);
 //! }
 //! ```
+//!
+//! ### Using an `Address` as a struct field.
+//!
+//! ```rust
+//! # #[cfg(feature = "serde")] {
+//! # use serde::{self, Deserialize, Serialize};
+//! use bitcoin::address::{Address, NetworkValidation, NetworkValidationUnchecked};
+//! #[derive(Serialize, Deserialize)]
+//! struct Foo<V>
+//!     where V: NetworkValidation,
+//! {
+//!     #[serde(bound(deserialize = "V: NetworkValidationUnchecked"))]
+//!     address: Address<V>,
+//! }
+//! # }
+//! ```
 
 pub mod error;
 pub mod script_pubkey;
@@ -107,6 +123,9 @@ mod sealed {
     pub trait NetworkValidation {}
     impl NetworkValidation for super::NetworkChecked {}
     impl NetworkValidation for super::NetworkUnchecked {}
+
+    pub trait NetworkValidationUnchecked {}
+    impl NetworkValidationUnchecked for super::NetworkUnchecked {}
 }
 
 /// Marker of status of address's network validation. See section [*Parsing addresses*](Address#parsing-addresses)
@@ -116,14 +135,25 @@ pub trait NetworkValidation: sealed::NetworkValidation + Sync + Send + Sized + U
     const IS_CHECKED: bool;
 }
 
+/// Marker trait for `FromStr` and `serde::Deserialize`.
+///
+/// This allows users to use `V: NetworkValidation` in conjunction with derives. Is only ever
+/// implemented for `NetworkUnchecked`.
+pub trait NetworkValidationUnchecked:
+    NetworkValidation + sealed::NetworkValidationUnchecked + Sync + Send + Sized + Unpin
+{
+}
+
 /// Marker that address's network has been successfully validated. See section [*Parsing addresses*](Address#parsing-addresses)
 /// on [`Address`] for details.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum NetworkChecked {}
 
 /// Marker that address's network has not yet been validated. See section [*Parsing addresses*](Address#parsing-addresses)
 /// on [`Address`] for details.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 pub enum NetworkUnchecked {}
 
 impl NetworkValidation for NetworkChecked {
@@ -133,6 +163,8 @@ impl NetworkValidation for NetworkUnchecked {
     const IS_CHECKED: bool = false;
 }
 
+impl NetworkValidationUnchecked for NetworkUnchecked {}
+
 /// The inner representation of an address, without the network validation tag.
 ///
 /// This struct represents the inner representation of an address without the network validation
@@ -361,10 +393,41 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self
 }
 
 #[cfg(feature = "serde")]
-internals::serde_string_deserialize_impl!(Address<NetworkUnchecked>, "a Bitcoin address");
+impl<'de, U: NetworkValidationUnchecked> serde::Deserialize<'de> for Address<U> {
+    fn deserialize<D>(deserializer: D) -> Result<Address<U>, D::Error>
+    where
+        D: serde::de::Deserializer<'de>,
+    {
+        use core::fmt::Formatter;
+
+        struct Visitor<U>(PhantomData<U>);
+        impl<U> serde::de::Visitor<'_> for Visitor<U>
+        where
+            U: NetworkValidationUnchecked + NetworkValidation,
+            Address<U>: FromStr,
+        {
+            type Value = Address<U>;
+
+            fn expecting(&self, f: &mut Formatter) -> core::fmt::Result {
+                f.write_str("A Bitcoin address")
+            }
+
+            fn visit_str<E>(self, v: &str) -> core::result::Result<Self::Value, E>
+            where
+                E: serde::de::Error,
+            {
+                // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
+                let address = v.parse::<Address<NetworkUnchecked>>().map_err(E::custom)?;
+                Ok(Address(address.0, PhantomData::<U>))
+            }
+        }
+
+        deserializer.deserialize_str(Visitor(PhantomData::<U>))
+    }
+}
 
 #[cfg(feature = "serde")]
-impl<N: NetworkValidation> serde::Serialize for Address<N> {
+impl<V: NetworkValidation> serde::Serialize for Address<V> {
     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
     where
         S: serde::Serializer,
@@ -880,15 +943,18 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 ///   not a valid base58 address.
 ///
 /// - [`UnknownHrpError`] if the address does not begin with one of the above SegWit or
-///   legacy prifixes.
-impl FromStr for Address<NetworkUnchecked> {
+///   legacy prefixes.
+impl<U: NetworkValidationUnchecked> FromStr for Address<U> {
     type Err = ParseError;
 
-    fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
+    fn from_str(s: &str) -> Result<Self, ParseError> {
         if ["bc1", "bcrt1", "tb1"].iter().any(|&prefix| s.to_lowercase().starts_with(prefix)) {
-            Ok(Address::from_bech32_str(s)?)
+            let address = Address::from_bech32_str(s)?;
+            // We know that `U` is only ever `NetworkUnchecked` but the compiler does not.
+            Ok(Address(address.0, PhantomData::<U>))
         } else if ["1", "2", "3", "m", "n"].iter().any(|&prefix| s.starts_with(prefix)) {
-            Ok(Address::from_base58_str(s)?)
+            let address = Address::from_base58_str(s)?;
+            Ok(Address(address.0, PhantomData::<U>))
         } else {
             let hrp = match s.rfind('1') {
                 Some(pos) => &s[..pos],
@@ -1427,4 +1493,37 @@ fn matches_script_pubkey() {
             }
         }
     }
+
+    #[test]
+    #[cfg(feature = "serde")]
+    fn serde_address_usage_in_struct() {
+        use serde::{self, Deserialize, Serialize};
+
+        #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+        struct Foo<V>
+        where
+            V: NetworkValidation,
+        {
+            #[serde(bound(deserialize = "V: NetworkValidationUnchecked"))]
+            address: Address<V>,
+        }
+
+        let addr_str = "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k";
+        let unchecked = addr_str.parse::<Address<_>>().unwrap();
+
+        // Serialize with an unchecked address.
+        let foo_unchecked = Foo { address: unchecked.clone() };
+        let ser = serde_json::to_string(&foo_unchecked).expect("failed to serialize");
+        let rinsed: Foo<NetworkUnchecked> =
+            serde_json::from_str(&ser).expect("failed to deserialize");
+        assert_eq!(rinsed, foo_unchecked);
+
+        // Serialize with a checked address.
+        let foo_checked = Foo { address: unchecked.assume_checked() };
+        let ser = serde_json::to_string(&foo_checked).expect("failed to serialize");
+        let rinsed: Foo<NetworkUnchecked> =
+            serde_json::from_str(&ser).expect("failed to deserialize");
+        assert_eq!(&rinsed.address, foo_checked.address.as_unchecked());
+        assert_eq!(rinsed, foo_unchecked);
+    }
 }
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index 569a8b7d..d32e7421 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -723,7 +723,7 @@ fn header_block_hash_regression() {
     #[test]
     fn soft_fork_signalling() {
         for i in 0..31 {
-            let version_int = (0x20000000u32 ^ (1 << i)) as i32;
+            let version_int = (0x20000000u32 ^ 1 << i) as i32;
             let version = Version::from_consensus(version_int);
             if i < 29 {
                 assert!(version.is_signalling_soft_fork(i));
@@ -732,7 +732,7 @@ fn soft_fork_signalling() {
             }
         }
 
-        let segwit_signal = Version::from_consensus(0x20000000 ^ (1 << 1));
+        let segwit_signal = Version::from_consensus(0x20000000 ^ 1 << 1);
         assert!(!segwit_signal.is_signalling_soft_fork(0));
         assert!(segwit_signal.is_signalling_soft_fork(1));
         assert!(!segwit_signal.is_signalling_soft_fork(2));
diff --git a/bitcoin/src/blockdata/script/builder.rs b/bitcoin/src/blockdata/script/builder.rs
index b8a52ea9..ea865813 100644
--- a/bitcoin/src/blockdata/script/builder.rs
+++ b/bitcoin/src/blockdata/script/builder.rs
@@ -151,5 +151,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0
 }
 
 impl fmt::Debug for Builder {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt::Display::fmt(self, f)
+    }
 }
diff --git a/bitcoin/src/merkle_tree/block.rs b/bitcoin/src/merkle_tree/block.rs
index 74b2b6c7..4410c8a9 100644
--- a/bitcoin/src/merkle_tree/block.rs
+++ b/bitcoin/src/merkle_tree/block.rs
@@ -146,7 +146,7 @@ fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Erro
 /// stored, and its children are not explored further. Otherwise, no hash is
 /// stored, but we recurse into both (or the only) child branch. During
 /// decoding, the same depth-first traversal is performed, consuming bits and
-/// hashes as they are written during encoding.
+/// hashes as they written during encoding.
 ///
 /// The serialization is fixed and provides a hard guarantee about the
 /// encoded size:
diff --git a/bitcoin/src/p2p/mod.rs b/bitcoin/src/p2p/mod.rs
index b594a669..040e2dfc 100644
--- a/bitcoin/src/p2p/mod.rs
+++ b/bitcoin/src/p2p/mod.rs
@@ -293,7 +293,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
 }
 
 impl fmt::Debug for Magic {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt::Display::fmt(self, f)
+    }
 }
 
 impl fmt::LowerHex for Magic {
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index 63a5c854..b6d531cb 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -618,8 +618,8 @@ fn mul_u64(self, rhs: u64) -> (U256, bool) {
             carry = n >> 64; // and carry the high bits.
         }
 
-        let low = u128::from(split_le[0]) | (u128::from(split_le[1]) << 64);
-        let high = u128::from(split_le[2]) | (u128::from(split_le[3]) << 64);
+        let low = u128::from(split_le[0]) | u128::from(split_le[1]) << 64;
+        let high = u128::from(split_le[2]) | u128::from(split_le[3]) << 64;
         (Self(high, low), carry != 0)
     }
 
@@ -872,7 +872,7 @@ fn to_f64(self) -> f64 {
         // (This is why we only care if the other non-msb dropped bits are all 0 or not,
         // so we can just OR them to make sure any bits show up somewhere.)
         let mantissa =
-            (mantissa + ((dropped_bits - ((dropped_bits >> 127) & !mantissa)) >> 127)) as u64;
+            (mantissa + ((dropped_bits - (dropped_bits >> 127 & !mantissa)) >> 127)) as u64;
         // Step 6: Calculate the exponent
         // If self is 0, exponent should be 0 (special meaning) and mantissa will end up 0 too
         // Otherwise, (255 - n) + 1022 so it simplifies to 1277 - n
@@ -1115,8 +1115,8 @@ fn bit_at(&self, index: usize) -> bool {
         /// Constructs a new U256 from a big-endian array of u64's
         fn from_array(a: [u64; 4]) -> Self {
             let mut ret = U256::ZERO;
-            ret.0 = ((a[0] as u128) << 64) ^ (a[1] as u128);
-            ret.1 = ((a[2] as u128) << 64) ^ (a[3] as u128);
+            ret.0 = (a[0] as u128) << 64 ^ (a[1] as u128);
+            ret.1 = (a[2] as u128) << 64 ^ (a[3] as u128);
             ret
         }
     }
@@ -1559,11 +1559,11 @@ fn u256_multiplication() {
     #[test]
     fn u256_multiplication_bits_in_each_word() {
         // Put a digit in the least significant bit of each 64 bit word.
-        let u = (1_u128 << 64) | 1_u128;
+        let u = 1_u128 << 64 | 1_u128;
         let x = U256(u, u);
 
         // Put a digit in the second least significant bit of each 64 bit word.
-        let u = (2_u128 << 64) | 2_u128;
+        let u = 2_u128 << 64 | 2_u128;
         let y = U256(u, u);
 
         let (got, overflow) = x.overflowing_mul(y);
diff --git a/chacha20_poly1305/src/poly1305.rs b/chacha20_poly1305/src/poly1305.rs
index 0b277b01..a064feb7 100644
--- a/chacha20_poly1305/src/poly1305.rs
+++ b/chacha20_poly1305/src/poly1305.rs
@@ -134,10 +134,10 @@ pub fn input(&mut self, message: &[u8]) {
             self.acc[i] = t & mask | self.acc[i] & !mask;
         }
         // Voodoo from donna to convert to [u32; 4].
-        let a0 = self.acc[0] | (self.acc[1] << 26);
-        let a1 = (self.acc[1] >> 6) | (self.acc[2] << 20);
-        let a2 = (self.acc[2] >> 12) | (self.acc[3] << 14);
-        let a3 = (self.acc[3] >> 18) | (self.acc[4] << 8);
+        let a0 = self.acc[0] | self.acc[1] << 26;
+        let a1 = self.acc[1] >> 6 | self.acc[2] << 20;
+        let a2 = self.acc[2] >> 12 | self.acc[3] << 14;
+        let a3 = self.acc[3] >> 18 | self.acc[4] << 8;
         let a = [a0, a1, a2, a3];
         // a + s
         let mut tag: [u64; 4] = [0; 4];
@@ -196,21 +196,21 @@ fn r_times_a(&mut self) {
     // Encode number in five 26-bit limbs.
     let m0 = u32::from_le_bytes(fmt_msg[0..4].try_into().expect("Valid subset of 32.")) & BITMASK;
     let m1 =
-        (u32::from_le_bytes(fmt_msg[3..7].try_into().expect("Valid subset of 32.")) >> 2) & BITMASK;
+        u32::from_le_bytes(fmt_msg[3..7].try_into().expect("Valid subset of 32.")) >> 2 & BITMASK;
     let m2 =
-        (u32::from_le_bytes(fmt_msg[6..10].try_into().expect("Valid subset of 32.")) >> 4) & BITMASK;
+        u32::from_le_bytes(fmt_msg[6..10].try_into().expect("Valid subset of 32.")) >> 4 & BITMASK;
     let m3 =
-        (u32::from_le_bytes(fmt_msg[9..13].try_into().expect("Valid subset of 32.")) >> 6) & BITMASK;
+        u32::from_le_bytes(fmt_msg[9..13].try_into().expect("Valid subset of 32.")) >> 6 & BITMASK;
     let m4 =
-        (u32::from_le_bytes(fmt_msg[12..16].try_into().expect("Valid subset of 32.")) >> 8) | hi_bit;
+        u32::from_le_bytes(fmt_msg[12..16].try_into().expect("Valid subset of 32.")) >> 8 | hi_bit;
     [m0, m1, m2, m3, m4]
 }
 
 fn _print_acc(num: &[u32; 5]) {
-    let a0 = num[0] | (num[1] << 26);
-    let a1 = (num[1] >> 6) | (num[2] << 20);
-    let a2 = (num[2] >> 12) | (num[3] << 14);
-    let a3 = (num[3] >> 18) | (num[4] << 8);
+    let a0 = num[0] | num[1] << 26;
+    let a1 = num[1] >> 6 | num[2] << 20;
+    let a2 = num[2] >> 12 | num[3] << 14;
+    let a3 = num[3] >> 18 | num[4] << 8;
     let a = [a0, a1, a2, a3];
     let mut ret: [u8; 16] = [0; 16];
     for i in 0..a.len() {
diff --git a/io/src/bridge.rs b/io/src/bridge.rs
index a979f738..204a77a5 100644
--- a/io/src/bridge.rs
+++ b/io/src/bridge.rs
@@ -12,6 +12,18 @@ impl<T> FromStd<T> {
     #[inline]
     pub const fn new(inner: T) -> Self { Self(inner) }
 
+    /// Returns the wrapped value.
+    #[inline]
+    pub fn into_inner(self) -> T { self.0 }
+
+    /// Returns a reference to the wrapped value.
+    #[inline]
+    pub fn inner(&self) -> &T { &self.0 }
+
+    /// Returns a mutable reference to the wrapped value.
+    #[inline]
+    pub fn inner_mut(&mut self) -> &mut T { &mut self.0 }
+
     /// Wraps a mutable reference to IO type.
     #[inline]
     pub fn new_mut(inner: &mut T) -> &mut Self {
@@ -26,28 +38,6 @@ pub fn new_boxed(inner: Box<T>) -> Box<Self> {
         // SAFETY: the type is repr(transparent) and the pointer is created from Box
         unsafe { Box::from_raw(Box::into_raw(inner) as *mut Self) }
     }
-
-    /// Returns the wrapped value.
-    #[inline]
-    pub fn into_inner(self) -> T { self.0 }
-
-    /// Returns a reference to the wrapped value.
-    #[inline]
-    pub fn get_ref(&self) -> &T { &self.0 }
-
-    /// Returns a mutable reference to the wrapped value.
-    #[inline]
-    pub fn get_mut(&mut self) -> &mut T { &mut self.0 }
-
-    /// Returns a reference to the wrapped value.
-    #[inline]
-    #[deprecated(since = "TBD", note = "use `get_ref()` instead")]
-    pub fn inner(&self) -> &T { &self.0 }
-
-    /// Returns a mutable reference to the wrapped value.
-    #[inline]
-    #[deprecated(since = "TBD", note = "use `get_ref()` instead")]
-    pub fn inner_mut(&mut self) -> &mut T { &mut self.0 }
 }
 
 impl<T: std::io::Read> super::Read for FromStd<T> {
diff --git a/primitives/src/opcodes.rs b/primitives/src/opcodes.rs
index bd9e8ce8..d1ab77b7 100644
--- a/primitives/src/opcodes.rs
+++ b/primitives/src/opcodes.rs
@@ -440,7 +440,9 @@ fn from(b: u8) -> Opcode { Opcode { code: b } }
 }
 
 impl fmt::Debug for Opcode {
-    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt::Display::fmt(self, f) }
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt::Display::fmt(self, f)
+    }
 }
 
 #[cfg(feature = "serde")]
diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs
index 2c7f59f2..79d2e7ec 100644
--- a/units/src/amount/mod.rs
+++ b/units/src/amount/mod.rs
@@ -285,7 +285,6 @@ fn parse_signed_to_satoshi(
     Ok((is_negative, value))
 }
 
-#[derive(Debug)]
 enum InnerParseError {
     Overflow { is_negative: bool },
     TooPrecise(TooPreciseError),
diff --git a/units/src/amount/serde.rs b/units/src/amount/serde.rs
index f022ba15..7eb35f6e 100644
--- a/units/src/amount/serde.rs
+++ b/units/src/amount/serde.rs
@@ -390,12 +390,6 @@ fn visit_some<D>(self, d: D) -> Result<Self::Value, D::Error>
 mod tests {
     use super::*;
 
-    #[test]
-    fn sanity_check() {
-        assert_eq!(Amount::type_prefix(private::Token), "u");
-        assert_eq!(SignedAmount::type_prefix(private::Token), "i");
-    }
-
     #[test]
     fn can_serde_as_sat() {
         #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
diff --git a/units/src/block.rs b/units/src/block.rs
index 8eaca0da..9e8efa52 100644
--- a/units/src/block.rs
+++ b/units/src/block.rs
@@ -278,29 +278,11 @@ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
 mod tests {
     use super::*;
 
-    #[test]
-    fn sanity_check() {
-        let height: u32 = BlockHeight(100).into();
-        assert_eq!(height, 100);
-
-        let interval: u32 = BlockInterval(100).into();
-        assert_eq!(interval, 100);
-
-        let interval_from_height: BlockInterval = relative::Height::from(10u16).into();
-        assert_eq!(interval_from_height.to_u32(), 10u32);
-
-        let invalid_height_greater = relative::Height::try_from(BlockInterval(u32::from(u16::MAX) + 1));
-        assert!(invalid_height_greater.is_err());
-
-        let valid_height = relative::Height::try_from(BlockInterval(u32::from(u16::MAX)));
-        assert!(valid_height.is_ok());
-    }
-
     // These tests are supposed to comprise an exhaustive list of available operations.
     #[test]
     fn all_available_ops() {
         // height - height = interval
-        assert!(BlockHeight(10) - BlockHeight(7) == BlockInterval(3));
+        assert!(BlockHeight(100) - BlockHeight(99) == BlockInterval(1));
 
         // height + interval = height
         assert!(BlockHeight(100) + BlockInterval(1) == BlockHeight(101));
@@ -312,10 +294,7 @@ fn all_available_ops() {
         assert!(BlockInterval(1) + BlockInterval(2) == BlockInterval(3));
 
         // interval - interval = interval
-        assert!(BlockInterval(10) - BlockInterval(7) == BlockInterval(3));
-
-        assert!([BlockInterval(1), BlockInterval(2), BlockInterval(3)].iter().sum::<BlockInterval>() == BlockInterval(6));
-        assert!([BlockInterval(4), BlockInterval(5), BlockInterval(6)].into_iter().sum::<BlockInterval>() == BlockInterval(15));
+        assert!(BlockInterval(3) - BlockInterval(2) == BlockInterval(1));
 
         // interval += interval
         let mut int = BlockInterval(1);
@@ -323,8 +302,8 @@ fn all_available_ops() {
         assert_eq!(int, BlockInterval(3));
 
         // interval -= interval
-        let mut int = BlockInterval(10);
-        int -= BlockInterval(7);
-        assert_eq!(int, BlockInterval(3));
+        let mut int = BlockInterval(3);
+        int -= BlockInterval(2);
+        assert_eq!(int, BlockInterval(1));
     }
 }
diff --git a/units/src/fee_rate/mod.rs b/units/src/fee_rate/mod.rs
index 2b5fe820..b12c08f4 100644
--- a/units/src/fee_rate/mod.rs
+++ b/units/src/fee_rate/mod.rs
@@ -72,9 +72,6 @@ pub const fn to_sat_per_kwu(self) -> u64 { self.0 }
     pub const fn to_sat_per_vb_floor(self) -> u64 { self.0 / (1000 / 4) }
 
     /// Converts to sat/vB rounding up.
-    /// TODO: cargo-mutants will try to replace - with /, which results in 1000 / 4 / 1 which is also 250.
-    /// Since we're addressing the mutants before introducing the cargo-mutants workflow, come back later
-    /// and skip this function in the mutants.toml config file
     pub const fn to_sat_per_vb_ceil(self) -> u64 { (self.0 + (1000 / 4 - 1)) / (1000 / 4) }
 
     /// Checked multiplication.
@@ -257,12 +254,6 @@ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
 mod tests {
     use super::*;
 
-    #[test]
-    fn sanity_check() {
-        let fee_rate: u64 = u64::from(FeeRate(100));
-        assert_eq!(fee_rate, 100_u64);
-    }
-
     #[test]
     #[allow(clippy::op_ref)]
     fn addition() {
@@ -279,24 +270,14 @@ fn addition() {
     #[test]
     #[allow(clippy::op_ref)]
     fn subtract() {
+        let one = FeeRate(1);
+        let two = FeeRate(2);
         let three = FeeRate(3);
-        let seven = FeeRate(7);
-        let ten = FeeRate(10);
-
-        assert_eq!(ten - seven, three);
-        assert_eq!(&ten - seven, three);
-        assert_eq!(ten - &seven, three);
-        assert_eq!(&ten - &seven, three);
-    }
-
-    #[test]
-    #[allow(clippy::op_ref)]
-    fn multiply() {
-        let two = FeeRate::from_sat_per_vb(2).unwrap();
-        let three = Weight::from_vb(3).unwrap();
-        let six = Amount::from_sat(6);
 
-        assert_eq!(two * three, six);
+        assert!(three - two == one);
+        assert!(&three - two == one);
+        assert!(three - &two == one);
+        assert!(&three - &two == one);
     }
 
     #[test]
@@ -362,7 +343,7 @@ fn fee_rate_from_sat_per_vb() {
 
     #[test]
     fn fee_rate_from_sat_per_kvb() {
-        let fee_rate = FeeRate::from_sat_per_kvb(11);
+        let fee_rate = FeeRate::from_sat_per_kvb(10);
         assert_eq!(FeeRate(2), fee_rate);
     }
 
@@ -385,10 +366,10 @@ fn from_sat_per_vb_unchecked() {
 
     #[test]
     fn raw_feerate() {
-        let fee_rate = FeeRate(749);
-        assert_eq!(749, fee_rate.to_sat_per_kwu());
-        assert_eq!(2, fee_rate.to_sat_per_vb_floor());
-        assert_eq!(3, fee_rate.to_sat_per_vb_ceil());
+        let fee_rate = FeeRate(333);
+        assert_eq!(333, fee_rate.to_sat_per_kwu());
+        assert_eq!(1, fee_rate.to_sat_per_vb_floor());
+        assert_eq!(2, fee_rate.to_sat_per_vb_ceil());
     }
 
     #[test]
@@ -400,25 +381,6 @@ fn checked_mul() {
         assert!(fee_rate.is_none());
     }
 
-    #[test]
-    fn fee_wu() {
-        let fee_overflow = FeeRate(10).fee_wu(Weight::MAX);
-        assert!(fee_overflow.is_none());
-
-        let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
-        let weight = Weight::from_vb(3).unwrap();
-        assert_eq!(fee_rate.fee_wu(weight).unwrap(), Amount::from_sat(6));
-    }
-
-    #[test]
-    fn fee_vb() {
-        let fee_overflow = FeeRate(10).fee_vb(Weight::MAX.to_wu());
-        assert!(fee_overflow.is_none());
-
-        let fee_rate = FeeRate::from_sat_per_vb(2).unwrap();
-        assert_eq!(fee_rate.fee_vb(3).unwrap(), Amount::from_sat(6));
-    }
-
     #[test]
     fn checked_weight_mul() {
         let weight = Weight::from_vb(10).unwrap();
diff --git a/units/src/locktime/absolute.rs b/units/src/locktime/absolute.rs
index b7ee722e..966f6e12 100644
--- a/units/src/locktime/absolute.rs
+++ b/units/src/locktime/absolute.rs
@@ -472,15 +472,6 @@ fn height_from_str_hex_invalid_hex_should_err() {
         assert!(result.is_err());
     }
 
-    #[test]
-    fn is_block_height_or_time() {
-        assert!(is_block_height(499_999_999));
-        assert!(!is_block_height(500_000_000));
-
-        assert!(!is_block_time(499_999_999));
-        assert!(is_block_time(500_000_000));
-    }
-
     #[test]
     #[cfg(feature = "serde")]
     pub fn encode_decode_height() {
diff --git a/units/src/locktime/relative.rs b/units/src/locktime/relative.rs
index a217391d..25c875ee 100644
--- a/units/src/locktime/relative.rs
+++ b/units/src/locktime/relative.rs
@@ -116,8 +116,6 @@ pub const fn value(self) -> u16 { self.0 }
 
     /// Returns the `u32` value used to encode this locktime in an nSequence field or
     /// argument to `OP_CHECKSEQUENCEVERIFY`.
-    /// TODO: Skip this in cargo-mutants. It will replace | with ^, which will return the same
-    /// value since the XOR is always taken against the u16 and an all-zero bitmask
     #[inline]
     pub const fn to_consensus_u32(self) -> u32 {
         (1u32 << 22) | self.0 as u32 // cast safety: u32 is wider than u16 on all architectures
@@ -195,13 +193,6 @@ mod tests {
 
     const MAXIMUM_ENCODABLE_SECONDS: u32 = u16::MAX as u32 * 512;
 
-    #[test]
-    fn sanity_check() {
-        assert_eq!(Height::MAX.to_consensus_u32(), u32::from(u16::MAX));
-        assert_eq!(Time::from_512_second_intervals(100).value(), 100u16);
-        assert_eq!(Time::from_512_second_intervals(100).to_consensus_u32(), 4_194_404u32); // 0x400064
-    }
-
     #[test]
     fn from_seconds_ceil_success() {
         let actual = Time::from_seconds_ceil(100).unwrap();
diff --git a/units/src/parse.rs b/units/src/parse.rs
index 4ad750ac..b51b60fe 100644
--- a/units/src/parse.rs
+++ b/units/src/parse.rs
@@ -24,7 +24,7 @@ pub struct ParseIntError {
     pub(crate) input: InputString,
     // for displaying - see Display impl with nice error message below
     pub(crate) bits: u8,
-    // We could represent this as a single bit, but it wouldn't actually decrease the cost of moving
+    // We could represent this as a single bit but it wouldn't actually derease the cost of moving
     // the struct because String contains pointers so there will be padding of bits at least
     // pointer_size - 1 bytes: min 1B in practice.
     pub(crate) is_signed: bool,
@@ -137,7 +137,7 @@ fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err>
 /// Implements `TryFrom<$from> for $to` using `parse::int`, mapping the output using infallible
 /// conversion function `fn`.
 #[macro_export]
-#[doc(hidden)] // Helper macro called by `impl_parse_str_from_int_infallible`.
+#[doc(hidden)]                  // Helper macro called by `impl_parse_str_from_int_infallible`.
 macro_rules! impl_tryfrom_str_from_int_infallible {
     ($($from:ty, $to:ident, $inner:ident, $fn:ident);*) => {
         $(
@@ -192,7 +192,7 @@ fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err>
 
 /// Implements `TryFrom<$from> for $to`.
 #[macro_export]
-#[doc(hidden)] // Helper macro called by `impl_parse_str`.
+#[doc(hidden)]                  // Helper macro called by `impl_parse_str`.
 macro_rules! impl_tryfrom_str {
     ($($from:ty, $to:ty, $err:ty, $inner_fn:expr);*) => {
         $(
@@ -488,64 +488,8 @@ impl std::error::Error for ContainsPrefixError {}
 
 #[cfg(test)]
 mod tests {
-    #[cfg(feature = "std")]
-    use std::panic;
-
     use super::*;
 
-    #[test]
-    fn parse_int() {
-        assert!(int::<u8, _>("1").is_ok());
-        let _ = int::<i8, _>("not a number").map_err(|e| assert!(e.is_signed));
-        let _ = int::<u8, _>("not a number").map_err(|e| assert!(!e.is_signed));
-    }
-
-    #[test]
-    #[cfg(feature = "std")]
-    fn parse_int_panic_when_populating_bits() {
-        // Fields in the test type are never used
-        #[allow(dead_code)]
-        struct TestTypeLargerThanU128(u128, u128);
-        impl_integer!(TestTypeLargerThanU128);
-        impl FromStr for TestTypeLargerThanU128 {
-            type Err = core::num::ParseIntError;
-
-            fn from_str(_: &str) -> Result<Self, Self::Err> {
-                "Always invalid for testing".parse::<u32>().map(|_| TestTypeLargerThanU128(0, 0))
-            }
-        }
-        impl From<i8> for TestTypeLargerThanU128 {
-            fn from(_: i8) -> Self { TestTypeLargerThanU128(0, 0) }
-        }
-
-        let result = panic::catch_unwind(|| int::<TestTypeLargerThanU128, _>("not a number"));
-        assert!(result.is_err());
-    }
-
-    #[test]
-    fn remove_prefix() {
-        let lower = "0xhello";
-        assert_eq!(hex_remove_prefix(lower).unwrap(), "hello");
-
-        let upper = "0Xhello";
-        assert_eq!(hex_remove_prefix(upper).unwrap(), "hello");
-
-        let err = "error";
-        assert!(hex_remove_prefix(err).is_err());
-    }
-
-    #[test]
-    fn check_unprefixed() {
-        let lower = "0xhello";
-        assert!(hex_check_unprefixed(lower).is_err());
-
-        let upper = "0Xhello";
-        assert!(hex_check_unprefixed(upper).is_err());
-
-        let valid = "hello";
-        assert_eq!(hex_check_unprefixed(valid).unwrap(), "hello");
-    }
-
     #[test]
     fn parse_u32_from_hex_prefixed() {
         let want = 171;
@@ -560,21 +504,6 @@ fn parse_u32_from_hex_no_prefix() {
         assert_eq!(got, want);
     }
 
-    #[test]
-    fn parse_hex_u32_prefixed() {
-        let want = 171; // 0xab
-        assert_eq!(hex_u32_prefixed("0xab").unwrap(), want);
-        assert!(hex_u32_unprefixed("0xab").is_err());
-
-    }
-
-    #[test]
-    fn parse_hex_u32_unprefixed() {
-        let want = 171; // 0xab
-        assert_eq!(hex_u32_unprefixed("ab").unwrap(), want);
-        assert!(hex_u32_prefixed("ab").is_err());
-    }
-
     #[test]
     fn parse_u128_from_hex_prefixed() {
         let want = 3_735_928_559;
@@ -589,21 +518,6 @@ fn parse_u128_from_hex_no_prefix() {
         assert_eq!(got, want);
     }
 
-    #[test]
-    fn parse_hex_u128_prefixed() {
-        let want = 3_735_928_559;
-        assert_eq!(hex_u128_prefixed("0xdeadbeef").unwrap(), want);
-        assert!(hex_u128_unprefixed("0xdeadbeef").is_err());
-
-    }
-
-    #[test]
-    fn parse_hex_u128_unprefixed() {
-        let want = 3_735_928_559;
-        assert_eq!(hex_u128_unprefixed("deadbeef").unwrap(), want);
-        assert!(hex_u128_prefixed("deadbeef").is_err());
-    }
-
     #[test]
     fn parse_u32_from_hex_unchecked_errors_on_prefix() {
         assert!(hex_u32_unchecked("0xab").is_err());
diff --git a/units/src/weight.rs b/units/src/weight.rs
index df654e9d..73bed18f 100644
--- a/units/src/weight.rs
+++ b/units/src/weight.rs
@@ -250,11 +250,6 @@ mod tests {
     const TWO: Weight = Weight(2);
     const FOUR: Weight = Weight(4);
 
-    #[test]
-    fn sanity_check() {
-        assert_eq!(Weight::MIN_TRANSACTION, Weight(240));
-    }
-
     #[test]
     fn from_kwu() {
         let got = Weight::from_kwu(1).unwrap();
@@ -307,8 +302,8 @@ fn from_non_witness_data_size() {
 
     #[test]
     fn to_kwu_floor() {
-        assert_eq!(Weight(5_000).to_kwu_floor(), 5);
-        assert_eq!(Weight(5_999).to_kwu_floor(), 5);
+        assert_eq!(Weight(1_000).to_kwu_floor(), 1);
+        assert_eq!(Weight(1_999).to_kwu_floor(), 1);
     }
 
     #[test]
@@ -319,8 +314,8 @@ fn to_kwu_ceil() {
 
     #[test]
     fn to_vb_floor() {
-        assert_eq!(Weight(8).to_vbytes_floor(), 2);
-        assert_eq!(Weight(9).to_vbytes_floor(), 2);
+        assert_eq!(Weight(4).to_vbytes_floor(), 1);
+        assert_eq!(Weight(5).to_vbytes_floor(), 1);
     }
 
     #[test]
@@ -379,33 +374,14 @@ fn addition() {
     #[test]
     #[allow(clippy::op_ref)]
     fn subtract() {
-        let ten = Weight(10);
-        let seven = Weight(7);
-        let three = Weight(3);
-
-        assert_eq!(ten - seven, three);
-        assert_eq!(&ten - seven, three);
-        assert_eq!(ten - &seven, three);
-        assert_eq!(&ten - &seven, three);
-    }
-
-    #[test]
-    #[allow(clippy::op_ref)]
-    fn multiply() {
+        let one = Weight(1);
         let two = Weight(2);
-        let six = Weight(6);
-
-        assert_eq!(3_u64 * two, six);
-        assert_eq!(two * 3_u64, six);
-    }
-
-    #[test]
-    fn divide() {
-        let eight = Weight(8);
-        let four = Weight(4);
+        let three = Weight(3);
 
-        assert_eq!(eight / four, 2_u64);
-        assert_eq!(eight / 4_u64, Weight(2));
+        assert!(three - two == one);
+        assert!(&three - two == one);
+        assert!(three - &two == one);
+        assert!(&three - &two == one);
     }
 
     #[test]
@@ -429,18 +405,4 @@ fn sub_assign() {
         f -= &Weight(2);
         assert_eq!(f, Weight(1));
     }
-
-    #[test]
-    fn mul_assign() {
-        let mut w = Weight(3);
-        w *= 2_u64;
-        assert_eq!(w, Weight(6));
-    }
-
-    #[test]
-    fn div_assign() {
-        let mut w = Weight(8);
-        w /= Weight(4).into();
-        assert_eq!(w, Weight(2));
-    }
 }


Test patch:
diff --git a/units/src/amount/tests.rs b/units/src/amount/tests.rs
index f6b88884..4f85f134 100644
--- a/units/src/amount/tests.rs
+++ b/units/src/amount/tests.rs
@@ -16,32 +16,6 @@
 #[cfg(feature = "alloc")]
 use crate::{FeeRate, Weight};
 
-#[test]
-fn sanity_check() {
-    assert_eq!(SignedAmount::from_sat(-100).abs(), SignedAmount::from_sat(100));
-    assert_eq!(SignedAmount::from_sat(i64::MIN + 1).checked_abs().unwrap(), SignedAmount::from_sat(i64::MAX));
-    assert_eq!(SignedAmount::from_sat(-100).signum(), -1);
-    assert_eq!(SignedAmount::from_sat(0).signum(), 0);
-    assert_eq!(SignedAmount::from_sat(100).signum(), 1);
-    assert_eq!(SignedAmount::from(Amount::from_sat(100)), SignedAmount::from_sat(100));
-    assert!(SignedAmount::from_sat(i64::MIN).checked_abs().is_none());
-    assert!(!SignedAmount::from_sat(-100).is_positive());
-    assert!(SignedAmount::from_sat(100).is_positive());
-
-    #[cfg(feature = "alloc")]
-    {
-        assert_eq!(Amount::from_float_in(0_f64, Denomination::Bitcoin).unwrap(), Amount::from_sat(0));
-        assert_eq!(Amount::from_float_in(2_f64, Denomination::Bitcoin).unwrap(), Amount::from_sat(200_000_000));
-        assert!(Amount::from_float_in(-100_f64, Denomination::Bitcoin).is_err());
-    }
-}
-
-#[test]
-fn check_if_num_is_too_precise() {
-    assert_eq!(is_too_precise("1234", 3).unwrap(), 3);
-    assert_eq!(is_too_precise("1234.1234", 3).unwrap(), 3);
-}
-
 #[test]
 #[cfg(feature = "alloc")]
 fn from_str_zero() {
@@ -120,27 +94,11 @@ fn mul_div() {
     assert_eq!(ssat(-14) / 2, ssat(-7));
     assert_eq!(ssat(-14) % 3, ssat(-2));
 
-    let mut a = sat(30);
-    a /= 3;
-    assert_eq!(a, sat(10));
-    a %= 3;
-    assert_eq!(a, sat(1));
-    a *= 3;
-    assert_eq!(a, Amount::from_sat(3));
-
     let mut b = ssat(30);
     b /= 3;
     assert_eq!(b, ssat(10));
     b %= 3;
     assert_eq!(b, ssat(1));
-    b *= 3;
-    assert_eq!(b, SignedAmount::from_sat(3));
-}
-
-#[test]
-fn neg() {
-    let amount = -SignedAmount::from_sat(2);
-    assert_eq!(amount.to_sat(), -2);
 }
 
 #[cfg(feature = "std")]
@@ -167,23 +125,6 @@ fn checked_arithmetic() {
     assert_eq!(ssat(-6).checked_div(2), Some(ssat(-3)));
 }
 
-#[test]
-#[allow(deprecated_in_future)]
-fn unchecked_arithmetic() {
-    assert_eq!(SignedAmount::from_sat(10).unchecked_add(SignedAmount::from_sat(20)), SignedAmount::from_sat(30));
-    assert_eq!(SignedAmount::from_sat(50).unchecked_sub(SignedAmount::from_sat(10)), SignedAmount::from_sat(40));
-    assert_eq!(Amount::from_sat(5).unchecked_add(Amount::from_sat(7)), Amount::from_sat(12));
-    assert_eq!(Amount::from_sat(10).unchecked_sub(Amount::from_sat(7)), Amount::from_sat(3));
-}
-
-#[test]
-fn positive_sub() {
-    assert_eq!(SignedAmount::from_sat(10).positive_sub(SignedAmount::from_sat(7)).unwrap(), SignedAmount::from_sat(3));
-    assert!(SignedAmount::from_sat(-10).positive_sub(SignedAmount::from_sat(7)).is_none());
-    assert!(SignedAmount::from_sat(10).positive_sub(SignedAmount::from_sat(-7)).is_none());
-    assert!(SignedAmount::from_sat(10).positive_sub(SignedAmount::from_sat(11)).is_none());
-}
-
 #[cfg(feature = "alloc")]
 #[test]
 fn amount_checked_div_by_weight_ceil() {
@@ -940,15 +881,11 @@ fn sum_amounts() {
     assert_eq!(SignedAmount::ZERO, [].iter().sum::<SignedAmount>());
 
     let amounts = [Amount::from_sat(42), Amount::from_sat(1337), Amount::from_sat(21)];
-    assert_eq!(amounts.iter().sum::<Amount>(), Amount::from_sat(1400));
-
     let sum = amounts.into_iter().sum::<Amount>();
     assert_eq!(Amount::from_sat(1400), sum);
 
     let amounts =
         [SignedAmount::from_sat(-42), SignedAmount::from_sat(1337), SignedAmount::from_sat(21)];
-    assert_eq!(amounts.iter().sum::<SignedAmount>(), SignedAmount::from_sat(1316));
-
     let sum = amounts.into_iter().sum::<SignedAmount>();
     assert_eq!(SignedAmount::from_sat(1316), sum);
 }