Repository: rust-bitcoin/rust-bitcoin
Pull request #2760 (2024-05-11 11:56:24)

bip32: derive_xpriv should not return a Result
We discussed in #2752 that `derive_priv` never fails.

This PR addresses that issue.

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


### Details

* **46** of **115**   **(40.0%)**  changed or added relevant lines in **2** files are covered.
* **4** unchanged lines in **2** files lost coverage.
* Overall coverage decreased (**-0.1%**) to **83.099%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [bitcoin/src/psbt/mod.rs](https://coveralls.io/builds/67434570/source?filename=bitcoin%2Fsrc%2Fpsbt%2Fmod.rs#L196) | 4 | 24 | 16.67%
| [bitcoin/src/bip32.rs](https://coveralls.io/builds/67434570/source?filename=bitcoin%2Fsrc%2Fbip32.rs#L159) | 42 | 91 | 46.15%
<!-- | **Total:** | **46** | **115** | **40.0%** | -->

|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [bitcoin/src/bip32.rs](https://coveralls.io/builds/67434570/source?filename=bitcoin%2Fsrc%2Fbip32.rs#L559) | 1 | 84.97% |
| [bitcoin/src/psbt/mod.rs](https://coveralls.io/builds/67434570/source?filename=bitcoin%2Fsrc%2Fpsbt%2Fmod.rs#L198) | 3 | 78.28% |
<!-- | **Total:** | **4** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/67434570/badge)](https://coveralls.io/builds/67434570) |
| :-- | --: |
| Change from base [Build 9024166987](https://coveralls.io/builds/67409283): |  -0.1% |
| Covered Lines: | 19220 |
| Relevant Lines: | 23129 |

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

storopoli: Should we tackle the `derive_pub` in this PR as well?

I draft something very simple like:

```rust
/// Public->Public child key derivation
    pub fn ckd_pub<C: secp256k1::Verification>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Xpub {
        let (sk, chain_code) = self.ckd_pub_tweak(i).expect("could not derive child key");
        let tweaked = self.public_key.add_exp_tweak(secp, &sk.into())?;

        // Check for invalid child number according to BIP32.
        // This means that if the derivation is hardened, we panic.

        match i {
            ChildNumber::Hardened { .. } => panic!("cannot derive hardened key from public key"),
            ChildNumber::Normal { .. } => Xpub {
                network: self.network,
                depth: self.depth + 1,
                parent_fingerprint: self.fingerprint(),
                child_number: i,
                public_key: tweaked,
                chain_code,
            },
        }
    }
```
apoelstra: > Should we tackle the `derive_pub` in this PR as well?

No, because it will require changing the `ChildNumber` structure to contain specific types rather than `u32`s, which is a much larger change. Then `ckd_pub` would accept the `HardenedChildNumber` type rather than the general `ChildNumber` enum. (If you want to tackle it, please do, but in another PR!)

Just replacing the error path with a panic is not the right approach :).
apoelstra: 7df90071c91316149e1c6e1ca984b649f68d6820 looks good except that `ckd_priv` should not return a result either. Then you can drop the new `expect`.
junderw: > [7df9007](https://github.com/rust-bitcoin/rust-bitcoin/commit/7df90071c91316149e1c6e1ca984b649f68d6820) looks good except that `ckd_priv` should not return a result either. Then you can drop the new `expect`.

Interestingly enough, all the Result-returning-calls within ckd_priv all expect using `statistically impossible to hit`...

Patch:
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/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/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 901793ed..8b15ba52 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).unwrap();
+    let child = root.derive_priv(&secp, &path);
     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 428461a9..93631fcc 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 };
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index b5db05ab..1b6d59c1 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/bip32.rs b/bitcoin/src/bip32.rs
index a913e44d..dd685507 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -583,27 +583,23 @@ pub fn to_keypair<C: secp256k1::Signing>(self, secp: &Secp256k1<C>) -> Keypair {
             .expect("BIP32 internal private key representation is broken")
     }
 
-    /// Attempts to derive an extended private key from a path.
+    /// Derives 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,
-    ) -> Result<Xpriv, Error> {
+    ) -> Xpriv {
         let mut sk: Xpriv = *self;
         for cnum in path.as_ref() {
-            sk = sk.ckd_priv(secp, *cnum)?;
+            sk = sk.ckd_priv(secp, *cnum)
         }
-        Ok(sk)
+        sk
     }
 
     /// Private->Private child key derivation
-    fn ckd_priv<C: secp256k1::Signing>(
-        &self,
-        secp: &Secp256k1<C>,
-        i: ChildNumber,
-    ) -> Result<Xpriv, Error> {
+    fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Xpriv {
         let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
         match i {
             ChildNumber::Normal { .. } => {
@@ -626,14 +622,14 @@ fn ckd_priv<C: secp256k1::Signing>(
         let tweaked =
             sk.add_tweak(&self.private_key.into()).expect("statistically impossible to hit");
 
-        Ok(Xpriv {
+        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
@@ -1001,7 +997,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).unwrap().to_string()[..], expected_sk);
+        assert_eq!(&sk.derive_priv(secp, &path).to_string()[..], expected_sk);
 
         // Check derivation convenience method for Xpub, should error
         // appropriately if any ChildNumber is hardened
@@ -1013,7 +1009,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).unwrap();
+            sk = sk.ckd_priv(secp, num);
             match num {
                 Normal { .. } => {
                     let pk2 = pk.ckd_pub(secp, num).unwrap();
diff --git a/bitcoin/src/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs
index cfe77811..152c6f5b 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"))]
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index 3d428fda..667719ec 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -762,7 +762,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 +808,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 +1366,7 @@ fn serialize_then_deserialize_output() {
             ChildNumber::from_normal_idx(31337).unwrap(),
         ];
 
-        sk = sk.derive_priv(secp, &dpath).unwrap();
+        sk = sk.derive_priv(secp, &dpath);
 
         let pk = Xpub::from_priv(secp, &sk);
 
diff --git a/contrib/crates.sh b/contrib/crates.sh
index f74787e4..4686b3b0 100755
--- a/contrib/crates.sh
+++ b/contrib/crates.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
 
 # 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/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/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..b3b32fb9 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
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/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/sha256t.rs b/hashes/src/sha256t.rs
index 593b9fd6..65e68f10 100644
--- a/hashes/src/sha256t.rs
+++ b/hashes/src/sha256t.rs
@@ -176,12 +176,10 @@ mod tests {
 
     // The digest created by sha256 hashing `&[0]` starting with `TEST_MIDSTATE`.
     #[cfg(feature = "alloc")]
-    const HASH_ZERO_BACKWARD: &str =
-        "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed";
+    const HASH_ZERO_BACKWARD: &str = "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed";
     // And the same thing, forward.
     #[cfg(feature = "alloc")]
-    const HASH_ZERO_FORWARD: &str =
-        "ed1382037800c9dd938dd8854f1a8863bcdeb6705069b4b56a66ec22519d5829";
+    const HASH_ZERO_FORWARD: &str = "ed1382037800c9dd938dd8854f1a8863bcdeb6705069b4b56a66ec22519d5829";
 
     #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
     pub struct TestHashTag;


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/tests/bip_174.rs b/bitcoin/tests/bip_174.rs
index fa32a0ce..522360c6 100644
--- a/bitcoin/tests/bip_174.rs
+++ b/bitcoin/tests/bip_174.rs
@@ -317,8 +317,7 @@ 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).expect("failed to derive ext priv key").to_priv();
+        let derived_priv = ext_priv.derive_priv(secp, &path).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..eb2b2511 100644
--- a/contrib/test_vars.sh
+++ b/contrib/test_vars.sh
@@ -4,8 +4,6 @@
 #
 # 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..d4073ddb 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"
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"