Repository: rust-bitcoin/rust-bitcoin
Pull request #2772 (2024-05-15 10:34:06)

Removed //! spare line at end of headers
Some of the headers had a //! at the end but most didn't.  They have all been removed in bitcoin/src/ to make the files consistent

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


### Details

* **0** of **0**   changed or added relevant lines in **0** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage remained the same at **83.191%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/67500742/badge)](https://coveralls.io/builds/67500742) |
| :-- | --: |
| Change from base [Build 9088761779](https://coveralls.io/builds/67493650): |  0.0% |
| Covered Lines: | 19168 |
| Relevant Lines: | 23041 |

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

tcharding: To get rid of the first patch you likely want to use `git rebase -i master` then kill the first patch (how you do that will depend on how you have git configured to work with your editor). Then when you only have a single patch on top of master branch you can run `git push -f` to force push the changes to github.

Patch:
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
deleted file mode 100644
index b8896a95..00000000
--- a/.github/workflows/README.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# rust-bitcoin workflow notes
-
-We are attempting to run max 20 parallel jobs using GitHub actions (usage limit for free tier).
-
-ref: https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration
-
-The minimal/recent lock files are handled by CI (`rust.yml`).
-
-## Jobs
-
-Run from `rust.yml` unless stated otherwise. Total 21 jobs but
-`Prepare` is quick and must be run first anyway.
-
-0.  `Prepare`
-1.  `Stable - minimal`
-2.  `Stable - recent`
-3.  `Nightly - minimal`
-4.  `Nightly - recent`
-5.  `MSRV - minimal`
-6.  `MSRV - recent`
-7.  `Lint`
-8.  `Docs`
-9.  `Docsrs`
-10. `Bench`
-11. `ASAN`
-12. `WASM`
-13. `schemars`
-14. `Arch32bit`
-15. `Cross`
-16. `Embedded`
-17. `Kani`
-18. `Coveralls` - run by `coveralls.yml`
-19. `release` - run by `release.yml`
-20. `labeler` - run by `manage-pr.yml`
diff --git a/.github/workflows/cron-semi-weekly-update-nightly.yml b/.github/workflows/cron-daily-update-nightly.yml
similarity index 96%
rename from .github/workflows/cron-semi-weekly-update-nightly.yml
rename to .github/workflows/cron-daily-update-nightly.yml
index 51f7afa2..467abc07 100644
--- a/.github/workflows/cron-semi-weekly-update-nightly.yml
+++ b/.github/workflows/cron-daily-update-nightly.yml
@@ -1,7 +1,7 @@
 name: Update Nightly rustc
 on:
   schedule:
-    - cron: "0 0 * * 1,4" # runs every Monday and Thursday at 00:00
+    - cron: "0 0 * * *" # runs daily at 00:00
   workflow_dispatch: # allows manual triggering
 jobs:
   format:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 7f778e8f..da20ed1d 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -1,5 +1,4 @@
----   # rust-bitcoin CI: If you edit this file please update README.md
-on:   # yamllint disable-line rule:truthy
+on:
   push:
     branches:
       - master
@@ -30,18 +29,12 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh stable
+        run: ./contrib/run_task.sh stable
 
   Nightly:                      # 2 jobs, one per manifest.
     name: Test - nightly toolchain
@@ -54,12 +47,6 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@v1
         with:
@@ -67,7 +54,7 @@ jobs:
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh nightly
+        run: ./contrib/run_task.sh nightly
 
   MSRV:                         # 2 jobs, one per manifest.
     name: Test - 1.56.1 toolchain
@@ -79,20 +66,14 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
         with:
           toolchain: "1.56.1"
-      - name: "Set dependencies"
+      - name: "Copy lock file"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh msrv
+        run: ./contrib/run_task.sh msrv
 
   Lint:
     name: Lint - nightly toolchain
@@ -105,12 +86,6 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@v1
         with:
@@ -120,7 +95,7 @@ jobs:
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh lint
+        run: ./contrib/run_task.sh lint
 
   Docs:
     name: Docs - stable toolchain
@@ -132,18 +107,12 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
-      - name: "Set dependencies"
+      - name: "Copy lock file"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh docs
+        run: ./contrib/run_task.sh docs
 
   Docsrs:
     name: Docs - nightly toolchain
@@ -156,12 +125,6 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@v1
         with:
@@ -169,7 +132,7 @@ jobs:
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh docsrs
+        run: ./contrib/run_task.sh docsrs
 
   Bench:
     name: Bench - nightly toolchain
@@ -182,20 +145,14 @@ jobs:
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
-      - name: "Checkout maintainer tools"
-        uses: actions/checkout@v4
-        with:
-          repository: rust-bitcoin/rust-bitcoin-maintainer-tools
-          rev: b2ac115
-          path: maintainer-tools
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@v1
         with:
           toolchain: ${{ needs.Prepare.outputs.nightly_version }}
-      - name: "Set dependencies"
+      - name: "Copy lock file"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./maintainer-tools/ci/run_task.sh bench
+        run: ./contrib/run_task.sh bench
 
   Arch32bit:
     name: Test 32-bit version
@@ -256,7 +213,7 @@ jobs:
       - name: "Run hashes/embedded with alloc"
         run: cd hashes/embedded && cargo run --target thumbv7m-none-eabi --features=alloc
 
-  ASAN:                         # hashes crate only.
+  ASAN: # hashes crate only.
     name: ASAN - nightly toolchain
     needs: Prepare
     runs-on: ubuntu-latest
@@ -273,12 +230,12 @@ jobs:
           toolchain: ${{ needs.Prepare.outputs.nightly_version }}
       - name: Install src
         run: rustup component add rust-src
-      - name: "Set dependencies"
+      - name: "Copy lock file"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run sanitizer script"
-        run: cd ./hashes && ./contrib/sanitizer.sh
+      - name: "Run test script"
+        run: ./contrib/run_task.sh asan
 
-  WASM:                         # hashes crate only.
+  WASM: # hashes crate only.
     name: WASM - stable toolchain
     runs-on: ubuntu-latest
     strategy:
@@ -289,22 +246,25 @@ jobs:
         uses: actions/checkout@v4
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
-      - name: "Run wasm script"
-        run: cd hashes && ./contrib/wasm.sh
+      - name: "Run test script"
+        run: ./contrib/run_task.sh wasm
 
-  Schemars:                     # hashes crate only.
+  Schemars: # hashes crate only.
     name: Schemars - stable toolchain
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
-      # Note we do not use the recent lock file for schemars testing.
+      matrix:
+        dep: [recent]
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
-      - name: "Run schemars test"
-        run: cd hashes/extended_tests/schemars && cargo test
+      - name: "Copy lock file"
+        run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
+      - name: "Run test script"
+        run: ./contrib/run_task.sh schemars
 
   Kani:
     name: Kani codegen - stable toolchain
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
deleted file mode 100644
index 8577c050..00000000
--- a/.github/workflows/shellcheck.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: Shellcheck
-on:
-  pull_request:
-    branches:
-      - master
-jobs:
-  shellcheck:
-    name: Shellcheck
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v4
-    - name: Run ShellCheck
-      uses: ludeeus/action-shellcheck@2.0.0
-      env:
-        SHELLCHECK_OPTS: -x # allow outside sources
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index cbc17e22..f30294e9 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -47,7 +47,7 @@ dependencies = [
 
 [[package]]
 name = "bitcoin"
-version = "0.33.0-unreleased"
+version = "0.32.0-rc1"
 dependencies = [
  "base58ck",
  "base64",
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 2bd141b0..57490f67 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -46,7 +46,7 @@ dependencies = [
 
 [[package]]
 name = "bitcoin"
-version = "0.33.0-unreleased"
+version = "0.32.0-rc1"
 dependencies = [
  "base58ck",
  "base64",
diff --git a/base58/src/lib.rs b/base58/src/lib.rs
index 05544c43..9533365e 100644
--- a/base58/src/lib.rs
+++ b/base58/src/lib.rs
@@ -19,9 +19,6 @@
 #[macro_use]
 extern crate alloc;
 
-#[cfg(bench)]
-extern crate test;
-
 #[cfg(feature = "std")]
 extern crate std;
 
@@ -31,7 +28,7 @@
 
 #[cfg(not(feature = "std"))]
 pub use alloc::{string::String, vec::Vec};
-use core::{fmt, str};
+use core::{fmt, iter, slice, str};
 #[cfg(feature = "std")]
 pub use std::{string::String, vec::Vec};
 
@@ -151,7 +148,7 @@ fn format_iter<I, W>(writer: &mut W, data: I) -> Result<(), fmt::Error>
     I: Iterator<Item = u8> + Clone,
     W: fmt::Write,
 {
-    let mut ret = Vec::with_capacity(128);
+    let mut ret = SmallVec::new();
 
     let mut leading_zero_count = 0;
     let mut leading_zeroes = true;
@@ -176,7 +173,9 @@ fn format_iter<I, W>(writer: &mut W, data: I) -> Result<(), fmt::Error>
     }
 
     // ... then reverse it and convert to chars
-    ret.resize(ret.len() + leading_zero_count, 0);
+    for _ in 0..leading_zero_count {
+        ret.push(0);
+    }
 
     for ch in ret.iter().rev() {
         writer.write_char(BASE58_CHARS[*ch as usize] as char)?;
@@ -185,6 +184,37 @@ fn format_iter<I, W>(writer: &mut W, data: I) -> Result<(), fmt::Error>
     Ok(())
 }
 
+/// Vector-like object that holds the first 100 elements on the stack. If more space is needed it
+/// will be allocated on the heap.
+struct SmallVec<T> {
+    len: usize,
+    stack: [T; 100],
+    heap: Vec<T>,
+}
+
+impl<T: Default + Copy> SmallVec<T> {
+    fn new() -> SmallVec<T> { SmallVec { len: 0, stack: [T::default(); 100], heap: Vec::new() } }
+
+    fn push(&mut self, val: T) {
+        if self.len < 100 {
+            self.stack[self.len] = val;
+            self.len += 1;
+        } else {
+            self.heap.push(val);
+        }
+    }
+
+    fn iter(&self) -> iter::Chain<slice::Iter<T>, slice::Iter<T>> {
+        // If len<100 then we just append an empty vec
+        self.stack[0..self.len].iter().chain(self.heap.iter())
+    }
+
+    fn iter_mut(&mut self) -> iter::Chain<slice::IterMut<T>, slice::IterMut<T>> {
+        // If len<100 then we just append an empty vec
+        self.stack[0..self.len].iter_mut().chain(self.heap.iter_mut())
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use hex::test_hex_unwrap as hex;
@@ -254,28 +284,3 @@ fn test_base58_roundtrip() {
         assert_eq!(decode_check(&encode(&[1, 2, 3])), Err(TooShortError { length: 3 }.into()));
     }
 }
-
-#[cfg(bench)]
-mod benches {
-    use test::{black_box, Bencher};
-
-    #[bench]
-    pub fn bench_encode_check_50(bh: &mut Bencher) {
-        let data: alloc::vec::Vec<_> = (0u8..50).collect();
-
-        bh.iter(|| {
-            let r = super::encode_check(&data);
-            black_box(&r);
-        });
-    }
-
-    #[bench]
-    pub fn bench_encode_check_xpub(bh: &mut Bencher) {
-        let data: alloc::vec::Vec<_> = (0u8..78).collect(); // lenght of xpub
-
-        bh.iter(|| {
-            let r = super::encode_check(&data);
-            black_box(&r);
-        });
-    }
-}
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 1f02b287..6202839e 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "bitcoin"
-version = "0.33.0-unreleased"
+version = "0.32.0-rc1"
 authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
 license = "CC0-1.0"
 repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
diff --git a/bitcoin/embedded/scripts/env.sh b/bitcoin/embedded/scripts/env.sh
index 798c4af8..125162b6 100644
--- a/bitcoin/embedded/scripts/env.sh
+++ b/bitcoin/embedded/scripts/env.sh
@@ -1,4 +1,2 @@
-# we don't want shebangs in env.sh, disable shellcheck warning
-# shellcheck disable=SC2148
 export RUSTFLAGS="-C link-arg=-Tlink.x"
 export CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER="qemu-system-arm -cpu cortex-m3 -machine mps2-an385 -nographic -semihosting-config enable=on,target=native -kernel"
diff --git a/bitcoin/examples/bip32.rs b/bitcoin/examples/bip32.rs
index 8b15ba52..901793ed 100644
--- a/bitcoin/examples/bip32.rs
+++ b/bitcoin/examples/bip32.rs
@@ -41,7 +41,7 @@ fn main() {
 
     // derive child xpub
     let path = DerivationPath::from_str("84h/0h/0h").unwrap();
-    let child = root.derive_priv(&secp, &path);
+    let child = root.derive_priv(&secp, &path).unwrap();
     println!("Child at {}: {}", path, child);
     let xpub = Xpub::from_priv(&secp, &child);
     println!("Public key at {}: {}", path, xpub);
diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs
index 93631fcc..4de4cf66 100644
--- a/bitcoin/examples/ecdsa-psbt.rs
+++ b/bitcoin/examples/ecdsa-psbt.rs
@@ -116,11 +116,11 @@ fn new<C: Signing>(secp: &Secp256k1<C>, xpriv: &str) -> Result<ExportData> {
         // Hardened children require secret data to derive.
 
         let path = "84h/0h/0h".into_derivation_path()?;
-        let account_0_xpriv = master_xpriv.derive_priv(secp, &path);
+        let account_0_xpriv = master_xpriv.derive_priv(secp, &path)?;
         let account_0_xpub = Xpub::from_priv(secp, &account_0_xpriv);
 
         let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?;
-        let input_xpriv = master_xpriv.derive_priv(secp, &path);
+        let input_xpriv = master_xpriv.derive_priv(secp, &path)?;
         let input_xpub = Xpub::from_priv(secp, &input_xpriv);
 
         let wallet = ColdStorage { master_xpriv, master_xpub };
@@ -171,7 +171,7 @@ fn new(account_0_xpub: Xpub, input_xpub: Xpub, master_fingerprint: Fingerprint)
         WatchOnly { account_0_xpub, input_xpub, master_fingerprint }
     }
 
-    /// Creates the PSBT, in BIP174 parlance this is the 'Creator'.
+    /// Creates the PSBT, in BIP174 parlance this is the 'Creater'.
     fn create_psbt<C: Verification>(&self, secp: &Secp256k1<C>) -> Result<Psbt> {
         let to_address = Address::from_str(RECEIVE_ADDRESS)?.require_network(Network::Regtest)?;
         let to_amount = Amount::from_str(OUTPUT_AMOUNT_BTC)?;
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 1b6d59c1..b5db05ab 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -296,7 +296,7 @@ fn generate_bip86_key_spend_tx(
                 .get(&input.tap_internal_key.ok_or("Internal key missing in PSBT")?)
                 .ok_or("Missing taproot key origin")?;
 
-            let secret_key = master_xpriv.derive_priv(secp, &derivation_path).to_priv().inner;
+            let secret_key = master_xpriv.derive_priv(secp, &derivation_path)?.to_priv().inner;
             sign_psbt_taproot(
                 &secret_key,
                 input.tap_internal_key.unwrap(),
@@ -391,7 +391,7 @@ fn create_inheritance_funding_tx(
         // that we use an unhardened path so we can make use of xpubs.
         let derivation_path = DerivationPath::from_str(&format!("101/1/0/0/{}", self.next))?;
         let internal_keypair =
-            self.master_xpriv.derive_priv(&self.secp, &derivation_path).to_keypair(&self.secp);
+            self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_keypair(&self.secp);
         let beneficiary_key =
             self.beneficiary_xpub.derive_pub(&self.secp, &derivation_path)?.to_x_only_pub();
 
@@ -481,7 +481,7 @@ fn refresh_tx(
                 DerivationPath::from_str(&format!("101/1/0/0/{}", self.next))?;
             let new_internal_keypair = self
                 .master_xpriv
-                .derive_priv(&self.secp, &new_derivation_path)
+                .derive_priv(&self.secp, &new_derivation_path)?
                 .to_keypair(&self.secp);
             let beneficiary_key =
                 self.beneficiary_xpub.derive_pub(&self.secp, &new_derivation_path)?.to_x_only_pub();
@@ -530,7 +530,7 @@ fn refresh_tx(
                     .get(&input.tap_internal_key.ok_or("Internal key missing in PSBT")?)
                     .ok_or("Missing taproot key origin")?;
                 let secret_key =
-                    self.master_xpriv.derive_priv(&self.secp, &derivation_path).to_priv().inner;
+                    self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
                 sign_psbt_taproot(
                     &secret_key,
                     spend_info.internal_key(),
@@ -651,7 +651,7 @@ fn spend_inheritance(
             &psbt.inputs[0].tap_key_origins.clone()
         {
             let secret_key =
-                self.master_xpriv.derive_priv(&self.secp, &derivation_path).to_priv().inner;
+                self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
             for lh in leaf_hashes {
                 let sighash_type = TapSighashType::All;
                 let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(
diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs
index 2d2c0fea..38c91304 100644
--- a/bitcoin/src/bip152.rs
+++ b/bitcoin/src/bip152.rs
@@ -3,7 +3,6 @@
 //! BIP152 Compact Blocks
 //!
 //! Implementation of compact blocks data structure and algorithms.
-//!
 
 use core::{convert, fmt, mem};
 #[cfg(feature = "std")]
diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs
index 66bef777..a820e997 100644
--- a/bitcoin/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -36,7 +36,6 @@
 //!   // get this block
 //! }
 //!  ```
-//!
 
 use core::cmp::{self, Ordering};
 use core::fmt::{self, Display, Formatter};
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index dd685507..716ea498 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -4,7 +4,6 @@
 //!
 //! Implementation of BIP32 hierarchical deterministic wallets, as defined
 //! at <https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki>.
-//!
 
 use core::ops::Index;
 use core::str::FromStr;
@@ -30,12 +29,12 @@
 const VERSION_BYTES_TESTNETS_PRIVATE: [u8; 4] = [0x04, 0x35, 0x83, 0x94];
 
 /// The old name for xpub, extended public key.
-#[deprecated(since = "0.31.0", note = "use Xpub instead")]
-pub type ExtendedPubKey = Xpub;
+#[deprecated(since = "0.31.0", note = "use xpub instead")]
+pub type ExtendendPubKey = Xpub;
 
 /// The old name for xpriv, extended public key.
-#[deprecated(since = "0.31.0", note = "use Xpriv instead")]
-pub type ExtendedPrivKey = Xpriv;
+#[deprecated(since = "0.31.0", note = "use xpriv instead")]
+pub type ExtendendPrivKey = Xpriv;
 
 /// A chain code
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -45,7 +44,7 @@
 
 impl ChainCode {
     fn from_hmac(hmac: Hmac<sha512::Hash>) -> Self {
-        hmac.as_ref()[32..].try_into().expect("half of hmac is guaranteed to be 32 bytes")
+        hmac[32..].try_into().expect("half of hmac is guaranteed to be 32 bytes")
     }
 }
 
@@ -566,7 +565,7 @@ pub fn new_master(network: impl Into<NetworkKind>, seed: &[u8]) -> Result<Xpriv,
             depth: 0,
             parent_fingerprint: Default::default(),
             child_number: ChildNumber::from_normal_idx(0)?,
-            private_key: secp256k1::SecretKey::from_slice(&hmac_result.as_ref()[..32])?,
+            private_key: secp256k1::SecretKey::from_slice(&hmac_result[..32])?,
             chain_code: ChainCode::from_hmac(hmac_result),
         })
     }
@@ -583,23 +582,27 @@ pub fn to_keypair<C: secp256k1::Signing>(self, secp: &Secp256k1<C>) -> Keypair {
             .expect("BIP32 internal private key representation is broken")
     }
 
-    /// Derives an extended private key from a path.
+    /// Attempts to derive an extended private key from a path.
     ///
     /// The `path` argument can be both of type `DerivationPath` or `Vec<ChildNumber>`.
     pub fn derive_priv<C: secp256k1::Signing, P: AsRef<[ChildNumber]>>(
         &self,
         secp: &Secp256k1<C>,
         path: &P,
-    ) -> Xpriv {
+    ) -> Result<Xpriv, Error> {
         let mut sk: Xpriv = *self;
         for cnum in path.as_ref() {
-            sk = sk.ckd_priv(secp, *cnum)
+            sk = sk.ckd_priv(secp, *cnum)?;
         }
-        sk
+        Ok(sk)
     }
 
     /// Private->Private child key derivation
-    fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Xpriv {
+    fn ckd_priv<C: secp256k1::Signing>(
+        &self,
+        secp: &Secp256k1<C>,
+        i: ChildNumber,
+    ) -> Result<Xpriv, Error> {
         let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
         match i {
             ChildNumber::Normal { .. } => {
@@ -617,19 +620,19 @@ fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -
 
         hmac_engine.input(&u32::from(i).to_be_bytes());
         let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
-        let sk = secp256k1::SecretKey::from_slice(&hmac_result.as_ref()[..32])
+        let sk = secp256k1::SecretKey::from_slice(&hmac_result[..32])
             .expect("statistically impossible to hit");
         let tweaked =
             sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit");
 
-        Xpriv {
+        Ok(Xpriv {
             network: self.network,
             depth: self.depth + 1,
             parent_fingerprint: self.fingerprint(secp),
             child_number: i,
             private_key: tweaked,
             chain_code: ChainCode::from_hmac(hmac_result),
-        }
+        })
     }
 
     /// Decoding extended private key from binary data according to BIP 32
@@ -738,7 +741,7 @@ pub fn ckd_pub_tweak(
 
                 let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
 
-                let private_key = secp256k1::SecretKey::from_slice(&hmac_result.as_ref()[..32])?;
+                let private_key = secp256k1::SecretKey::from_slice(&hmac_result[..32])?;
                 let chain_code = ChainCode::from_hmac(hmac_result);
                 Ok((private_key, chain_code))
             }
@@ -997,7 +1000,7 @@ fn test_path<C: secp256k1::Signing + secp256k1::Verification>(
         let mut pk = Xpub::from_priv(secp, &sk);
 
         // Check derivation convenience method for Xpriv
-        assert_eq!(&sk.derive_priv(secp, &path).to_string()[..], expected_sk);
+        assert_eq!(&sk.derive_priv(secp, &path).unwrap().to_string()[..], expected_sk);
 
         // Check derivation convenience method for Xpub, should error
         // appropriately if any ChildNumber is hardened
@@ -1009,7 +1012,7 @@ fn test_path<C: secp256k1::Signing + secp256k1::Verification>(
 
         // Derive keys, checking hardened and non-hardened derivation one-by-one
         for &num in path.0.iter() {
-            sk = sk.ckd_priv(secp, num);
+            sk = sk.ckd_priv(secp, num).unwrap();
             match num {
                 Normal { .. } => {
                     let pk2 = pk.ckd_pub(secp, num).unwrap();
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index ebdbfa34..7be62e47 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -6,7 +6,6 @@
 //! which commits to an earlier block to form the blockchain. This
 //! module describes structures and functions needed to describe
 //! these blocks and the blockchain.
-//!
 
 use core::fmt;
 
diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs
index 0b01831f..dd0466bf 100644
--- a/bitcoin/src/blockdata/constants.rs
+++ b/bitcoin/src/blockdata/constants.rs
@@ -5,7 +5,6 @@
 //! This module provides various constants relating to the blockchain and
 //! consensus code. In particular, it defines the genesis block and its
 //! single transaction.
-//!
 
 use hashes::{sha256d, Hash};
 use hex_lit::hex;
diff --git a/bitcoin/src/blockdata/locktime/absolute.rs b/bitcoin/src/blockdata/locktime/absolute.rs
index 70d920f9..548bf8e3 100644
--- a/bitcoin/src/blockdata/locktime/absolute.rs
+++ b/bitcoin/src/blockdata/locktime/absolute.rs
@@ -4,7 +4,6 @@
 //!
 //! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
 //! whether `LockTime < LOCKTIME_THRESHOLD`.
-//!
 
 use core::cmp::Ordering;
 use core::fmt;
diff --git a/bitcoin/src/blockdata/locktime/mod.rs b/bitcoin/src/blockdata/locktime/mod.rs
index c09d3323..4cf62ad1 100644
--- a/bitcoin/src/blockdata/locktime/mod.rs
+++ b/bitcoin/src/blockdata/locktime/mod.rs
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: CC0-1.0
 
 //! Provides absolute and relative locktimes.
-//!
 
 pub mod absolute;
 pub mod relative;
diff --git a/bitcoin/src/blockdata/locktime/relative.rs b/bitcoin/src/blockdata/locktime/relative.rs
index 32abf968..c7cd6124 100644
--- a/bitcoin/src/blockdata/locktime/relative.rs
+++ b/bitcoin/src/blockdata/locktime/relative.rs
@@ -4,7 +4,6 @@
 //!
 //! There are two types of lock time: lock-by-blockheight and lock-by-blocktime, distinguished by
 //! whether bit 22 of the `u32` consensus value is set.
-//!
 
 #[cfg(feature = "ordered")]
 use core::cmp::Ordering;
diff --git a/bitcoin/src/blockdata/mod.rs b/bitcoin/src/blockdata/mod.rs
index 1d7ee2bb..07671250 100644
--- a/bitcoin/src/blockdata/mod.rs
+++ b/bitcoin/src/blockdata/mod.rs
@@ -4,7 +4,6 @@
 //!
 //! This module defines structures and functions for storing the blocks and
 //! transactions which make up the Bitcoin system.
-//!
 
 pub mod block;
 pub mod constants;
diff --git a/bitcoin/src/blockdata/opcodes.rs b/bitcoin/src/blockdata/opcodes.rs
index 95054f5a..5a502d05 100644
--- a/bitcoin/src/blockdata/opcodes.rs
+++ b/bitcoin/src/blockdata/opcodes.rs
@@ -4,7 +4,6 @@
 //!
 //! Bitcoin's script uses a stack-based assembly language. This module defines
 //! all of the opcodes for that language.
-//!
 
 #![allow(non_camel_case_types)]
 
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index b4633ed6..96597ce0 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -9,7 +9,6 @@
 //! signatures ensures that coins cannot be spent by unauthorized parties.
 //!
 //! This module provides the structures and functions needed to support transactions.
-//!
 
 use core::{cmp, fmt, str};
 
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index d617f980..2ae6b48a 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -3,7 +3,6 @@
 //! Witness
 //!
 //! This module contains the [`Witness`] struct and related methods to operate on it
-//!
 
 use core::fmt;
 use core::ops::Index;
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index 3468feb1..beed6e5b 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -13,7 +13,6 @@
 //! course, but has some critical differences from the network format e.g.,
 //! scripts come with an opcode decode, hashes are big-endian, numbers are
 //! typically big-endian decimals, etc.)
-//!
 
 use core::{fmt, mem};
 
diff --git a/bitcoin/src/consensus/mod.rs b/bitcoin/src/consensus/mod.rs
index 1ea0b036..ca41173e 100644
--- a/bitcoin/src/consensus/mod.rs
+++ b/bitcoin/src/consensus/mod.rs
@@ -4,7 +4,6 @@
 //!
 //! This module defines structures, functions, and traits that are needed to
 //! conform to Bitcoin consensus.
-//!
 
 pub mod encode;
 pub mod params;
diff --git a/bitcoin/src/crypto/mod.rs b/bitcoin/src/crypto/mod.rs
index 82c41279..d14e7304 100644
--- a/bitcoin/src/crypto/mod.rs
+++ b/bitcoin/src/crypto/mod.rs
@@ -3,7 +3,6 @@
 //! Cryptography
 //!
 //! Cryptography related functionality: keys and signatures.
-//!
 
 pub mod ecdsa;
 pub mod key;
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index 0ea93820..69d67f9b 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -61,6 +61,7 @@ fn from(hash: $ty) -> secp256k1::Message {
     /// Taproot-tagged hash with tag \"TapSighash\".
     ///
     /// This hash type is used for computing taproot signature hash."
+    #[hash_newtype(forward)]
     pub struct TapSighash(_);
 }
 
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index c97c03f2..2d3dc830 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -3,7 +3,6 @@
 //! Bitcoin taproot keys.
 //!
 //! This module provides taproot keys used in Bitcoin (including reexporting secp256k1 keys).
-//!
 
 use core::fmt;
 
diff --git a/bitcoin/src/internal_macros.rs b/bitcoin/src/internal_macros.rs
index b8d20694..28df98d8 100644
--- a/bitcoin/src/internal_macros.rs
+++ b/bitcoin/src/internal_macros.rs
@@ -3,7 +3,6 @@
 //! Internal macros.
 //!
 //! Macros meant to be used inside the Rust Bitcoin library.
-//!
 
 macro_rules! impl_consensus_encoding {
     ($thing:ident, $($field:ident),+) => (
diff --git a/bitcoin/src/p2p/address.rs b/bitcoin/src/p2p/address.rs
index a0a381c9..6e1a6c20 100644
--- a/bitcoin/src/p2p/address.rs
+++ b/bitcoin/src/p2p/address.rs
@@ -4,7 +4,6 @@
 //!
 //! This module defines the structures and functions needed to encode
 //! network addresses in Bitcoin messages.
-//!
 
 use core::{fmt, iter};
 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
diff --git a/bitcoin/src/p2p/message.rs b/bitcoin/src/p2p/message.rs
index 567d07bd..65eb19f8 100644
--- a/bitcoin/src/p2p/message.rs
+++ b/bitcoin/src/p2p/message.rs
@@ -4,7 +4,6 @@
 //!
 //! This module defines the `NetworkMessage` and `RawNetworkMessage` types that
 //! are used for (de)serializing Bitcoin objects for transmission on the network.
-//!
 
 use core::{fmt, iter};
 
diff --git a/bitcoin/src/p2p/message_blockdata.rs b/bitcoin/src/p2p/message_blockdata.rs
index 8719572a..c3d4b19f 100644
--- a/bitcoin/src/p2p/message_blockdata.rs
+++ b/bitcoin/src/p2p/message_blockdata.rs
@@ -4,7 +4,6 @@
 //!
 //! This module describes network messages which are used for passing
 //! Bitcoin data (blocks and transactions) around.
-//!
 
 use hashes::{sha256d, Hash as _};
 use io::{BufRead, Write};
diff --git a/bitcoin/src/p2p/message_bloom.rs b/bitcoin/src/p2p/message_bloom.rs
index cb04fca7..1e6b5e23 100644
--- a/bitcoin/src/p2p/message_bloom.rs
+++ b/bitcoin/src/p2p/message_bloom.rs
@@ -3,7 +3,6 @@
 //! Bitcoin Connection Bloom filtering network messages.
 //!
 //! This module describes BIP37 Connection Bloom filtering network messages.
-//!
 
 use io::{BufRead, Write};
 
diff --git a/bitcoin/src/p2p/message_compact_blocks.rs b/bitcoin/src/p2p/message_compact_blocks.rs
index 66246052..d4d8153b 100644
--- a/bitcoin/src/p2p/message_compact_blocks.rs
+++ b/bitcoin/src/p2p/message_compact_blocks.rs
@@ -2,7 +2,6 @@
 
 //!
 //! BIP152  Compact Blocks network messages
-//!
 
 use crate::bip152;
 use crate::internal_macros::impl_consensus_encoding;
diff --git a/bitcoin/src/p2p/message_filter.rs b/bitcoin/src/p2p/message_filter.rs
index 82e6e7ea..1537a73d 100644
--- a/bitcoin/src/p2p/message_filter.rs
+++ b/bitcoin/src/p2p/message_filter.rs
@@ -3,7 +3,6 @@
 //! Bitcoin Client Side Block Filtering network messages.
 //!
 //! This module describes BIP157 Client Side Block Filtering network messages.
-//!
 
 use crate::bip158::{FilterHash, FilterHeader};
 use crate::blockdata::block::BlockHash;
diff --git a/bitcoin/src/p2p/message_network.rs b/bitcoin/src/p2p/message_network.rs
index 606536ee..fafd6411 100644
--- a/bitcoin/src/p2p/message_network.rs
+++ b/bitcoin/src/p2p/message_network.rs
@@ -4,7 +4,6 @@
 //!
 //! This module defines network messages which describe peers and their
 //! capabilities.
-//!
 
 use hashes::sha256d;
 use io::{BufRead, Write};
diff --git a/bitcoin/src/policy.rs b/bitcoin/src/policy.rs
index 6bda6608..f9089f66 100644
--- a/bitcoin/src/policy.rs
+++ b/bitcoin/src/policy.rs
@@ -10,7 +10,6 @@
 //! Bitcoin. As such they must not be relied upon as if they were consensus rules.
 //!
 //! These values were taken from bitcoind v0.21.1 (194b9b8792d9b0798fdb570b79fa51f1d1f5ebaf).
-//!
 
 use core::cmp;
 
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index 3f543b1a..3854329b 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -4,7 +4,6 @@
 //!
 //! Provides the [`Work`] and [`Target`] types that are used in proof-of-work calculations. The
 //! functions here are designed to be fast, by that we mean it is safe to use them to check headers.
-//!
 
 use core::cmp;
 use core::fmt::{self, LowerHex, UpperHex};
@@ -15,7 +14,6 @@
 use mutagen::mutate;
 use units::parse;
 
-use crate::block::Header;
 use crate::blockdata::block::BlockHash;
 use crate::consensus::encode::{self, Decodable, Encodable};
 use crate::consensus::Params;
@@ -367,82 +365,6 @@ pub fn from_unprefixed_hex(s: &str) -> Result<Self, UnprefixedHexError> {
         Ok(Self::from_consensus(lock_time))
     }
 
-    /// Computes the [`CompactTarget`] from a difficulty adjustment.
-    ///
-    /// 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 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.
-    ///
-    /// # Note
-    ///
-    /// Under the consensus rules, the difference in the number of blocks between the headers does
-    /// not equate to the `difficulty_adjustment_interval` of [`Params`]. This is due to an off-by-one
-    /// error, and, the expected number of blocks in between headers is `difficulty_adjustment_interval - 1`
-    /// when calculating the difficulty adjustment.
-    ///
-    /// 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.
-    ///
-    /// # Returns
-    ///
-    /// The expected [`CompactTarget`] recalculation.
-    pub fn from_next_work_required(
-        last: CompactTarget,
-        timespan: u64,
-        params: impl AsRef<Params>,
-    ) -> CompactTarget {
-        let params = params.as_ref();
-        if params.no_pow_retargeting {
-            return last;
-        }
-        // Comments relate to the `pow.cpp` file from Core.
-        // 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, 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(actual_timespan.into());
-        let retarget = retarget.div(params.pow_target_timespan.into());
-        let retarget = Target(retarget);
-        if retarget.ge(&maximum_retarget) {
-            return maximum_retarget.to_compact_lossy();
-        }
-        retarget.to_compact_lossy()
-    }
-
-    /// Computes the [`CompactTarget`] from a difficulty adjustment,
-    /// assuming these are the relevant block headers.
-    ///
-    /// Given two headers, representing the start and end of a difficulty adjustment epoch,
-    /// compute the [`CompactTarget`] based on the net time between them and the current
-    /// [`CompactTarget`].
-    ///
-    /// # Note
-    ///
-    /// See [`CompactTarget::from_next_work_required`]
-    ///
-    /// For example, to successfully compute the first difficulty adjustment on the Bitcoin network,
-    /// one would pass the header for Block 2015 as `current` and the header for Block 0 as
-    /// `last_epoch_boundary`.
-    ///
-    /// # Returns
-    ///
-    /// The expected [`CompactTarget`] recalculation.
-    pub fn from_header_difficulty_adjustment(
-        last_epoch_boundary: Header,
-        current: Header,
-        params: impl AsRef<Params>,
-    ) -> CompactTarget {
-        let timespan = current.time - last_epoch_boundary.time;
-        let bits = current.bits;
-        CompactTarget::from_next_work_required(bits, timespan.into(), params)
-    }
-
     /// Creates a [`CompactTarget`] from a consensus encoded `u32`.
     pub fn from_consensus(bits: u32) -> Self { Self(bits) }
 
@@ -1766,113 +1688,6 @@ fn compact_target_lower_hex_and_upper_hex() {
         assert_eq!(format!("{:08X}", CompactTarget(0x01d0f456)), "01D0F456");
     }
 
-    #[test]
-    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: 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
-        assert_eq!(adjustment, adjustment_bits);
-    }
-
-    #[test]
-    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: 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
-        assert_eq!(adjustment, adjustment_bits);
-    }
-
-    #[test]
-    fn compact_target_from_upwards_difficulty_adjustment_using_headers() {
-        use crate::{block::Version, constants::genesis_block, TxMerkleNode};
-        use hashes::Hash;
-        let params = Params::new(crate::Network::Signet);
-        let epoch_start = genesis_block(&params).header;
-        // Block 2015, the only information used are `bits` and `time`
-        let current = Header {
-            version: Version::ONE,
-            prev_blockhash: BlockHash::all_zeros(),
-            merkle_root: TxMerkleNode::all_zeros(),
-            time: 1599332177,
-            bits: epoch_start.bits,
-            nonce: epoch_start.nonce
-        };
-        let adjustment = CompactTarget::from_header_difficulty_adjustment(epoch_start, current, params);
-        let adjustment_bits = CompactTarget::from_consensus(503394215); // Block 2016 compact target
-        assert_eq!(adjustment, adjustment_bits);
-    }
-
-    #[test]
-    fn compact_target_from_downwards_difficulty_adjustment_using_headers() {
-        use crate::{block::Version, TxMerkleNode};
-        use hashes::Hash;
-        let params = Params::new(crate::Network::Signet);
-        let starting_bits = CompactTarget::from_consensus(503394215); // Block 2016 compact target
-        // Block 2016, the only information used is `time`
-        let epoch_start = Header {
-            version: Version::ONE,
-            prev_blockhash: BlockHash::all_zeros(),
-            merkle_root: TxMerkleNode::all_zeros(),
-            time: 1599332844,
-            bits: starting_bits,
-            nonce: 0
-        };
-        // Block 4031, the only information used are `bits` and `time`
-        let current = Header {
-            version: Version::ONE,
-            prev_blockhash: BlockHash::all_zeros(),
-            merkle_root: TxMerkleNode::all_zeros(),
-            time: 1600591200,
-            bits: starting_bits,
-            nonce: 0
-        };
-        let adjustment = CompactTarget::from_header_difficulty_adjustment(epoch_start, current, params);
-        let adjustment_bits = CompactTarget::from_consensus(503397348); // Block 4032 compact target
-        assert_eq!(adjustment, adjustment_bits);
-    }
-
-    #[test]
-    fn compact_target_from_maximum_upward_difficulty_adjustment() {
-        let params = Params::new(crate::Network::Signet);
-        let starting_bits = CompactTarget::from_consensus(503403001);
-        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();
-        assert_eq!(got, want);
-    }
-
-    #[test]
-    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, &params);
-        let want = Target::from_compact(starting_bits)
-            .max_transition_threshold(params)
-            .to_compact_lossy();
-        assert_eq!(got, want);
-    }
-
-    #[test]
-    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, &params);
-        let want = params.max_attainable_target.to_compact_lossy();
-        assert_eq!(got, want);
-    }
-
     #[test]
     fn target_from_compact() {
         // (nBits, target)
diff --git a/bitcoin/src/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs
index cfe77811..6c722f2f 100644
--- a/bitcoin/src/psbt/map/input.rs
+++ b/bitcoin/src/psbt/map/input.rs
@@ -136,16 +136,6 @@ pub struct Input {
 /// type can be either [`EcdsaSighashType`] or [`TapSighashType`] but it is not possible to know
 /// directly which signature hash type the user is dealing with. Therefore, the user is responsible
 /// for converting to/from [`PsbtSighashType`] from/to the desired signature hash type they need.
-///
-/// # Examples
-///
-/// ```
-/// use bitcoin::{EcdsaSighashType, TapSighashType};
-/// use bitcoin::psbt::PsbtSighashType;
-///
-/// let ecdsa_sighash_all: PsbtSighashType = EcdsaSighashType::All.into();
-/// let tap_sighash_all: PsbtSighashType = TapSighashType::All.into();
-/// ```
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
@@ -524,7 +514,7 @@ fn psbt_insert_hash_pair<H>(
             if <H as hashes::Hash>::hash(&val) != key_val {
                 return Err(psbt::Error::InvalidPreimageHashPair {
                     preimage: val.into_boxed_slice(),
-                    hash: Box::from(key_val.as_ref()),
+                    hash: Box::from(key_val.borrow()),
                     hash_type,
                 });
             }
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index 667719ec..f47a7030 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -5,7 +5,6 @@
 //! Implementation of BIP174 Partially Signed Bitcoin Transaction Format as
 //! defined at <https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>
 //! except we define PSBTs containing non-standard sighash types as invalid.
-//!
 
 #[macro_use]
 mod macros;
@@ -762,7 +761,7 @@ fn get_key<C: Signing>(
             KeyRequest::Pubkey(_) => Err(GetKeyError::NotSupported),
             KeyRequest::Bip32((fingerprint, path)) => {
                 let key = if self.fingerprint(secp) == fingerprint {
-                    let k = self.derive_priv(secp, &path);
+                    let k = self.derive_priv(secp, &path)?;
                     Some(k.to_priv())
                 } else {
                     None
@@ -808,7 +807,7 @@ fn get_key<C: Signing>(
             KeyRequest::Bip32((fingerprint, path)) => {
                 for xpriv in self.iter() {
                     if xpriv.parent_fingerprint == fingerprint {
-                        let k = xpriv.derive_priv(secp, &path);
+                        let k = xpriv.derive_priv(secp, &path)?;
                         return Ok(Some(k.to_priv()));
                     }
                 }
@@ -1366,7 +1365,7 @@ fn serialize_then_deserialize_output() {
             ChildNumber::from_normal_idx(31337).unwrap(),
         ];
 
-        sk = sk.derive_priv(secp, &dpath);
+        sk = sk.derive_priv(secp, &dpath).unwrap();
 
         let pk = Xpub::from_priv(secp, &sk);
 
diff --git a/bitcoin/src/psbt/raw.rs b/bitcoin/src/psbt/raw.rs
index 9f9c64fd..65b350b3 100644
--- a/bitcoin/src/psbt/raw.rs
+++ b/bitcoin/src/psbt/raw.rs
@@ -4,7 +4,6 @@
 //!
 //! Raw PSBT key-value pairs as defined at
 //! <https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki>.
-//!
 
 use core::fmt;
 
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index f9e0564a..5b48d354 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -4,7 +4,6 @@
 //!
 //! Traits to serialize PSBT values to and from raw bytes
 //! according to the BIP-174 specification.
-//!
 
 use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
 use secp256k1::XOnlyPublicKey;
diff --git a/bitcoin/src/serde_utils.rs b/bitcoin/src/serde_utils.rs
index ce741a1e..7b83ea67 100644
--- a/bitcoin/src/serde_utils.rs
+++ b/bitcoin/src/serde_utils.rs
@@ -3,7 +3,6 @@
 //! Bitcoin serde utilities.
 //!
 //! This module is for special serde serializations.
-//!
 
 pub(crate) struct SerializeBytesAsHex<'a>(pub(crate) &'a [u8]);
 
diff --git a/bitcoin/src/sign_message.rs b/bitcoin/src/sign_message.rs
index b8232dab..b373e452 100644
--- a/bitcoin/src/sign_message.rs
+++ b/bitcoin/src/sign_message.rs
@@ -4,7 +4,6 @@
 //!
 //! This module provides signature related functions including secp256k1 signature recovery when
 //! library is used with the `secp-recovery` feature.
-//!
 
 use hashes::{sha256d, Hash, HashEngine};
 
diff --git a/bitcoin/src/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index 87318ada..227e45c5 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -3,7 +3,6 @@
 //! Bitcoin Taproot.
 //!
 //! This module provides support for taproot tagged hashes.
-//!
 
 pub mod merkle_branch;
 pub mod serialized_signature;
@@ -36,6 +35,7 @@
     /// Taproot-tagged hash with tag \"TapLeaf\".
     ///
     /// This is used for computing tapscript script spend hash.
+    #[hash_newtype(forward)]
     pub struct TapLeafHash(_);
 
     pub struct TapBranchTag = hash_str("TapBranch");
@@ -43,6 +43,7 @@
     /// Tagged hash used in taproot trees.
     ///
     /// See BIP-340 for tagging rules.
+    #[hash_newtype(forward)]
     pub struct TapNodeHash(_);
 
     pub struct TapTweakTag = hash_str("TapTweak");
@@ -50,6 +51,7 @@
     /// Taproot-tagged hash with tag \"TapTweak\".
     ///
     /// This hash type is used while computing the tweaked public key.
+    #[hash_newtype(forward)]
     pub struct TapTweakHash(_);
 }
 
diff --git a/contrib/crates.sh b/contrib/crates.sh
deleted file mode 100755
index f74787e4..00000000
--- a/contrib/crates.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
-
-# Crates in this workspace to test (note "fuzz" is only built not tested).
-CRATES=("base58" "bitcoin" "fuzz" "hashes" "internals" "io" "units")
diff --git a/contrib/gen_label_config.sh b/contrib/gen_label_config.sh
index 11536bbf..089bd30d 100755
--- a/contrib/gen_label_config.sh
+++ b/contrib/gen_label_config.sh
@@ -20,7 +20,7 @@ fi
 
 excluded_crates="fuzz|dep_test"
 
-CRATES="$(cd "$SCAN_DIR" && cargo metadata --no-deps --format-version 1 | jq -j -r '.packages | map(.manifest_path | rtrimstr("/Cargo.toml") | ltrimstr("'"$PWD"'/")) | join(" ")')"
+CRATES="`cd "$SCAN_DIR" && cargo metadata --no-deps --format-version 1 | jq -j -r '.packages | map(.manifest_path | rtrimstr("/Cargo.toml") | ltrimstr("'$PWD'/")) | join(" ")'`"
 
 for crate in $CRATES;
 do
@@ -29,9 +29,7 @@ do
 		continue
 	fi
 
-	{
-		echo "C-$crate:"
-		echo "  - changed-files:"
-		echo "    - any-glob-to-any-file: $crate/**"
-	} >> "$config"
+	echo "C-$crate:" >> "$config"
+	echo "  - changed-files:" >> "$config"
+	echo "    - any-glob-to-any-file: $crate/**" >> "$config"
 done
diff --git a/contrib/get_matrix.sh b/contrib/get_matrix.sh
new file mode 100755
index 00000000..d72a865b
--- /dev/null
+++ b/contrib/get_matrix.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+. contrib/test_vars.sh
+
+crates="`cargo metadata --no-deps --format-version 1 | jq -c '.packages | map(.manifest_path | rtrimstr("/Cargo.toml") | ltrimstr("'$PWD'/"))'`"
+deps="`echo -n $DEPS | jq -R -c 'split(" ")'`"
+# debug
+echo "$crates"
+echo "$deps"
+
+echo "crates=$crates" >> $GITHUB_OUTPUT
+echo "deps=$deps" >> $GITHUB_OUTPUT
diff --git a/contrib/release.sh b/contrib/release.sh
index b30f2fb8..558ba0fb 100755
--- a/contrib/release.sh
+++ b/contrib/release.sh
@@ -28,7 +28,7 @@ main () {
 # preparation for releasing the crate.
 release_changes() {
     local crate=$1
-    git log --patch --reverse master.. -- "$crate"/Cargo.toml | grep version
+    git log --patch --reverse master.. -- $crate/Cargo.toml | grep version
 }
 
 # Do a dry run publish to crates.io using the correct package name for crate ($1).
@@ -47,4 +47,4 @@ publish_dry_run() {
 #
 # Main script.
 #
-main "$@"
+main $@
diff --git a/contrib/run_task.sh b/contrib/run_task.sh
new file mode 100755
index 00000000..424dd902
--- /dev/null
+++ b/contrib/run_task.sh
@@ -0,0 +1,341 @@
+#!/usr/bin/env bash
+#
+# We are attempting to run max 20 parallel jobs using GitHub actions (usage limit for free tier).
+#
+# ref: https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration
+#
+# The minimal/recent manifests are handled by CI (rust.yaml).
+#
+# Jobs (shell functions) that get run twice, once for each manifest:
+#
+# 1+2  stable
+# 3+4  nightly
+# 5+6  msrv
+#
+# Jobs (shell functions) that get run once:
+#
+# 7   lint
+# 8   docs
+# 9   docsrs
+# 10  bench
+# 11  asan
+# 12  wasm
+# 13  schemars
+#
+# Jobs run directly by rust.yml workflow:
+#
+# 0 Prepare
+#
+# 14  Arch32bit
+# 15  Cross
+# 16  Embedded
+# 17  Kani
+#
+# Jobs run directly from other workflows:
+#
+# 18  Coveralls - run by coveralls.yml
+# 19  release - run by release.yml
+# 20  labeler - run by manage-pr.yml
+
+set -euox pipefail
+
+REPO_DIR=$(git rev-parse --show-toplevel)
+CRATES=("bitcoin" "hashes" "internals" "io" "units" "base58")
+
+# Make all cargo invocations verbose.
+export CARGO_TERM_VERBOSE=true
+
+main() {
+    local task="$1"
+
+    check_required_commands
+
+    cargo --version
+    rustc --version
+    /usr/bin/env bash --version
+    locale
+    env
+
+    case $task in
+        # 2 jobs each for these (one for each lock file).
+	stable)
+            # Test, run examples, do feature matrix.
+            # crate/contrib/test_vars.sh is sourced in this function.
+            build_and_test
+            ;;
+
+	nightly)
+            build_and_test
+            ;;
+
+	msrv)
+            build_and_test
+            ;;
+
+        # 1 job each for these.
+        lint)
+            do_lint
+            do_dup_deps
+            ;;
+
+	docs)
+            build_docs_with_stable_toolchain
+	    ;;
+
+	docsrs)
+            build_docs_with_nightly_toolchain
+	    ;;
+
+	bench)
+	    do_bench
+	    ;;
+
+	wasm)
+            # hashes crate only.
+	    do_wasm
+	    ;;
+
+	asan)
+            # hashes crate only - hashes/contrib/test_vars.sh is sourced in this function.
+	    do_asan
+	    ;;
+
+	schemars)
+            # hashes crate only.
+	    do_schemars
+	    ;;
+
+        *)
+            err "Error: unknown task $task"
+            ;;
+    esac
+}
+
+# Build and test for each crate, done with each toolchain.
+build_and_test() {
+    # Building the fuzz crate is more-or-less just a sanity check.
+    pushd "$REPO_DIR/fuzz" > /dev/null
+    cargo --locked build
+    popd > /dev/null
+
+    for crate in "${CRATES[@]}"; do
+        pushd "$REPO_DIR/$crate" > /dev/null
+
+        # Set crate specific variables.
+        . contrib/test_vars.sh || exit 1
+
+        do_test
+        do_feature_matrix
+
+        popd > /dev/null
+    done
+}
+
+do_test() {
+    # Use the current (recent/minimal) lock file.
+    local cargo="cargo --locked"
+
+    # Defaults / sanity checks
+    $cargo build
+    $cargo test
+
+    for example in $EXAMPLES; do # EXAMPLES is set in contrib/test_vars.sh
+        name="$(echo "$example" | cut -d ':' -f 1)"
+        features="$(echo "$example" | cut -d ':' -f 2)"
+        $cargo run --example "$name" --features="$features"
+    done
+
+    if [ -e ./contrib/extra_tests.sh ];
+    then
+        ./contrib/extra_tests.sh
+    fi
+}
+
+# Each crate defines its own feature matrix test so feature combinations
+# can be better controlled.
+do_feature_matrix() {
+    local cargo="cargo --locked"
+
+    $cargo build --no-default-features
+    $cargo test --no-default-features
+
+    # All crates have a "std" feature and FEATURES_WITH_STD is set in
+    # contrib/test_vars.sh
+    loop_features "std" "$FEATURES_WITH_STD"
+
+    # All but `bitcoin` crate have an "alloc" feature, this tests it
+    # along with any other features that should work with "std".
+    if [ -n "$FEATURES_WITHOUT_STD" ]
+       then
+           loop_features "" "$FEATURES_WITHOUT_STD"
+    fi
+}
+
+# Build with each feature as well as all combinations of two features.
+#
+# Usage: loop_features "std" "this-feature that-feature other"
+loop_features() {
+    local use="$1"
+    local features="$2"
+    local cargo="cargo --locked"
+
+    # All the provided features including $use
+    $cargo build --no-default-features --features="$use $features"
+    $cargo test --no-default-features --features="$use $features"
+
+    read -r -a array <<< "$features"
+    local len="${#array[@]}"
+
+    if (( len > 1 )); then
+        for ((i = 0 ; i < len ; i++ ));
+        do
+            $cargo build --features="$use ${array[i]}"
+            $cargo test --features="$use ${array[i]}"
+
+            if (( i < len - 1 )); then
+               for ((j = i + 1 ; j < len ; j++ ));
+               do
+                   $cargo build --features="$use ${array[i]} ${array[j]}"
+                   $cargo test --features="$use ${array[i]} ${array[j]}"
+               done
+            fi
+        done
+    fi
+}
+
+# Lint the workspace.
+do_lint() {
+    need_nightly
+    local cargo="cargo --locked"
+
+    # Lint various feature combinations to try and catch mistakes in feature gating.
+    $cargo clippy --workspace --all-targets --keep-going -- -D warnings
+    $cargo clippy --workspace --all-targets --all-features --keep-going -- -D warnings
+    $cargo clippy --workspace --all-targets --no-default-features --keep-going -- -D warnings
+}
+
+# We should not have any duplicate dependencies. This catches mistakes made upgrading dependencies
+# in one crate and not in another (e.g. upgrade bitcoin_hashes in bitcoin but not in secp).
+do_dup_deps() {
+    # We can't use pipefail because these grep statements fail by design when there is no duplicate,
+    # the shell therefore won't pick up mistakes in your pipe - you are on your own.
+    set +o pipefail
+
+    duplicate_dependencies=$(
+        # Only show the actual duplicated deps, not their reverse tree, then
+        # whitelist the 'syn' crate which is duplicated but it's not our fault.
+        #
+        # Temporarily allow 2 versions of `hashes`, `internals`, and `hex` while we upgrade.
+        cargo tree  --target=all --all-features --duplicates \
+            | grep '^[0-9A-Za-z]' \
+            | grep -v 'syn' \
+            | wc -l
+                          )
+    if [ "$duplicate_dependencies" -ne 0 ]; then
+        echo "Dependency tree is broken, contains duplicates"
+        cargo tree  --target=all --all-features --duplicates
+        exit 1
+    fi
+
+    set -o pipefail
+}
+
+# Build the docs with a nightly toolchain, in unison with the function
+# below this checks that we feature guarded docs imports correctly.
+build_docs_with_nightly_toolchain() {
+    need_nightly
+    local cargo="cargo --locked"
+    RUSTDOCFLAGS="--cfg docsrs -D warnings -D rustdoc::broken-intra-doc-links" $cargo doc --all-features
+}
+
+# Build the docs with a stable toolchain, in unison with the function
+# above this checks that we feature guarded docs imports correctly.
+build_docs_with_stable_toolchain() {
+    local cargo="cargo +stable --locked"
+    RUSTDOCFLAGS="-D warnings" $cargo doc --all-features
+}
+
+# Bench only works with a non-stable toolchain (nightly, beta).
+do_bench() {
+    for crate in bitcoin hashes; do
+        pushd "$REPO_DIR/$crate" > /dev/null
+        RUSTFLAGS='--cfg=bench' cargo bench
+        popd > /dev/null
+    done
+}
+
+# This is only relevant for hashes.
+do_schemars() {
+    pushd "$REPO_DIR/hashes/extended_tests/schemars" > /dev/null
+    cargo test
+    popd > /dev/null
+}
+
+# Note we do not use the recent lock file or `--locked` when running the wasm tests.
+do_wasm() {
+    pushd "$REPO_DIR/hashes" > /dev/null
+
+    clang --version &&
+	CARGO_TARGET_DIR=wasm cargo install --force wasm-pack &&
+	printf '\n[target.wasm32-unknown-unknown.dev-dependencies]\nwasm-bindgen-test = "0.3"\n' >> Cargo.toml &&
+	printf '\n[lib]\ncrate-type = ["cdylib", "rlib"]\n' >> Cargo.toml &&
+	CC=clang-9 wasm-pack build &&
+	CC=clang-9 wasm-pack test --node;
+
+    popd > /dev/null
+}
+
+do_asan() {
+    pushd "$REPO_DIR/hashes" > /dev/null
+
+    # Set ASAN_FEATURES
+    . contrib/test_vars.sh || exit 1
+
+    cargo clean
+    CC='clang -fsanitize=address -fno-omit-frame-pointer'                                        \
+      RUSTFLAGS='-Zsanitizer=address -Clinker=clang -Cforce-frame-pointers=yes'                    \
+      ASAN_OPTIONS='detect_leaks=1 detect_invalid_pointer_pairs=1 detect_stack_use_after_return=1' \
+      cargo test --lib --no-default-features --features="$ASAN_FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu
+    # There is currently a bug in the MemorySanitizer (MSAN) - disable the job for now.
+    #
+    # cargo clean
+    # CC='clang -fsanitize=memory -fno-omit-frame-pointer'                                         \
+    #   RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins -Cforce-frame-pointers=yes'   \
+    #   cargo test --lib --no-default-features --features="$ASAN_FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu
+
+    popd > /dev/null
+}
+
+# Check all the commands we use are present in the current environment.
+check_required_commands() {
+    need_cmd cargo
+    need_cmd rustc
+    need_cmd jq
+    need_cmd cut
+    need_cmd grep
+    need_cmd wc
+}
+
+need_cmd() {
+    if ! command -v "$1" > /dev/null 2>&1
+    then err "need '$1' (command not found)"
+    fi
+}
+
+need_nightly() {
+    cargo_ver=$(cargo --version)
+    if echo "$cargo_ver" | grep -q -v nightly; then
+        err "Need a nightly compiler; have $(cargo --version)"
+    fi
+}
+
+err() {
+    echo "$1" >&2
+    exit 1
+}
+
+#
+# Main script
+#
+main "$@"
+exit 0
diff --git a/fuzz/cycle.sh b/fuzz/cycle.sh
index c642157d..b72c44da 100755
--- a/fuzz/cycle.sh
+++ b/fuzz/cycle.sh
@@ -8,8 +8,7 @@
 set -euo pipefail
 
 REPO_DIR=$(git rev-parse --show-toplevel)
-# can't find the file because of the ENV var
-# shellcheck source=/dev/null
+# shellcheck source=./fuzz-util.sh
 source "$REPO_DIR/fuzz/fuzz-util.sh"
 
 while :
diff --git a/fuzz/fuzz.sh b/fuzz/fuzz.sh
index 2632e6bc..c85b8e4e 100755
--- a/fuzz/fuzz.sh
+++ b/fuzz/fuzz.sh
@@ -3,8 +3,7 @@ set -euox pipefail
 
 REPO_DIR=$(git rev-parse --show-toplevel)
 
-# can't find the file because of the ENV var
-# shellcheck source=/dev/null
+# shellcheck source=./fuzz-util.sh
 source "$REPO_DIR/fuzz/fuzz-util.sh"
 
 # Check that input files are correct Windows file names
diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh
index 39e64697..bbb36ea4 100755
--- a/fuzz/generate-files.sh
+++ b/fuzz/generate-files.sh
@@ -4,8 +4,7 @@ set -euo pipefail
 
 REPO_DIR=$(git rev-parse --show-toplevel)
 
-# can't find the file because of the ENV var
-# shellcheck source=/dev/null
+# shellcheck source=./fuzz-util.sh
 source "$REPO_DIR/fuzz/fuzz-util.sh"
 
 # 1. Generate fuzz/Cargo.toml
@@ -23,7 +22,7 @@ cargo-fuzz = true
 
 [dependencies]
 honggfuzz = { version = "0.5.55", default-features = false }
-bitcoin = { path = "../bitcoin", features = [ "serde" ] }
+bitcoin = { version = "0.31.0", features = [ "serde" ] }
 
 serde = { version = "1.0.103", features = [ "derive" ] }
 serde_json = "1.0"
@@ -41,16 +40,16 @@ EOF
 done
 
 # 2. Generate .github/workflows/fuzz.yml
-cat > "$REPO_DIR/.github/workflows/cron-daily-fuzz.yml" <<EOF
+cat > "$REPO_DIR/.github/workflows/fuzz.yml" <<EOF
 # Automatically generated by fuzz/generate-files.sh
 name: Fuzz
+
 on:
-  schedule:
-    # 5am every day UTC, this correlates to:
-    # - 10pm PDT
-    # - 6am CET
-    # - 4pm AEDT
-    - cron: '00 05 * * *'
+  push:
+    branches:
+      - master
+      - 'test-ci/**'
+  pull_request:
 
 jobs:
   fuzz:
@@ -59,8 +58,6 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        # We only get 20 jobs at a time, we probably don't want to go
-        # over that limit with fuzzing because of the hour run time.
         fuzz_target: [
 $(for name in $(listTargetNames); do echo "          $name,"; done)
         ]
@@ -68,7 +65,7 @@ $(for name in $(listTargetNames); do echo "          $name,"; done)
       - name: Install test dependencies
         run: sudo apt-get update -y && sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev
       - uses: actions/checkout@v4
-      - uses: actions/cache@v4
+      - uses: actions/cache@v3
         id: cache-fuzz
         with:
           path: |
diff --git a/githooks/pre-commit b/githooks/pre-commit
index d0e92ee9..5d0b9fe6 100755
--- a/githooks/pre-commit
+++ b/githooks/pre-commit
@@ -21,13 +21,11 @@ exec 1>&2
 # Cross platform projects tend to avoid non-ASCII filenames; prevent
 # them from being added to the repository. We exploit the fact that the
 # printable range starts at the space character and ends with tilde.
-# disable the check to quoting the $(...) command
-# shellcheck disable=SC2046
 if [ "$allownonascii" != "true" ] &&
 	# Note that the use of brackets around a tr range is ok here, (it's
 	# even required, for portability to Solaris 10's /usr/bin/tr), since
 	# the square bracket bytes happen to fall in the designated range.
-	test $(git diff --cached --name-only --diff-filter=A -z "$against" |
+	test $(git diff --cached --name-only --diff-filter=A -z $against |
 	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
 then
 	cat <<\EOF
@@ -45,9 +43,7 @@ EOF
 fi
 
 # If there are whitespace errors, print the offending file names and fail.
-git diff-index --check --cached "$against" -- || exit 1
+git diff-index --check --cached $against -- || exit 1
 
 # Check that code lints cleanly.
-# disable the check to quoting the $(...) command
-# shellcheck disable=SC2046
 cargo +$(cat ./nightly-version) clippy --workspace --all-targets --all-features -- --deny warnings || exit 1
diff --git a/hashes/CHANGELOG.md b/hashes/CHANGELOG.md
index 887776a5..ca96b020 100644
--- a/hashes/CHANGELOG.md
+++ b/hashes/CHANGELOG.md
@@ -1,20 +1,3 @@
-# unreleased
-
-* Change the default display direction of for tagged hashes to forwards. [#2707](https://github.com/rust-bitcoin/rust-bitcoin/pull/2707)
-
-  Note please this usage if you need to display backward:
-
-```rust
-  sha256t_hash_newtype! {
-    /// Test detailed explanation.
-    struct NewTypeTag = hash_str("tag");
-
-    /// A test hash.
-    #[hash_newtype(backward)]
-    struct NewTypeHash(_);
-  }
-```
-
 # 0.14.0 - 2024-03-21
 
 * Bump MSRV to Rust version 1.56.1 [#2188](https://github.com/rust-bitcoin/rust-bitcoin/pull/2188)
diff --git a/hashes/contrib/sanitizer.sh b/hashes/contrib/sanitizer.sh
deleted file mode 100755
index 5a1e56c2..00000000
--- a/hashes/contrib/sanitizer.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env bash
-#
-# Run the Address/Memory Sanitizer tests.
-
-set -euox pipefail
-
-# Run the sanitizer with these features.
-FEATURES="std io serde"
-
-cargo clean
-CC='clang -fsanitize=address -fno-omit-frame-pointer'                                        \
-  RUSTFLAGS='-Zsanitizer=address -Clinker=clang -Cforce-frame-pointers=yes'                    \
-  ASAN_OPTIONS='detect_leaks=1 detect_invalid_pointer_pairs=1 detect_stack_use_after_return=1' \
-  cargo test --lib --no-default-features --features="$FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu
-
-# There is currently a bug in the MemorySanitizer (MSAN) - disable the job for now.
-#
-# cargo clean
-# CC='clang -fsanitize=memory -fno-omit-frame-pointer'                                         \
-    #   RUSTFLAGS='-Zsanitizer=memory -Zsanitizer-memory-track-origins -Cforce-frame-pointers=yes'   \
-    #   cargo test --lib --no-default-features --features="$FEATURES" -Zbuild-std --target x86_64-unknown-linux-gnu
diff --git a/hashes/contrib/wasm.sh b/hashes/contrib/wasm.sh
deleted file mode 100755
index 6139e307..00000000
--- a/hashes/contrib/wasm.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/env bash
-#
-# Run the WASM tests.
-
-set -euox pipefail
-
-clang --version &&
-    CARGO_TARGET_DIR=wasm cargo install --force wasm-pack &&
-    printf '\n[target.wasm32-unknown-unknown.dev-dependencies]\nwasm-bindgen-test = "0.3"\n' >> Cargo.toml &&
-    printf '\n[lib]\ncrate-type = ["cdylib", "rlib"]\n' >> Cargo.toml &&
-    CC=clang-9 wasm-pack build &&
-    CC=clang-9 wasm-pack test --node;
diff --git a/hashes/embedded/scripts/env.sh b/hashes/embedded/scripts/env.sh
index 798c4af8..7ea1e31c 100644
--- a/hashes/embedded/scripts/env.sh
+++ b/hashes/embedded/scripts/env.sh
@@ -1,4 +1,4 @@
-# we don't want shebangs in env.sh, disable shellcheck warning
-# shellcheck disable=SC2148
+#!/usr/bin/env bash
+
 export RUSTFLAGS="-C link-arg=-Tlink.x"
 export CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER="qemu-system-arm -cpu cortex-m3 -machine mps2-an385 -nographic -semihosting-config enable=on,target=native -kernel"
diff --git a/hashes/src/hash160.rs b/hashes/src/hash160.rs
index 21697f79..41098a8a 100644
--- a/hashes/src/hash160.rs
+++ b/hashes/src/hash160.rs
@@ -10,6 +10,7 @@
 
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::str;
 
 use crate::{ripemd160, sha256, FromSliceError};
 
diff --git a/hashes/src/hmac.rs b/hashes/src/hmac.rs
index 53be1959..32b967a5 100644
--- a/hashes/src/hmac.rs
+++ b/hashes/src/hmac.rs
@@ -8,7 +8,7 @@
 //! Hash-based Message Authentication Code (HMAC).
 //!
 
-use core::{convert, fmt, str};
+use core::{borrow, fmt, ops, str};
 
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
@@ -72,10 +72,10 @@ pub fn new(key: &[u8]) -> HmacEngine<T> {
 
         if key.len() > T::Engine::BLOCK_SIZE {
             let hash = <T as Hash>::hash(key);
-            for (b_i, b_h) in ipad.iter_mut().zip(hash.as_ref()) {
+            for (b_i, b_h) in ipad.iter_mut().zip(&hash[..]) {
                 *b_i ^= *b_h;
             }
-            for (b_o, b_h) in opad.iter_mut().zip(hash.as_ref()) {
+            for (b_o, b_h) in opad.iter_mut().zip(&hash[..]) {
                 *b_o ^= *b_h;
             }
         } else {
@@ -124,8 +124,33 @@ impl<T: Hash> fmt::LowerHex for Hmac<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
 }
 
-impl<T: Hash> convert::AsRef<[u8]> for Hmac<T> {
-    fn as_ref(&self) -> &[u8] { self.0.as_ref() }
+impl<T: Hash> ops::Index<usize> for Hmac<T> {
+    type Output = u8;
+    fn index(&self, index: usize) -> &u8 { &self.0[index] }
+}
+
+impl<T: Hash> ops::Index<ops::Range<usize>> for Hmac<T> {
+    type Output = [u8];
+    fn index(&self, index: ops::Range<usize>) -> &[u8] { &self.0[index] }
+}
+
+impl<T: Hash> ops::Index<ops::RangeFrom<usize>> for Hmac<T> {
+    type Output = [u8];
+    fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] { &self.0[index] }
+}
+
+impl<T: Hash> ops::Index<ops::RangeTo<usize>> for Hmac<T> {
+    type Output = [u8];
+    fn index(&self, index: ops::RangeTo<usize>) -> &[u8] { &self.0[index] }
+}
+
+impl<T: Hash> ops::Index<ops::RangeFull> for Hmac<T> {
+    type Output = [u8];
+    fn index(&self, index: ops::RangeFull) -> &[u8] { &self.0[index] }
+}
+
+impl<T: Hash> borrow::Borrow<[u8]> for Hmac<T> {
+    fn borrow(&self) -> &[u8] { &self[..] }
 }
 
 impl<T: Hash> Hash for Hmac<T> {
@@ -134,7 +159,7 @@ impl<T: Hash> Hash for Hmac<T> {
 
     fn from_engine(mut e: HmacEngine<T>) -> Hmac<T> {
         let ihash = T::from_engine(e.iengine);
-        e.oengine.input(ihash.as_ref());
+        e.oengine.input(&ihash[..]);
         let ohash = T::from_engine(e.oengine);
         Hmac(ohash)
     }
@@ -293,7 +318,7 @@ struct Test {
             let mut engine = HmacEngine::<sha256::Hash>::new(&test.key);
             engine.input(&test.input);
             let hash = Hmac::<sha256::Hash>::from_engine(engine);
-            assert_eq!(hash.as_ref(), &test.output[..]);
+            assert_eq!(&hash[..], &test.output[..]);
             assert_eq!(hash.as_byte_array(), test.output.as_slice());
         }
     }
diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs
index 5635020e..9b3f9dde 100644
--- a/hashes/src/internal_macros.rs
+++ b/hashes/src/internal_macros.rs
@@ -86,7 +86,7 @@ impl<$($gen: $gent),*> Hash<$($gen),*> {
             }
         }
 
-        impl<$($gen: $gent),*> $crate::_export::_core::str::FromStr for Hash<$($gen),*> {
+        impl<$($gen: $gent),*> str::FromStr for Hash<$($gen),*> {
             type Err = $crate::hex::HexToArrayError;
             fn from_str(s: &str) -> $crate::_export::_core::result::Result<Self, Self::Err> {
                 use $crate::{Hash, hex::{FromHex}};
diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs
index 1aa7a6b9..3bfdbf62 100644
--- a/hashes/src/lib.rs
+++ b/hashes/src/lib.rs
@@ -123,7 +123,7 @@ pub mod _core {
 pub mod sha512_256;
 pub mod siphash24;
 
-use core::{convert, fmt, hash};
+use core::{borrow, fmt, hash, ops};
 
 pub use hmac::{Hmac, HmacEngine};
 
@@ -158,7 +158,12 @@ pub trait Hash:
     + fmt::Debug
     + fmt::Display
     + fmt::LowerHex
-    + convert::AsRef<[u8]>
+    + ops::Index<ops::RangeFull, Output = [u8]>
+    + ops::Index<ops::RangeFrom<usize>, Output = [u8]>
+    + ops::Index<ops::RangeTo<usize>, Output = [u8]>
+    + ops::Index<ops::Range<usize>, Output = [u8]>
+    + ops::Index<usize, Output = u8>
+    + borrow::Borrow<[u8]>
 {
     /// A hashing engine which bytes can be serialized into. It is expected
     /// to implement the `io::Write` trait, and to never return errors under
diff --git a/hashes/src/ripemd160.rs b/hashes/src/ripemd160.rs
index 7c06e429..1581dd24 100644
--- a/hashes/src/ripemd160.rs
+++ b/hashes/src/ripemd160.rs
@@ -3,9 +3,9 @@
 //! RIPEMD160 implementation.
 //!
 
-use core::cmp;
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::{cmp, str};
 
 use crate::{FromSliceError, HashEngine as _};
 
diff --git a/hashes/src/sha1.rs b/hashes/src/sha1.rs
index 8f3a8cc1..00d8446d 100644
--- a/hashes/src/sha1.rs
+++ b/hashes/src/sha1.rs
@@ -3,9 +3,9 @@
 //! SHA1 implementation.
 //!
 
-use core::cmp;
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::{cmp, str};
 
 use crate::{FromSliceError, HashEngine as _};
 
diff --git a/hashes/src/sha256.rs b/hashes/src/sha256.rs
index 93bbd01e..8affa07b 100644
--- a/hashes/src/sha256.rs
+++ b/hashes/src/sha256.rs
@@ -7,9 +7,9 @@
 use core::arch::x86::*;
 #[cfg(all(feature = "std", target_arch = "x86_64"))]
 use core::arch::x86_64::*;
-use core::cmp;
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::{cmp, str};
 
 use crate::{sha256d, FromSliceError, HashEngine as _};
 
@@ -126,7 +126,7 @@ impl<I: SliceIndex<[u8]>> Index<I> for Midstate {
     fn index(&self, index: I) -> &Self::Output { &self.0[index] }
 }
 
-impl core::str::FromStr for Midstate {
+impl str::FromStr for Midstate {
     type Err = hex::HexToArrayError;
     fn from_str(s: &str) -> Result<Self, Self::Err> { hex::FromHex::from_hex(s) }
 }
diff --git a/hashes/src/sha256d.rs b/hashes/src/sha256d.rs
index 3628a878..fb3b0c5e 100644
--- a/hashes/src/sha256d.rs
+++ b/hashes/src/sha256d.rs
@@ -5,6 +5,7 @@
 
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::str;
 
 use crate::{sha256, FromSliceError};
 
diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs
index 593b9fd6..1a613cd3 100644
--- a/hashes/src/sha256t.rs
+++ b/hashes/src/sha256t.rs
@@ -3,10 +3,10 @@
 //! SHA256t implementation (tagged SHA256).
 //!
 
-use core::cmp;
 use core::marker::PhantomData;
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::{cmp, str};
 
 use crate::{sha256, FromSliceError};
 
@@ -66,7 +66,7 @@ impl<T: Tag> core::hash::Hash for Hash<T> {
     fn hash<H: core::hash::Hasher>(&self, h: &mut H) { self.0.hash(h) }
 }
 
-crate::internal_macros::hash_trait_impls!(256, false, T: Tag);
+crate::internal_macros::hash_trait_impls!(256, true, T: Tag);
 
 fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
     use crate::Hash as _;
@@ -91,8 +91,8 @@ fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
 ///     pub struct FooTag = hash_str("foo");
 ///
 ///     /// A foo hash.
-///     // Direction works just like the hash_newtype! macro.
-///     #[hash_newtype(backward)]
+///     // Direction works just like in case of hash_newtype! macro.
+///     #[hash_newtype(forward)]
 ///     pub struct FooHash(_);
 /// }
 /// ```
@@ -174,15 +174,6 @@ mod tests {
         108, 71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201,
     ];
 
-    // The digest created by sha256 hashing `&[0]` starting with `TEST_MIDSTATE`.
-    #[cfg(feature = "alloc")]
-    const HASH_ZERO_BACKWARD: &str =
-        "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed";
-    // And the same thing, forward.
-    #[cfg(feature = "alloc")]
-    const HASH_ZERO_FORWARD: &str =
-        "ed1382037800c9dd938dd8854f1a8863bcdeb6705069b4b56a66ec22519d5829";
-
     #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
     pub struct TestHashTag;
 
@@ -194,45 +185,44 @@ fn engine() -> sha256::HashEngine {
         }
     }
 
-    // We support manually implementing `Tag` and creating a tagged hash from it.
-    #[cfg(feature = "alloc")]
-    pub type TestHash = sha256t::Hash<TestHashTag>;
-
-    #[test]
-    #[cfg(feature = "alloc")]
-    fn manually_created_sha256t_hash_type() {
-        assert_eq!(TestHash::hash(&[0]).to_string(), HASH_ZERO_FORWARD);
-    }
-
-    // We also provide a macro to create the tag and the hash type.
-    sha256t_hash_newtype! {
-        /// Test detailed explanation.
-        struct NewTypeTagBackward = raw(TEST_MIDSTATE, 64);
-
-        /// A test hash.
-        #[hash_newtype(backward)]
-        struct NewTypeHashBackward(_);
+    #[cfg(feature = "schemars")]
+    impl schemars::JsonSchema for TestHashTag {
+        fn schema_name() -> String { "Hash".to_owned() }
+
+        fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
+            let mut schema: schemars::schema::SchemaObject = <String>::json_schema(gen).into();
+            schema.string = Some(Box::new(schemars::schema::StringValidation {
+                max_length: Some(64 * 2),
+                min_length: Some(64 * 2),
+                pattern: Some("[0-9a-fA-F]+".to_owned()),
+            }));
+            schema.into()
+        }
     }
 
-    #[test]
+    /// A hash tagged with `$name`.
     #[cfg(feature = "alloc")]
-    fn macro_created_sha256t_hash_type_backward() {
-        assert_eq!(NewTypeHashBackward::hash(&[0]).to_string(), HASH_ZERO_BACKWARD);
-    }
+    pub type TestHash = sha256t::Hash<TestHashTag>;
 
-    // We also provide a macro to create the tag and the hash type.
     sha256t_hash_newtype! {
         /// Test detailed explanation.
-        struct NewTypeTagForward = raw(TEST_MIDSTATE, 64);
+        struct NewTypeTag = raw(TEST_MIDSTATE, 64);
 
         /// A test hash.
-        #[hash_newtype(forward)]
-        struct NewTypeHashForward(_);
+        #[hash_newtype(backward)]
+        struct NewTypeHash(_);
     }
 
     #[test]
     #[cfg(feature = "alloc")]
-    fn macro_created_sha256t_hash_type_prints_forward() {
-        assert_eq!(NewTypeHashForward::hash(&[0]).to_string(), HASH_ZERO_FORWARD);
+    fn test_sha256t() {
+        assert_eq!(
+            TestHash::hash(&[0]).to_string(),
+            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
+        );
+        assert_eq!(
+            NewTypeHash::hash(&[0]).to_string(),
+            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
+        );
     }
 }
diff --git a/hashes/src/sha384.rs b/hashes/src/sha384.rs
index ce23c311..a9438753 100644
--- a/hashes/src/sha384.rs
+++ b/hashes/src/sha384.rs
@@ -4,6 +4,7 @@
 
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::str;
 
 use crate::{sha512, FromSliceError};
 
diff --git a/hashes/src/sha512.rs b/hashes/src/sha512.rs
index 3d1f0967..ae6155f9 100644
--- a/hashes/src/sha512.rs
+++ b/hashes/src/sha512.rs
@@ -3,9 +3,9 @@
 //! SHA512 implementation.
 //!
 
-use core::cmp;
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::{cmp, str};
 
 use crate::{FromSliceError, HashEngine as _};
 
diff --git a/hashes/src/sha512_256.rs b/hashes/src/sha512_256.rs
index bcaee921..e5c36624 100644
--- a/hashes/src/sha512_256.rs
+++ b/hashes/src/sha512_256.rs
@@ -9,6 +9,7 @@
 
 use core::ops::Index;
 use core::slice::SliceIndex;
+use core::str;
 
 use crate::{sha512, FromSliceError};
 
diff --git a/hashes/src/siphash24.rs b/hashes/src/siphash24.rs
index 81434aef..a20c1d33 100644
--- a/hashes/src/siphash24.rs
+++ b/hashes/src/siphash24.rs
@@ -5,7 +5,7 @@
 
 use core::ops::Index;
 use core::slice::SliceIndex;
-use core::{cmp, mem, ptr};
+use core::{cmp, mem, ptr, str};
 
 use crate::{FromSliceError, Hash as _, HashEngine as _};
 
diff --git a/justfile b/justfile
index 22dbfbc9..b73a6820 100644
--- a/justfile
+++ b/justfile
@@ -13,10 +13,6 @@ check:
 lint:
   cargo +$(cat ./nightly-version) clippy --workspace --all-targets --all-features -- --deny warnings
 
-# Run cargo fmt
-fmt:
-  cargo +$(cat ./nightly-version) fmt --all
-
 # Check the formatting
 format:
   cargo +$(cat ./nightly-version) fmt --all --check
diff --git a/nightly-version b/nightly-version
index e70649f1..683dfde9 100644
--- a/nightly-version
+++ b/nightly-version
@@ -1,1 +1,1 @@
-nightly-2024-05-04
+nightly-2024-04-28


Test patch:
diff --git a/base58/contrib/test_vars.sh b/base58/contrib/test_vars.sh
index c456fe0d..0c25edc7 100644
--- a/base58/contrib/test_vars.sh
+++ b/base58/contrib/test_vars.sh
@@ -1,6 +1,4 @@
 #!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
 # Test all these features with "std" enabled.
 FEATURES_WITH_STD=""
diff --git a/bitcoin/contrib/test_vars.sh b/bitcoin/contrib/test_vars.sh
index cab77a50..e1d02314 100644
--- a/bitcoin/contrib/test_vars.sh
+++ b/bitcoin/contrib/test_vars.sh
@@ -1,6 +1,4 @@
 #!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
 # Test all these features with "std" enabled.
 FEATURES_WITH_STD="rand-std serde secp-recovery bitcoinconsensus-std base64 ordered"
diff --git a/bitcoin/src/test_macros.rs b/bitcoin/src/test_macros.rs
index cbcbd4bd..bc2d05e2 100644
--- a/bitcoin/src/test_macros.rs
+++ b/bitcoin/src/test_macros.rs
@@ -3,7 +3,6 @@
 //! Bitcoin serde macros.
 //!
 //! This module provides internal macros used for unit tests.
-//!
 
 #[cfg(feature = "serde")]
 macro_rules! serde_round_trip (
diff --git a/bitcoin/tests/bip_174.rs b/bitcoin/tests/bip_174.rs
index 522360c6..fa32a0ce 100644
--- a/bitcoin/tests/bip_174.rs
+++ b/bitcoin/tests/bip_174.rs
@@ -317,7 +317,8 @@ fn parse_and_verify_keys(
 
         let path =
             derivation_path.into_derivation_path().expect("failed to convert derivation path");
-        let derived_priv = ext_priv.derive_priv(secp, &path).to_priv();
+        let derived_priv =
+            ext_priv.derive_priv(secp, &path).expect("failed to derive ext priv key").to_priv();
         assert_eq!(wif_priv, derived_priv);
         let derived_pub = derived_priv.public_key(secp);
         key_map.insert(derived_pub, derived_priv);
diff --git a/contrib/test_vars.sh b/contrib/test_vars.sh
index c490c367..3f96f89e 100644
--- a/contrib/test_vars.sh
+++ b/contrib/test_vars.sh
@@ -1,11 +1,4 @@
 #!/usr/bin/env bash
-#
-# Used by labeler.yaml
-#
-# Not to be confused with the per crate `test_vars.sh` used by
-# `rust-bitcoin-maintainer-tools-run_task.sh`.
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
-CRATES="$(cargo metadata --no-deps --format-version 1 | jq -j -r '.packages | map(.manifest_path | rtrimstr("/Cargo.toml") | ltrimstr("'"$PWD"'/")) | join(" ")')"
+CRATES="`cargo metadata --no-deps --format-version 1 | jq -j -r '.packages | map(.manifest_path | rtrimstr("/Cargo.toml") | ltrimstr("'$PWD'/")) | join(" ")'`"
 DEPS="recent minimal"
diff --git a/hashes/contrib/test_vars.sh b/hashes/contrib/test_vars.sh
index 4020401c..5e89c21e 100644
--- a/hashes/contrib/test_vars.sh
+++ b/hashes/contrib/test_vars.sh
@@ -1,6 +1,4 @@
 #!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
 # Test all these features with "std" enabled.
 FEATURES_WITH_STD="io serde small-hash schemars"
@@ -8,5 +6,8 @@ FEATURES_WITH_STD="io serde small-hash schemars"
 # Test all these features without "std" enabled.
 FEATURES_WITHOUT_STD="alloc serde small-hash"
 
+# Run address sanitizer with these features.
+ASAN_FEATURES="std io serde"
+
 # Run these examples.
 EXAMPLES=""
diff --git a/internals/contrib/test_vars.sh b/internals/contrib/test_vars.sh
index c1731775..759f5afb 100644
--- a/internals/contrib/test_vars.sh
+++ b/internals/contrib/test_vars.sh
@@ -1,6 +1,4 @@
 #!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
 # Test all these features with "std" enabled.
 FEATURES_WITH_STD="serde"
diff --git a/io/contrib/test_vars.sh b/io/contrib/test_vars.sh
index df392afa..0317fc3b 100644
--- a/io/contrib/test_vars.sh
+++ b/io/contrib/test_vars.sh
@@ -1,6 +1,4 @@
 #!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
 # Test all these features with "std" enabled.
 FEATURES_WITH_STD=""
diff --git a/units/contrib/test_vars.sh b/units/contrib/test_vars.sh
index c1731775..759f5afb 100644
--- a/units/contrib/test_vars.sh
+++ b/units/contrib/test_vars.sh
@@ -1,6 +1,4 @@
 #!/usr/bin/env bash
-# disable verify unused vars, despite the fact that they are used when sourced
-# shellcheck disable=SC2034
 
 # Test all these features with "std" enabled.
 FEATURES_WITH_STD="serde"