Repository: rust-bitcoin/rust-bitcoin
Pull request #2736 (2024-05-02 03:11:07)

CI: Use run_task from maintainer tools
First we do some clean up, then we pull the stuff that is specific to this repo out into separate tests, then as the last patch we switch over to use the new script for `rust-bitcoin-maintainer-tools` that was introduced in https://github.com/rust-bitcoin/rust-bitcoin-maintainer-tools/pull/4.




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


### 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.131%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/67256750/badge)](https://coveralls.io/builds/67256750) |
| :-- | --: |
| Change from base [Build 8908468276](https://coveralls.io/builds/67239844): |  0.0% |
| Covered Lines: | 19195 |
| Relevant Lines: | 23090 |

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

apoelstra: Nice!

I would like to move WASM and ASAN into the main maintainer-tools script eventually, but since they're currently specific to `hashes` fine to just move them out of the way for now.

So ACK 55b477d6e1478c859f479b8aeb16e1f24e57704c except that for the particular commit of maintainer-tools.
tcharding: @storopoli you are a docs boss! Will use all suggestions.

Patch:
diff --git a/.github/workflows/README.md b/.github/workflows/README.md
new file mode 100644
index 00000000..b8896a95
--- /dev/null
+++ b/.github/workflows/README.md
@@ -0,0 +1,34 @@
+# 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 da20ed1d..7f778e8f 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -1,4 +1,5 @@
-on:
+---   # rust-bitcoin CI: If you edit this file please update README.md
+on:   # yamllint disable-line rule:truthy
   push:
     branches:
       - master
@@ -29,12 +30,18 @@ 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: ./contrib/run_task.sh stable
+        run: ./maintainer-tools/ci/run_task.sh stable
 
   Nightly:                      # 2 jobs, one per manifest.
     name: Test - nightly toolchain
@@ -47,6 +54,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@v1
         with:
@@ -54,7 +67,7 @@ jobs:
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./contrib/run_task.sh nightly
+        run: ./maintainer-tools/ci/run_task.sh nightly
 
   MSRV:                         # 2 jobs, one per manifest.
     name: Test - 1.56.1 toolchain
@@ -66,14 +79,20 @@ 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: "Copy lock file"
+      - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./contrib/run_task.sh msrv
+        run: ./maintainer-tools/ci/run_task.sh msrv
 
   Lint:
     name: Lint - nightly toolchain
@@ -86,6 +105,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@v1
         with:
@@ -95,7 +120,7 @@ jobs:
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./contrib/run_task.sh lint
+        run: ./maintainer-tools/ci/run_task.sh lint
 
   Docs:
     name: Docs - stable toolchain
@@ -107,12 +132,18 @@ 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: "Copy lock file"
+      - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./contrib/run_task.sh docs
+        run: ./maintainer-tools/ci/run_task.sh docs
 
   Docsrs:
     name: Docs - nightly toolchain
@@ -125,6 +156,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@v1
         with:
@@ -132,7 +169,7 @@ jobs:
       - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./contrib/run_task.sh docsrs
+        run: ./maintainer-tools/ci/run_task.sh docsrs
 
   Bench:
     name: Bench - nightly toolchain
@@ -145,14 +182,20 @@ 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: "Copy lock file"
+      - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
       - name: "Run test script"
-        run: ./contrib/run_task.sh bench
+        run: ./maintainer-tools/ci/run_task.sh bench
 
   Arch32bit:
     name: Test 32-bit version
@@ -213,7 +256,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
@@ -230,12 +273,12 @@ jobs:
           toolchain: ${{ needs.Prepare.outputs.nightly_version }}
       - name: Install src
         run: rustup component add rust-src
-      - name: "Copy lock file"
+      - name: "Set dependencies"
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh asan
+      - name: "Run sanitizer script"
+        run: cd ./hashes && ./contrib/sanitizer.sh
 
-  WASM: # hashes crate only.
+  WASM:                         # hashes crate only.
     name: WASM - stable toolchain
     runs-on: ubuntu-latest
     strategy:
@@ -246,25 +289,22 @@ jobs:
         uses: actions/checkout@v4
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
-      - name: "Run test script"
-        run: ./contrib/run_task.sh wasm
+      - name: "Run wasm script"
+        run: cd hashes && ./contrib/wasm.sh
 
-  Schemars: # hashes crate only.
+  Schemars:                     # hashes crate only.
     name: Schemars - stable toolchain
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
-      matrix:
-        dep: [recent]
+      # Note we do not use the recent lock file for schemars testing.
     steps:
       - name: "Checkout repo"
         uses: actions/checkout@v4
       - name: "Select toolchain"
         uses: dtolnay/rust-toolchain@stable
-      - name: "Copy lock file"
-        run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh schemars
+      - name: "Run schemars test"
+        run: cd hashes/extended_tests/schemars && cargo test
 
   Kani:
     name: Kani codegen - stable toolchain
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index a913e44d..1014ffdc 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -45,7 +45,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 +566,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),
         })
     }
@@ -621,7 +621,7 @@ fn ckd_priv<C: secp256k1::Signing>(
 
         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");
@@ -742,7 +742,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))
             }
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/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs
index 152c6f5b..6c722f2f 100644
--- a/bitcoin/src/psbt/map/input.rs
+++ b/bitcoin/src/psbt/map/input.rs
@@ -514,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/taproot/mod.rs b/bitcoin/src/taproot/mod.rs
index 87318ada..60ce28ee 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -36,6 +36,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 +44,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 +52,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
new file mode 100755
index 00000000..4686b3b0
--- /dev/null
+++ b/contrib/crates.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+# 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/get_matrix.sh b/contrib/get_matrix.sh
deleted file mode 100755
index d72a865b..00000000
--- a/contrib/get_matrix.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/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/run_task.sh b/contrib/run_task.sh
deleted file mode 100755
index 424dd902..00000000
--- a/contrib/run_task.sh
+++ /dev/null
@@ -1,341 +0,0 @@
-#!/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/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
new file mode 100755
index 00000000..5a1e56c2
--- /dev/null
+++ b/hashes/contrib/sanitizer.sh
@@ -0,0 +1,21 @@
+#!/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
new file mode 100755
index 00000000..6139e307
--- /dev/null
+++ b/hashes/contrib/wasm.sh
@@ -0,0 +1,12 @@
+#!/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/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/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/sha256t.rs b/hashes/src/sha256t.rs
index 65e68f10..c98d4e61 100644
--- a/hashes/src/sha256t.rs
+++ b/hashes/src/sha256t.rs
@@ -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,13 +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;
 
@@ -192,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"
+        );
     }
 }


Test patch:
diff --git a/contrib/test_vars.sh b/contrib/test_vars.sh
index 3f96f89e..eb2b2511 100644
--- a/contrib/test_vars.sh
+++ b/contrib/test_vars.sh
@@ -1,4 +1,9 @@
 #!/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`.
 
 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 5e89c21e..d4073ddb 100644
--- a/hashes/contrib/test_vars.sh
+++ b/hashes/contrib/test_vars.sh
@@ -6,8 +6,5 @@ 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=""