Repository: rust-bitcoin/rust-bitcoin
Pull request #4253 (2025-03-17 17:20:06)

primitives: Increase test coverage in LockTime
Increase the test coverage in `primitives/src/locktime/*`.

Modify existing tests and add new ones where required to increase the test coverage to 100% for all features excluding `arbitrary` and `serde`.

coveralls: ## Pull Request Test Coverage Report for [Build 13905768508](https://coveralls.io/builds/72768186)


### Details

* **80** of **80**   **(100.0%)**  changed or added relevant lines in **2** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.3%**) to **83.422%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/72768186/badge)](https://coveralls.io/builds/72768186) |
| :-- | --: |
| Change from base [Build 13887050957](https://coveralls.io/builds/72749373): |  0.3% |
| Covered Lines: | 21919 |
| Relevant Lines: | 26275 |

---
##### 💛  - [Coveralls](https://coveralls.io)

apoelstra: In e223cb5582666af20882f8bdad740b8236b602b0: there are still checks that the alternate display is non-empty without checking it more specifically.

Patch:
diff --git a/bitcoin/examples/sighash.rs b/bitcoin/examples/sighash.rs
index 0878ea43..e4bf819e 100644
--- a/bitcoin/examples/sighash.rs
+++ b/bitcoin/examples/sighash.rs
@@ -57,7 +57,7 @@ fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, amount: Amount) {
 /// # Parameters
 ///
 /// * `raw_tx` - spending tx hex
-/// * `inp_idx` - spending tx input index
+/// * `inp_idx` - spending tx input inde
 /// * `script_pubkey_bytes_opt` - Option with scriptPubKey bytes. If None, it's p2sh case, i.e., reftx output's scriptPubKey.type is "scripthash". In this case scriptPubkey is extracted from the spending transaction's scriptSig. If Some(), it's p2ms case, i.e., reftx output's scriptPubKey.type is "multisig", and the scriptPubkey is supplied from the referenced output.
 fn compute_sighash_legacy(raw_tx: &[u8], inp_idx: usize, script_pubkey_bytes_opt: Option<&[u8]>) {
     let tx: Transaction = consensus::deserialize(raw_tx).unwrap();
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index 36b6a553..a4588617 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -273,16 +273,6 @@ impl From<Network> for KnownHrp {
     fn from(n: Network) -> Self { Self::from_network(n) }
 }
 
-impl From<KnownHrp> for NetworkKind {
-    fn from(hrp: KnownHrp) -> Self {
-        match hrp {
-            KnownHrp::Mainnet => NetworkKind::Main,
-            KnownHrp::Testnets => NetworkKind::Test,
-            KnownHrp::Regtest => NetworkKind::Test,
-        }
-    }
-}
-
 /// The data encoded by an `Address`.
 ///
 /// This is the data used to encumber an output that pays to this address i.e., it is the address
@@ -460,16 +450,6 @@ pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
 
     /// Marks the network of this address as unchecked.
     pub fn into_unchecked(self) -> Address<NetworkUnchecked> { Address(self.0, PhantomData) }
-
-    /// Returns the [`NetworkKind`] of this address.
-    pub fn network_kind(&self) -> NetworkKind {
-        use AddressInner::*;
-        match self.0 {
-            P2pkh { hash: _, ref network } => *network,
-            P2sh { hash: _, ref network } => *network,
-            Segwit { program: _, ref hrp } => NetworkKind::from(*hrp),
-        }
-    }
 }
 
 /// Methods and functions that can be called only on `Address<NetworkChecked>`.
diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs
index 979dabc5..e602a313 100644
--- a/bitcoin/src/bip152.rs
+++ b/bitcoin/src/bip152.rs
@@ -413,7 +413,7 @@ mod test {
     use crate::merkle_tree::TxMerkleNode;
     use crate::transaction::OutPointExt;
     use crate::{
-        transaction, Amount, BlockChecked, BlockTime, CompactTarget, OutPoint, ScriptBuf, Sequence,
+        transaction, Amount, BlockChecked, CompactTarget, OutPoint, ScriptBuf, Sequence, BlockTime,
         TxIn, TxOut, Txid, Witness,
     };
 
diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs
index 6e8ef036..8d48609a 100644
--- a/bitcoin/src/blockdata/constants.rs
+++ b/bitcoin/src/blockdata/constants.rs
@@ -16,7 +16,7 @@
 use crate::pow::CompactTarget;
 use crate::transaction::{self, OutPoint, Transaction, TxIn, TxOut};
 use crate::witness::Witness;
-use crate::{script, Amount, BlockHash, BlockTime, Sequence, TestnetVersion};
+use crate::{script, Amount, BlockHash, Sequence, TestnetVersion, BlockTime};
 
 /// How many seconds between blocks we expect on average.
 pub const TARGET_BLOCK_SPACING: u32 = 600;
diff --git a/bitcoin/src/blockdata/mod.rs b/bitcoin/src/blockdata/mod.rs
index a6f39ba9..9916e56a 100644
--- a/bitcoin/src/blockdata/mod.rs
+++ b/bitcoin/src/blockdata/mod.rs
@@ -21,10 +21,10 @@
 
 /// Implements `FeeRate` and assoctiated features.
 pub mod fee_rate {
-    #[cfg(feature = "serde")]
-    pub use units::fee_rate::serde;
     /// Re-export everything from the [`units::fee_rate`] module.
     pub use units::fee_rate::FeeRate;
+    #[cfg(feature = "serde")]
+    pub use units::fee_rate::serde;
 }
 
 /// Provides absolute and relative locktimes.
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index 8b8b73e3..ccc82ca8 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -203,7 +203,7 @@ fn extend<T>(&mut self, iter: T)
 /// Pretends that this is a mutable reference to [`ScriptBuf`]'s internal buffer.
 ///
 /// In reality the backing `Vec<u8>` is swapped with an empty one and this is holding both the
-/// reference and the vec. The vec is put back when this drops so it also covers panics. (But not
+/// reference and the vec. The vec is put back when this drops so it also covers paics. (But not
 /// leaks, which is OK since we never leak.)
 pub(crate) struct ScriptBufAsVec<'a>(&'a mut ScriptBuf, Vec<u8>);
 
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 862ed1fc..4581298b 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -462,7 +462,7 @@ pub fn to_vec(self) -> Vec<u8> { self.inner[..].to_vec() }
     /// Deserializes a private key from a byte array.
     pub fn from_byte_array(
         data: [u8; 32],
-        network: impl Into<NetworkKind>,
+        network: impl Into<NetworkKind>
     ) -> Result<PrivateKey, secp256k1::Error> {
         Ok(PrivateKey::new(secp256k1::SecretKey::from_byte_array(&data)?, network))
     }
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 54560439..ffb70ba5 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -164,7 +164,9 @@
     blockdata::script::witness_program::{self, WitnessProgram},
     blockdata::script::witness_version::{self, WitnessVersion},
     // These modules also re-export all the respective `primitives` types.
-    blockdata::{block, constants, fee_rate, locktime, script, transaction, weight, witness},
+    blockdata::{
+        block, constants, fee_rate, locktime, script, transaction, weight, witness,
+    },
 };
 
 #[rustfmt::skip]
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index ae295a3b..bb59edb5 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -253,7 +253,7 @@ pub fn to_work(self) -> Work { Work(self.0.inverse()) }
     /// [max]: Target::max
     /// [target]: crate::block::HeaderExt::target
     pub fn difficulty(&self, params: impl AsRef<Params>) -> u128 {
-        // Panic here may be easier to debug than during the actual division.
+        // Panic here may be eaiser to debug than during the actual division.
         assert_ne!(self.0, U256::ZERO, "divide by zero");
 
         let max = params.as_ref().max_attainable_target;
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index ffba04ea..bfe52825 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -135,7 +135,7 @@ pub fn from_unsigned_tx(tx: Transaction) -> Result<Self, Error> {
     /// An alias for [`extract_tx_fee_rate_limit`].
     ///
     /// [`extract_tx_fee_rate_limit`]: Psbt::extract_tx_fee_rate_limit
-    #[allow(clippy::result_large_err)] // The PSBT returned in `SendingToomuch` is large.
+    #[allow(clippy::result_large_err)]  // The PSBT returned in `SendingToomuch` is large.
     pub fn extract_tx(self) -> Result<Transaction, ExtractTxError> {
         self.internal_extract_tx_with_fee_rate_limit(Self::DEFAULT_MAX_FEE_RATE)
     }
@@ -147,7 +147,7 @@ pub fn extract_tx(self) -> Result<Transaction, ExtractTxError> {
     /// [`ExtractTxError`] variants will contain either the [`Psbt`] itself or the [`Transaction`]
     /// that was extracted. These can be extracted from the Errors in order to recover.
     /// See the error documentation for info on the variants. In general, it covers large fees.
-    #[allow(clippy::result_large_err)] // The PSBT returned in `SendingToomuch` is large.
+    #[allow(clippy::result_large_err)]  // The PSBT returned in `SendingToomuch` is large.
     pub fn extract_tx_fee_rate_limit(self) -> Result<Transaction, ExtractTxError> {
         self.internal_extract_tx_with_fee_rate_limit(Self::DEFAULT_MAX_FEE_RATE)
     }
@@ -159,7 +159,7 @@ pub fn extract_tx_fee_rate_limit(self) -> Result<Transaction, ExtractTxError> {
     /// See [`extract_tx`].
     ///
     /// [`extract_tx`]: Psbt::extract_tx
-    #[allow(clippy::result_large_err)] // The PSBT returned in `SendingToomuch` is large.
+    #[allow(clippy::result_large_err)]  // The PSBT returned in `SendingToomuch` is large.
     pub fn extract_tx_with_fee_rate_limit(
         self,
         max_fee_rate: FeeRate,
@@ -187,7 +187,7 @@ fn internal_extract_tx(self) -> Transaction {
     }
 
     #[inline]
-    #[allow(clippy::result_large_err)] // The PSBT returned in `SendingToomuch` is large.
+    #[allow(clippy::result_large_err)]  // The PSBT returned in `SendingToomuch` is large.
     fn internal_extract_tx_with_fee_rate_limit(
         self,
         max_fee_rate: FeeRate,
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index 34086afa..89de08dc 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -260,8 +260,10 @@ fn serialize(&self) -> Vec<u8> { XOnlyPublicKey::serialize(self).to_vec() }
 
 impl Deserialize for XOnlyPublicKey {
     fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
-        XOnlyPublicKey::from_byte_array(bytes.try_into().map_err(|_| Error::InvalidXOnlyPublicKey)?)
-            .map_err(|_| Error::InvalidXOnlyPublicKey)
+        XOnlyPublicKey::from_byte_array(
+            bytes.try_into().map_err(|_| Error::InvalidXOnlyPublicKey)?,
+        )
+        .map_err(|_| Error::InvalidXOnlyPublicKey)
     }
 }
 
@@ -366,7 +368,7 @@ fn serialize(&self) -> Vec<u8> {
         for leaf_info in self.script_leaves() {
             // # Cast Safety:
             //
-            // TaprootMerkleBranch can only have len at most 128(TAPROOT_CONTROL_MAX_NODE_COUNT).
+            // TaprootMerkleBranch can only have len atmost 128(TAPROOT_CONTROL_MAX_NODE_COUNT).
             // safe to cast from usize to u8
             buf.push(leaf_info.merkle_branch().len() as u8);
             buf.push(leaf_info.version().to_consensus());
diff --git a/bitcoin/src/taproot/merkle_branch.rs b/bitcoin/src/taproot/merkle_branch.rs
index 5ad08cbc..33f0330f 100644
--- a/bitcoin/src/taproot/merkle_branch.rs
+++ b/bitcoin/src/taproot/merkle_branch.rs
@@ -52,7 +52,11 @@ pub fn decode(sl: &[u8]) -> Result<Self, TaprootError> {
         } else if node_hashes.len() > TAPROOT_CONTROL_MAX_NODE_COUNT {
             Err(InvalidMerkleTreeDepthError(sl.len() / TAPROOT_CONTROL_NODE_SIZE).into())
         } else {
-            let inner = node_hashes.iter().copied().map(TapNodeHash::from_byte_array).collect();
+            let inner = node_hashes
+                .iter()
+                .copied()
+                .map(TapNodeHash::from_byte_array)
+                .collect();
 
             Ok(TaprootMerkleBranch(inner))
         }
diff --git a/chacha20_poly1305/src/chacha20.rs b/chacha20_poly1305/src/chacha20.rs
index 0f332fba..bf001319 100644
--- a/chacha20_poly1305/src/chacha20.rs
+++ b/chacha20_poly1305/src/chacha20.rs
@@ -243,7 +243,7 @@ pub struct ChaCha20 {
     nonce: Nonce,
     /// Internal block index of keystream.
     block_count: u32,
-    /// Internal byte offset index of the block_count.
+    /// Interal byte offset index of the block_count.
     seek_offset_bytes: usize,
 }
 
diff --git a/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs b/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs
index 7b36cf69..043d1959 100644
--- a/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs
+++ b/fuzz/fuzz_targets/bitcoin/deserialize_psbt.rs
@@ -1,6 +1,7 @@
-use bitcoin_fuzz::fuzz_utils::consume_random_bytes;
 use honggfuzz::fuzz;
 
+use bitcoin_fuzz::fuzz_utils::consume_random_bytes;
+
 fn do_test(data: &[u8]) {
     let mut new_data = data;
     let bytes = consume_random_bytes(&mut new_data);
diff --git a/fuzz/fuzz_targets/bitcoin/deserialize_script.rs b/fuzz/fuzz_targets/bitcoin/deserialize_script.rs
index 41ef8049..0968b64f 100644
--- a/fuzz/fuzz_targets/bitcoin/deserialize_script.rs
+++ b/fuzz/fuzz_targets/bitcoin/deserialize_script.rs
@@ -2,9 +2,10 @@
 use bitcoin::consensus::encode;
 use bitcoin::script::{self, ScriptExt as _};
 use bitcoin::{FeeRate, Network};
-use bitcoin_fuzz::fuzz_utils::{consume_random_bytes, consume_u64};
 use honggfuzz::fuzz;
 
+use bitcoin_fuzz::fuzz_utils::{consume_random_bytes, consume_u64};
+
 fn do_test(data: &[u8]) {
     let mut new_data = data;
     let bytes = consume_random_bytes(&mut new_data);
diff --git a/hashes/src/ripemd160/crypto.rs b/hashes/src/ripemd160/crypto.rs
index 70e92f66..178a2f44 100644
--- a/hashes/src/ripemd160/crypto.rs
+++ b/hashes/src/ripemd160/crypto.rs
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: CC0-1.0
 
 use internals::slice::SliceExt;
-
 use super::{HashEngine, BLOCK_SIZE};
 
 #[cfg(feature = "small-hash")]
diff --git a/hashes/src/sha1/crypto.rs b/hashes/src/sha1/crypto.rs
index 70d04f50..9076c910 100644
--- a/hashes/src/sha1/crypto.rs
+++ b/hashes/src/sha1/crypto.rs
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: CC0-1.0
 
 use internals::slice::SliceExt;
-
 use super::{HashEngine, BLOCK_SIZE};
 
 impl HashEngine {
diff --git a/hashes/src/sha256/crypto.rs b/hashes/src/sha256/crypto.rs
index eea9ff7a..34e780d7 100644
--- a/hashes/src/sha256/crypto.rs
+++ b/hashes/src/sha256/crypto.rs
@@ -6,7 +6,6 @@
 use core::arch::x86_64::*;
 
 use internals::slice::SliceExt;
-
 use super::{HashEngine, Midstate, BLOCK_SIZE};
 
 #[allow(non_snake_case)]
diff --git a/hashes/src/sha256/mod.rs b/hashes/src/sha256/mod.rs
index a45eb0bf..ac11002c 100644
--- a/hashes/src/sha256/mod.rs
+++ b/hashes/src/sha256/mod.rs
@@ -9,7 +9,6 @@
 mod tests;
 
 use core::{cmp, convert, fmt};
-
 use internals::slice::SliceExt;
 
 use crate::{incomplete_block_len, sha256d, HashEngine as _};
@@ -81,8 +80,7 @@ pub const fn new() -> Self {
     /// Please see docs on [`Midstate`] before using this function.
     pub fn from_midstate(midstate: Midstate) -> HashEngine {
         let mut ret = [0; 8];
-        for (ret_val, midstate_bytes) in ret.iter_mut().zip(midstate.as_ref().bitcoin_as_chunks().0)
-        {
+        for (ret_val, midstate_bytes) in ret.iter_mut().zip(midstate.as_ref().bitcoin_as_chunks().0) {
             *ret_val = u32::from_be_bytes(*midstate_bytes);
         }
 
diff --git a/hashes/src/sha512/crypto.rs b/hashes/src/sha512/crypto.rs
index 6bf4f5ce..0a9d5b0d 100644
--- a/hashes/src/sha512/crypto.rs
+++ b/hashes/src/sha512/crypto.rs
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: CC0-1.0
 
 use internals::slice::SliceExt;
-
 use super::{HashEngine, BLOCK_SIZE};
 
 #[allow(non_snake_case)]
diff --git a/internals/src/slice.rs b/internals/src/slice.rs
index 4c32126d..38ac9587 100644
--- a/internals/src/slice.rs
+++ b/internals/src/slice.rs
@@ -23,9 +23,7 @@ pub trait SliceExt {
     /// let mut slice = [1, 2, 3];
     /// let fail = slice.as_chunks_mut::<0>();
     /// ```
-    fn bitcoin_as_chunks_mut<const N: usize>(
-        &mut self,
-    ) -> (&mut [[Self::Item; N]], &mut [Self::Item]);
+    fn bitcoin_as_chunks_mut<const N: usize>(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]);
 }
 
 impl<T> SliceExt for [T] {
@@ -42,15 +40,11 @@ impl<T> SliceExt for [T] {
         // we're merely casting, so no aliasing issues here
         // arrays of T have same alignment as T
         // the resulting slice points within the obtained slice as was computed above
-        let left = unsafe {
-            core::slice::from_raw_parts(left.as_ptr().cast::<[Self::Item; N]>(), chunks_count)
-        };
+        let left = unsafe { core::slice::from_raw_parts(left.as_ptr().cast::<[Self::Item; N]>(), chunks_count) };
         (left, right)
     }
 
-    fn bitcoin_as_chunks_mut<const N: usize>(
-        &mut self,
-    ) -> (&mut [[Self::Item; N]], &mut [Self::Item]) {
+    fn bitcoin_as_chunks_mut<const N: usize>(&mut self) -> (&mut [[Self::Item; N]], &mut [Self::Item]) {
         #[allow(clippy::let_unit_value)]
         let _ = Hack::<N>::IS_NONZERO;
 
@@ -61,12 +55,7 @@ fn bitcoin_as_chunks_mut<const N: usize>(
         // we're merely casting, so no aliasing issues here
         // arrays of T have same alignment as T
         // the resulting slice points within the obtained slice as was computed above
-        let left = unsafe {
-            core::slice::from_raw_parts_mut(
-                left.as_mut_ptr().cast::<[Self::Item; N]>(),
-                chunks_count,
-            )
-        };
+        let left = unsafe { core::slice::from_raw_parts_mut(left.as_mut_ptr().cast::<[Self::Item; N]>(), chunks_count) };
         (left, right)
     }
 }
@@ -74,9 +63,7 @@ fn bitcoin_as_chunks_mut<const N: usize>(
 struct Hack<const N: usize>;
 
 impl<const N: usize> Hack<N> {
-    const IS_NONZERO: () = {
-        assert!(N != 0);
-    };
+    const IS_NONZERO: () = { assert!(N != 0); };
 }
 
 #[cfg(test)]
diff --git a/primitives/src/locktime/absolute.rs b/primitives/src/locktime/absolute.rs
index 8d90cf25..ad03f510 100644
--- a/primitives/src/locktime/absolute.rs
+++ b/primitives/src/locktime/absolute.rs
@@ -405,12 +405,25 @@ mod tests {
 
     #[test]
     fn display_and_alternate() {
+        let lock_by_height = LockTime::from_height(741_521).unwrap();
+        let lock_by_time = LockTime::from_time(1_653_195_600).unwrap(); // May 22nd 2022, 5am UTC.
+
+        assert_eq!(format!("{}", lock_by_height), "741521");
+        assert_eq!(format!("{:#}", lock_by_height), "block-height 741521");
+        assert!(!format!("{:?}", lock_by_height).is_empty());
+
+        assert_eq!(format!("{}", lock_by_time), "1653195600");
+        assert_eq!(format!("{:#}", lock_by_time), "block-time 1653195600 (seconds since epoch)");
+        assert!(!format!("{:?}", lock_by_time).is_empty());
+    }
+
+    #[test]
+    fn roundtrips() {
         let lock_by_height = LockTime::from_consensus(741_521);
-        let s = format!("{}", lock_by_height);
-        assert_eq!(&s, "741521");
+        let lock_by_time = LockTime::from_consensus(1_653_195_600);
 
-        let got = format!("{:#}", lock_by_height);
-        assert_eq!(got, "block-height 741521");
+        assert_eq!(lock_by_height.to_consensus_u32(), 741_521);
+        assert_eq!(lock_by_time.to_consensus_u32(), 1_653_195_600);
     }
 
     #[test]
@@ -438,10 +451,20 @@ fn lock_time_from_unprefixed_hex_upper() {
     }
 
     #[test]
-    fn lock_time_from_invalid_hex_should_err() {
-        let hex = "0xzb93";
-        let result = LockTime::from_hex(hex);
-        assert!(result.is_err());
+    fn invalid_hex() {
+        assert!(LockTime::from_hex("0xzb93").is_err());
+        assert!(LockTime::from_unprefixed_hex("zb93").is_err());
+    }
+
+    #[test]
+    fn invalid_locktime_type() {
+        assert!(LockTime::from_height(499_999_999).is_ok()); // Below the threshold.
+        assert!(LockTime::from_height(500_000_000).is_err()); // The threshold.
+        assert!(LockTime::from_height(500_000_001).is_err()); // Above the threshold.
+
+        assert!(LockTime::from_time(499_999_999).is_err()); // Below the threshold.
+        assert!(LockTime::from_time(500_000_000).is_ok()); // The threshold.
+        assert!(LockTime::from_time(500_000_001).is_ok()); // Above the threshold.
     }
 
     #[test]
@@ -466,33 +489,33 @@ fn parses_correctly_to_height_or_time() {
 
     #[test]
     fn satisfied_by_height() {
-        let lock_by_height = LockTime::from_consensus(750_000);
+        let height_below = Height::from_consensus(700_000).unwrap();
+        let height = Height::from_consensus(750_000).unwrap();
+        let height_above = Height::from_consensus(800_000).unwrap();
 
-        let height_same = Height::from_consensus(750_000).expect("failed to parse height");
-        let height_above = Height::from_consensus(800_000).expect("failed to parse height");
-        let height_below = Height::from_consensus(700_000).expect("failed to parse height");
+        let lock_by_height = LockTime::from(height);
 
         let t: u32 = 1_653_195_600; // May 22nd, 5am UTC.
-        let time = Time::from_consensus(t).expect("invalid time value");
+        let time = Time::from_consensus(t).unwrap();
 
-        assert!(lock_by_height.is_satisfied_by(height_same, time));
-        assert!(lock_by_height.is_satisfied_by(height_above, time));
         assert!(!lock_by_height.is_satisfied_by(height_below, time));
+        assert!(lock_by_height.is_satisfied_by(height, time));
+        assert!(lock_by_height.is_satisfied_by(height_above, time));
     }
 
     #[test]
     fn satisfied_by_time() {
-        let lock_by_time = LockTime::from_consensus(1_653_195_600); // May 22nd 2022, 5am UTC.
+        let time_before = Time::from_consensus(1_653_109_200).unwrap(); // "May 21th 2022, 5am UTC.
+        let time = Time::from_consensus(1_653_195_600).unwrap(); // "May 22nd 2022, 5am UTC.
+        let time_after = Time::from_consensus(1_653_282_000).unwrap(); // "May 23rd 2022, 5am UTC.
 
-        let time_same = Time::from_consensus(1_653_195_600).expect("May 22nd 2022, 5am UTC");
-        let time_after = Time::from_consensus(1_653_282_000).expect("May 23rd 2022, 5am UTC");
-        let time_before = Time::from_consensus(1_653_109_200).expect("May 21th 2022, 5am UTC");
+        let lock_by_time = LockTime::from(time);
 
-        let height = Height::from_consensus(800_000).expect("failed to parse height");
+        let height = Height::from_consensus(800_000).unwrap();
 
-        assert!(lock_by_time.is_satisfied_by(height, time_same));
-        assert!(lock_by_time.is_satisfied_by(height, time_after));
         assert!(!lock_by_time.is_satisfied_by(height, time_before));
+        assert!(lock_by_time.is_satisfied_by(height, time));
+        assert!(lock_by_time.is_satisfied_by(height, time_after));
     }
 
     #[test]
diff --git a/primitives/src/locktime/relative.rs b/primitives/src/locktime/relative.rs
index 8c03a945..000adb07 100644
--- a/primitives/src/locktime/relative.rs
+++ b/primitives/src/locktime/relative.rs
@@ -470,6 +470,36 @@ impl std::error::Error for IncompatibleTimeError {}
 mod tests {
     use super::*;
 
+    #[test]
+    fn display_and_alternate() {
+        let lock_by_height = LockTime::from_height(10);
+        let lock_by_time = LockTime::from_512_second_intervals(70);
+
+        assert_eq!(format!("{}", lock_by_height), "10");
+        assert_eq!(format!("{:#}", lock_by_height), "block-height 10");
+        assert!(!format!("{:?}", lock_by_height).is_empty());
+
+        assert_eq!(format!("{}", lock_by_time), "70");
+        assert_eq!(format!("{:#}", lock_by_time), "block-time 70 (512 second intervals)");
+        assert!(!format!("{:?}", lock_by_time).is_empty());
+    }
+
+    #[test]
+    fn from_seconds_ceil_and_floor() {
+        let time = 70*512+1;
+        let lock_by_time = LockTime::from_seconds_ceil(time).unwrap();
+        assert_eq!(lock_by_time, LockTime::from_512_second_intervals(71));
+
+        let lock_by_time = LockTime::from_seconds_floor(time).unwrap();
+        assert_eq!(lock_by_time, LockTime::from_512_second_intervals(70));
+
+        let mut max_time = 0xffff * 512;
+        assert_eq!(LockTime::from_seconds_ceil(max_time),LockTime::from_seconds_floor(max_time));
+        max_time += 512;
+        assert!(LockTime::from_seconds_ceil(max_time).is_err());
+        assert!(LockTime::from_seconds_floor(max_time).is_err());
+    }
+
     #[test]
     fn parses_correctly_to_height_or_time() {
         let height1 = Height::from(10);
@@ -555,6 +585,10 @@ fn sequence_correctly_implies() {
 
         assert!(lock_by_time.is_implied_by_sequence(seq_time));
         assert!(!lock_by_time.is_implied_by_sequence(seq_height));
+
+        let disabled_sequence = Sequence::from_consensus(1 << 31);
+        assert!(!lock_by_height.is_implied_by_sequence(disabled_sequence));
+        assert!(!lock_by_time.is_implied_by_sequence(disabled_sequence));
     }
 
     #[test]
@@ -587,4 +621,37 @@ fn consensus_round_trip() {
             assert_eq!(LockTime::from_sequence(seq).unwrap().to_sequence(), seq);
         }
     }
+
+    #[test]
+    fn disabled_locktime_error() {
+        let disabled_sequence = Sequence::from_consensus(1 << 31);
+        let err = LockTime::try_from(disabled_sequence).unwrap_err();
+
+        assert_eq!(err.disabled_locktime_value(), 1 << 31);
+        assert!(!format!("{}", err).is_empty());
+    }
+
+    #[test]
+    fn incompatible_height_error() {
+        let height = Height::from(10);
+        let time = Time::from_512_second_intervals(70);
+        let lock_by_time = LockTime::from(time);
+        let err = lock_by_time.is_satisfied_by_height(height).unwrap_err();
+
+        assert_eq!(err.incompatible(), height);
+        assert_eq!(err.expected(), time);
+        assert!(!format!("{}", err).is_empty());
+    }
+
+    #[test]
+    fn incompatible_time_error() {
+        let height = Height::from(10);
+        let time = Time::from_512_second_intervals(70);
+        let lock_by_height = LockTime::from(height);
+        let err = lock_by_height.is_satisfied_by_time(time).unwrap_err();
+
+        assert_eq!(err.incompatible(), time);
+        assert_eq!(err.expected(), height);
+        assert!(!format!("{}", err).is_empty());
+    }
 }
diff --git a/primitives/src/opcodes.rs b/primitives/src/opcodes.rs
index 84384455..3a3bce70 100644
--- a/primitives/src/opcodes.rs
+++ b/primitives/src/opcodes.rs
@@ -6,7 +6,7 @@
 //! all of the opcodes for that language.
 
 #![allow(non_camel_case_types)]
-#![allow(dead_code)] // This module is private and is duplicated in `bitcoin`.
+#![allow(dead_code)]            // This module is private and is duplicated in `bitcoin`.
 
 use core::fmt;
 
diff --git a/primitives/src/transaction.rs b/primitives/src/transaction.rs
index 764ed68c..bb2f8152 100644
--- a/primitives/src/transaction.rs
+++ b/primitives/src/transaction.rs
@@ -670,10 +670,7 @@ fn transaction_functions() {
             witness: Witness::new(),
         };
 
-        let txout = TxOut {
-            value: Amount::from_sat(123_456_789).unwrap(),
-            script_pubkey: ScriptBuf::new(),
-        };
+        let txout = TxOut { value: Amount::from_sat(123_456_789).unwrap(), script_pubkey: ScriptBuf::new() };
 
         let tx_orig = Transaction {
             version: Version::ONE,
diff --git a/primitives/src/witness.rs b/primitives/src/witness.rs
index bf2a8f9f..d5cf2722 100644
--- a/primitives/src/witness.rs
+++ b/primitives/src/witness.rs
@@ -355,7 +355,8 @@ fn visit_seq<A: serde::de::SeqAccess<'de>>(
                 self,
                 mut a: A,
             ) -> Result<Self::Value, A::Error> {
-                use hex::{FromHex, HexToBytesError as E};
+                use hex::FromHex;
+                use hex::HexToBytesError as E;
                 use serde::de::{self, Unexpected};
 
                 let mut ret = match a.size_hint() {
@@ -365,17 +366,16 @@ fn visit_seq<A: serde::de::SeqAccess<'de>>(
 
                 while let Some(elem) = a.next_element::<String>()? {
                     let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e {
-                        E::InvalidChar(ref e) =>
-                            match core::char::from_u32(e.invalid_char().into()) {
-                                Some(c) => de::Error::invalid_value(
-                                    Unexpected::Char(c),
-                                    &"a valid hex character",
-                                ),
-                                None => de::Error::invalid_value(
-                                    Unexpected::Unsigned(e.invalid_char().into()),
-                                    &"a valid hex character",
-                                ),
-                            },
+                        E::InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) {
+                            Some(c) => de::Error::invalid_value(
+                                Unexpected::Char(c),
+                                &"a valid hex character",
+                            ),
+                            None => de::Error::invalid_value(
+                                Unexpected::Unsigned(e.invalid_char().into()),
+                                &"a valid hex character",
+                            ),
+                        },
                         E::OddLengthString(ref e) =>
                             de::Error::invalid_length(e.length(), &"an even length string"),
                     })?;
diff --git a/units/src/amount/mod.rs b/units/src/amount/mod.rs
index 98e2137a..9333b87e 100644
--- a/units/src/amount/mod.rs
+++ b/units/src/amount/mod.rs
@@ -115,7 +115,7 @@ fn precision(self) -> i8 {
             Denomination::MicroBitcoin => -2,
             Denomination::Bit => -2,
             Denomination::Satoshi => 0,
-            Denomination::_DoNotUse(infallible) => match infallible {},
+            Denomination::_DoNotUse(infallible) => match infallible {}
         }
     }
 
@@ -128,7 +128,7 @@ fn as_str(self) -> &'static str {
             Denomination::MicroBitcoin => "uBTC",
             Denomination::Bit => "bits",
             Denomination::Satoshi => "satoshi",
-            Denomination::_DoNotUse(infallible) => match infallible {},
+            Denomination::_DoNotUse(infallible) => match infallible {}
         }
     }
 


Test patch:
diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs
index 3f4d56ae..4443645b 100644
--- a/bitcoin/src/blockdata/script/tests.rs
+++ b/bitcoin/src/blockdata/script/tests.rs
@@ -8,7 +8,7 @@
 };
 use crate::consensus::encode::{deserialize, serialize};
 use crate::crypto::key::{PublicKey, XOnlyPublicKey};
-use crate::{opcodes, Amount, FeeRate};
+use crate::{Amount, FeeRate, opcodes};
 
 #[test]
 #[rustfmt::skip]
@@ -494,7 +494,7 @@ fn script_asm() {
     assert_eq!(ScriptBuf::from_hex("0047304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401004cf1552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae").unwrap().to_string(),
                "OP_0 OP_PUSHBYTES_71 304402202457e78cc1b7f50d0543863c27de75d07982bde8359b9e3316adec0aec165f2f02200203fd331c4e4a4a02f48cf1c291e2c0d6b2f7078a784b5b3649fca41f8794d401 OP_0 OP_PUSHDATA1 552103244e602b46755f24327142a0517288cebd159eccb6ccf41ea6edf1f601e9af952103bbbacc302d19d29dbfa62d23f37944ae19853cf260c745c2bea739c95328fcb721039227e83246bd51140fe93538b2301c9048be82ef2fb3c7fc5d78426ed6f609ad210229bf310c379b90033e2ecb07f77ecf9b8d59acb623ab7be25a0caed539e2e6472103703e2ed676936f10b3ce9149fa2d4a32060fb86fa9a70a4efe3f21d7ab90611921031e9b7c6022400a6bb0424bbcde14cff6c016b91ee3803926f3440abf5c146d05210334667f975f55a8455d515a2ef1c94fdfa3315f12319a14515d2a13d82831f62f57ae");
     // Various weird scripts found in transaction 6d7ed9914625c73c0288694a6819196a27ef6c08f98e1270d975a8e65a3dc09a
-    // which triggered overflow bugs on 32-bit machines in script formatting in the past.
+    // which triggerred overflow bugs on 32-bit machines in script formatting in the past.
     assert_eq!(ScriptBuf::from_hex("01").unwrap().to_string(), "OP_PUSHBYTES_1 <push past end>");
     assert_eq!(ScriptBuf::from_hex("0201").unwrap().to_string(), "OP_PUSHBYTES_2 <push past end>");
     assert_eq!(ScriptBuf::from_hex("4c").unwrap().to_string(), "<unexpected end>");
diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs
index 66ffc63b..dff93b0c 100644
--- a/bitcoin/tests/serde.rs
+++ b/bitcoin/tests/serde.rs
@@ -109,7 +109,8 @@ fn serde_regression_txin() {
 
 #[test]
 fn serde_regression_txout() {
-    let txout = TxOut { value: Amount::MAX, script_pubkey: ScriptBuf::from(vec![0u8, 1u8, 2u8]) };
+    let txout =
+        TxOut { value: Amount::MAX, script_pubkey: ScriptBuf::from(vec![0u8, 1u8, 2u8]) };
     let got = serialize(&txout).unwrap();
     let want = include_bytes!("data/serde/txout_bincode") as &[_];
     assert_eq!(got, want)
diff --git a/units/src/amount/tests.rs b/units/src/amount/tests.rs
index fedeb78c..b9d5518e 100644
--- a/units/src/amount/tests.rs
+++ b/units/src/amount/tests.rs
@@ -345,8 +345,14 @@ fn parsing() {
     assert_eq!(p("1.1", den_btc), Ok(sat(1_100_000_00)));
     assert_eq!(p("100", den_sat), Ok(sat(100)));
     assert_eq!(p("55", den_sat), Ok(sat(55)));
-    assert_eq!(p("2100000000000000", den_sat), Ok(sat(21_000_000__000_000_00)));
-    assert_eq!(p("2100000000000000.", den_sat), Ok(sat(21_000_000__000_000_00)));
+    assert_eq!(
+        p("2100000000000000", den_sat),
+        Ok(sat(21_000_000__000_000_00))
+    );
+    assert_eq!(
+        p("2100000000000000.", den_sat),
+        Ok(sat(21_000_000__000_000_00))
+    );
     assert_eq!(p("21000000", den_btc), Ok(sat(21_000_000__000_000_00)));
 
     // exactly 50 chars.
@@ -696,15 +702,28 @@ fn to_from_string_in() {
     assert_eq!(ua_str(&ua_sat(21_000_000).to_string_in(D::Bit), D::Bit), Ok(ua_sat(21_000_000)));
     assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
 
-    assert!(ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::Bitcoin), D::Bitcoin).is_ok());
-    assert!(ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::CentiBitcoin), D::CentiBitcoin)
-        .is_ok());
-    assert!(ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::MilliBitcoin), D::MilliBitcoin)
-        .is_ok());
-    assert!(ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::MicroBitcoin), D::MicroBitcoin)
-        .is_ok());
+    assert!(
+        ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::Bitcoin), D::Bitcoin).is_ok()
+    );
+    assert!(ua_str(
+        &ua_sat(Amount::MAX.to_sat()).to_string_in(D::CentiBitcoin),
+        D::CentiBitcoin
+    )
+    .is_ok());
+    assert!(ua_str(
+        &ua_sat(Amount::MAX.to_sat()).to_string_in(D::MilliBitcoin),
+        D::MilliBitcoin
+    )
+    .is_ok());
+    assert!(ua_str(
+        &ua_sat(Amount::MAX.to_sat()).to_string_in(D::MicroBitcoin),
+        D::MicroBitcoin
+    )
+    .is_ok());
     assert!(ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::Bit), D::Bit).is_ok());
-    assert!(ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::Satoshi), D::Satoshi).is_ok());
+    assert!(
+        ua_str(&ua_sat(Amount::MAX.to_sat()).to_string_in(D::Satoshi), D::Satoshi).is_ok()
+    );
 
     assert_eq!(
         sa_str(&SignedAmount::MAX.to_string_in(D::Satoshi), D::MicroBitcoin),
@@ -951,19 +970,11 @@ fn sum_amounts() {
     assert_eq!([].iter().sum::<NumOpResult<Amount>>(), Amount::ZERO.into());
     assert_eq!([].iter().sum::<NumOpResult<SignedAmount>>(), SignedAmount::ZERO.into());
 
-    let results =
-        [NumOpResult::Valid(sat(42)), NumOpResult::Valid(sat(1337)), NumOpResult::Valid(sat(21))];
+    let results = [NumOpResult::Valid(sat(42)), NumOpResult::Valid(sat(1337)), NumOpResult::Valid(sat(21))];
     assert_eq!(results.iter().sum::<NumOpResult<Amount>>(), NumOpResult::Valid(sat(1400)));
 
-    let signed_results = [
-        NumOpResult::Valid(ssat(42)),
-        NumOpResult::Valid(ssat(1337)),
-        NumOpResult::Valid(ssat(21)),
-    ];
-    assert_eq!(
-        signed_results.iter().sum::<NumOpResult<SignedAmount>>(),
-        NumOpResult::Valid(ssat(1400))
-    );
+    let signed_results = [NumOpResult::Valid(ssat(42)), NumOpResult::Valid(ssat(1337)), NumOpResult::Valid(ssat(21))];
+    assert_eq!(signed_results.iter().sum::<NumOpResult<SignedAmount>>(), NumOpResult::Valid(ssat(1400)));
 
     let amounts = [sat(42), sat(1337), sat(21)];
     assert_eq!(
@@ -1229,7 +1240,9 @@ fn unsigned_amount_div_by_amount() {
 
 #[test]
 #[should_panic(expected = "attempt to divide by zero")]
-fn unsigned_amount_div_by_amount_zero() { let _ = sat(1897) / Amount::ZERO; }
+fn unsigned_amount_div_by_amount_zero() {
+    let _ = sat(1897) / Amount::ZERO;
+}
 
 #[test]
 fn signed_amount_div_by_amount() {
@@ -1243,7 +1256,9 @@ fn signed_amount_div_by_amount() {
 
 #[test]
 #[should_panic(expected = "attempt to divide by zero")]
-fn signed_amount_div_by_amount_zero() { let _ = ssat(1897) / SignedAmount::ZERO; }
+fn signed_amount_div_by_amount_zero() {
+    let _ = ssat(1897) / SignedAmount::ZERO;
+}
 
 #[test]
 fn check_const() {
diff --git a/units/tests/api.rs b/units/tests/api.rs
index e3b89fed..b8ec96c3 100644
--- a/units/tests/api.rs
+++ b/units/tests/api.rs
@@ -14,8 +14,8 @@
 // These imports test "typical" usage by user code.
 use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::Height`.
 use bitcoin_units::{
-    amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval,
-    BlockTime, FeeRate, SignedAmount, Weight,
+    amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval, FeeRate,
+    SignedAmount, BlockTime, Weight,
 };
 
 /// A struct that includes all public non-error enums.
@@ -147,7 +147,7 @@ fn api_can_use_modules_from_crate_root() {
 #[test]
 fn api_can_use_types_from_crate_root() {
     use bitcoin_units::{
-        Amount, BlockHeight, BlockInterval, BlockTime, FeeRate, SignedAmount, Weight,
+        Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, BlockTime, Weight,
     };
 }