Repository: rust-bitcoin/rust-bitcoin
Pull request #2707 (2024-04-23 03:01:24)

Default to forward for tagged hashes
First three patches are preparation, improvements to the units tests in `sha256t`.

From the final patch:

    Displaying backward is an anomaly of Bitcoin Core's early days and the
    double SHA256 hash type. We should not let this unfortunate beast leak
    out into other places.
    
    Default to displaying forward when creating a new tagged hash and remove
    all the explicit attributes from `bitcoin` that just clutter the code.
 
This is an API break and may quietly break some users downstream - eventually we should stop doing that sort of thing.


Patch:
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/manage-pr.yml b/.github/workflows/manage-pr.yml
index 49407eea..1ae25001 100644
--- a/.github/workflows/manage-pr.yml
+++ b/.github/workflows/manage-pr.yml
@@ -24,4 +24,3 @@ jobs:
         uses: actions/labeler@v5
         with:
           configuration-path: master/.github/labeler.yml
-          sync-labels: true
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index da20ed1d..e3000372 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -11,6 +11,8 @@ jobs:
   Prepare:
     runs-on: ubuntu-latest
     outputs:
+      crates: ${{ steps.get_matrix.outputs.crates }}
+      deps: ${{ steps.get_matrix.outputs.deps }}
       nightly_version: ${{ steps.read_toolchain.outputs.nightly_version }}
     steps:
       - name: Checkout Crate
@@ -18,261 +20,221 @@ jobs:
       - name: Read nightly version
         id: read_toolchain
         run: echo "nightly_version=$(cat nightly-version)" >> $GITHUB_OUTPUT
+      - name: Prepare tests
+        id: get_matrix
+        run: contrib/get_matrix.sh
 
-  Stable:                       # 2 jobs, one per manifest.
+  Stable:
+    needs: Prepare
     name: Test - stable toolchain
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        dep: [minimal, recent]
+        crate: ${{ fromJson(needs.Prepare.outputs.crates) }}
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [test, docs, feature_matrix, dup_deps]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
+        # https://github.com/dtolnay/rust-toolchain
         uses: dtolnay/rust-toolchain@stable
-      - name: "Set dependencies"
+      - name: Set dependencies
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh stable
+      - name: Running test script
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
-  Nightly:                      # 2 jobs, one per manifest.
-    name: Test - nightly toolchain
+  Beta:
     needs: Prepare
+    name: Test - beta toolchain
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        dep: [minimal, recent]
-    steps:
-      - name: "Checkout repo"
-        uses: actions/checkout@v4
-      - name: "Select toolchain"
-        uses: dtolnay/rust-toolchain@v1
-        with:
-          toolchain: ${{ needs.Prepare.outputs.nightly_version }}
-      - name: "Set dependencies"
-        run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh nightly
-
-  MSRV:                         # 2 jobs, one per manifest.
-    name: Test - 1.56.1 toolchain
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        dep: [minimal, recent]
+        crate: ${{ fromJson(needs.Prepare.outputs.crates) }}
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [test]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
-        uses: dtolnay/rust-toolchain@stable
-        with:
-          toolchain: "1.56.1"
-      - name: "Copy lock file"
+      - name: Checkout Toolchain
+        uses: dtolnay/rust-toolchain@beta
+      - name: Set dependencies
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh msrv
+      - name: Running test script
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
-  Lint:
-    name: Lint - nightly toolchain
+  Nightly:
     needs: Prepare
+    name: Test - nightly toolchain
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        dep: [recent]
+        crate: ${{ fromJson(needs.Prepare.outputs.crates) }}
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [test, lint, bench, docsrs]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
         uses: dtolnay/rust-toolchain@v1
         with:
           toolchain: ${{ needs.Prepare.outputs.nightly_version }}
       - name: Install clippy
         run: rustup component add clippy
-      - name: "Set dependencies"
-        run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh lint
-
-  Docs:
-    name: Docs - stable toolchain
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        dep: [recent]
-    steps:
-      - name: "Checkout repo"
-        uses: actions/checkout@v4
-      - 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
+      - name: Running test script
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
-  Docsrs:
-    name: Docs - nightly toolchain
-    needs: Prepare
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        dep: [recent]
-    steps:
-      - name: "Checkout repo"
-        uses: actions/checkout@v4
-      - name: "Select toolchain"
-        uses: dtolnay/rust-toolchain@v1
-        with:
-          toolchain: ${{ needs.Prepare.outputs.nightly_version }}
-      - name: "Set dependencies"
-        run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh docsrs
-
-  Bench:
-    name: Bench - nightly toolchain
+  MSRV:
     needs: Prepare
+    name: Test - 1.56.1 toolchain
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        dep: [recent]
+        crate: ${{ fromJson(needs.Prepare.outputs.crates) }}
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [test, feature_matrix]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
-        uses: dtolnay/rust-toolchain@v1
+      - name: Checkout Toolchain
+        uses: dtolnay/rust-toolchain@stable
         with:
-          toolchain: ${{ needs.Prepare.outputs.nightly_version }}
-      - name: "Copy lock file"
+          toolchain: "1.56.1"
+      - name: Set dependencies
         run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh bench
+      - name: Running test script
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
   Arch32bit:
     name: Test 32-bit version
     runs-on: ubuntu-latest
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
         uses: dtolnay/rust-toolchain@stable
-      - name: "Add architecture i386"
+      - name: Add architecture i386
         run: sudo dpkg --add-architecture i386
-      - name: "Install i686 gcc"
+      - name: Install i686 gcc
         run: sudo apt-get update -y && sudo apt-get install -y gcc-multilib
-      - name: "Install target"
+      - name: Install target
         run: rustup target add i686-unknown-linux-gnu
-      - name: "Run test on i686"
+      - name: Run test on i686
         run: cargo test --target i686-unknown-linux-gnu
 
   Cross:
-    name: Cross test - stable toolchain
+    name: Cross test
     if: ${{ !github.event.act }}
     runs-on: ubuntu-latest
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
         uses: dtolnay/rust-toolchain@stable
-      - name: "Install target"
+      - name: Install target
         run: rustup target add s390x-unknown-linux-gnu
-      - name: "Install cross"
+      - name: install cross
         run: cargo install cross --locked
-      - name: "Run cross test"
+      - name: run cross test
         run: cross test --target s390x-unknown-linux-gnu
 
   Embedded:
-    name: Embedded - nightly toolchain
     needs: Prepare
     runs-on: ubuntu-latest
     env:
       RUSTFLAGS: "-C link-arg=-Tlink.x"
       CARGO_TARGET_THUMBV7M_NONE_EABI_RUNNER: "qemu-system-arm -cpu cortex-m3 -machine mps2-an385 -nographic -semihosting-config enable=on,target=native -kernel"
     steps:
-      - name: "Checkout repo"
+      - name: Checkout
         uses: actions/checkout@v4
-      - name: "Set up QEMU"
+      - name: Set up QEMU
         run: sudo apt update && sudo apt install -y qemu-system-arm gcc-arm-none-eabi
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
         uses: dtolnay/rust-toolchain@v1
         with:
           toolchain: ${{ needs.Prepare.outputs.nightly_version }}
           targets: thumbv7m-none-eabi
-      - name: "Install rust-src"
+      - name: Install src
         run: rustup component add rust-src
-      - name: "Run bitcoin/embedded"
+      - name: Run bitcoin/embedded
         run: cd bitcoin/embedded && cargo run --target thumbv7m-none-eabi
-      - name: "Run hashes/embedded no alloc"
+      - name: Run hashes/embedded no alloc
         run: cd hashes/embedded && cargo run --target thumbv7m-none-eabi
-      - name: "Run hashes/embedded with alloc"
+      - name: Run hashes/embedded with alloc
         run: cd hashes/embedded && cargo run --target thumbv7m-none-eabi --features=alloc
 
-  ASAN: # hashes crate only.
-    name: ASAN - nightly toolchain
+  ASAN:
     needs: Prepare
+    name: Address sanitizer     # hashes crate only.
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        dep: [recent]
+        crate: [hashes]
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [asan]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
         uses: dtolnay/rust-toolchain@v1
         with:
           toolchain: ${{ needs.Prepare.outputs.nightly_version }}
       - name: Install src
         run: rustup component add rust-src
-      - name: "Copy lock file"
-        run: cp Cargo-${{ matrix.dep }}.lock Cargo.lock
-      - name: "Run test script"
-        run: ./contrib/run_task.sh asan
+      - name: Running address sanitizer
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
-  WASM: # hashes crate only.
-    name: WASM - stable toolchain
+  WASM:
+    needs: Prepare
+    name: WebAssembly Build # hashes crate only.
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
-      # Note we do not use the recent lock file for wasm testing.
+      matrix:
+        crate: [hashes]
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [wasm]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout Toolchain
         uses: dtolnay/rust-toolchain@stable
-      - name: "Run test script"
-        run: ./contrib/run_task.sh wasm
+      - name: Running WASM build
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
-  Schemars: # hashes crate only.
-    name: Schemars - stable toolchain
+  Schemars:
+    needs: Prepare
+    name: Schemars
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        dep: [recent]
+        crate: [hashes]
+        dep: ${{ fromJson(needs.Prepare.outputs.deps) }}
+        task: [schemars]
     steps:
-      - name: "Checkout repo"
+      - name: Checkout Crate
         uses: actions/checkout@v4
-      - name: "Select toolchain"
+      - name: Checkout 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: Running schemars test
+        run: ./contrib/run_task.sh ${{ matrix.crate }} ${{ matrix.task }}
 
   Kani:
-    name: Kani codegen - stable toolchain
     runs-on: ubuntu-20.04
     steps:
-      - name: "Checkout repo"
+      - name: 'Checkout your code.'
         uses: actions/checkout@v4
-      - name: "Build Kani proofs"
+
+      - name: 'Kani build proofs'
         uses: model-checking/kani-github-action@v1.1
         with:
-          args: "--only-codegen"
+          kani-version: '0.48.0'
+          args: '--only-codegen'
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index f30294e9..11991ea3 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -25,9 +25,9 @@ dependencies = [
 
 [[package]]
 name = "base64"
-version = "0.22.0"
+version = "0.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
+checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
 
 [[package]]
 name = "bech32"
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 57490f67..8383f522 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -25,9 +25,9 @@ dependencies = [
 
 [[package]]
 name = "base64"
-version = "0.22.0"
+version = "0.21.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
+checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
 
 [[package]]
 name = "bech32"
diff --git a/base58/README.md b/base58/README.md
index d69760c5..211b8574 100644
--- a/base58/README.md
+++ b/base58/README.md
@@ -1,4 +1,5 @@
-# Bitcoin base58 encoding
+Bitcoin base58 encoding
+=======================
 
 This crate provides encoding and decoding of base58 strings as defined by the Bitcoin ecosystem
 including the checksum.
@@ -20,10 +21,12 @@ obviously named ones differ from this crate because:
 This crate uses [bitcoin_hashes](https://crates.io/crates/bitcoin_hashes) when hashing to calculate
 the checksum.
 
+
 ## Minimum Supported Rust Version (MSRV)
 
 This library should always compile with any combination of features on **Rust 1.56.1**.
 
+
 ## Licensing
 
 The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](LICENSE).
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 6202839e..4f4a6c8b 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -38,7 +38,7 @@ io = { package = "bitcoin-io", version = "0.1.1", default-features = false, feat
 secp256k1 = { version = "0.29.0", default-features = false, features = ["hashes", "alloc"] }
 units = { package = "bitcoin-units", version = "0.1.0", default-features = false, features = ["alloc"] }
 
-base64 = { version = "0.22.0", optional = true }
+base64 = { version = "0.21.3", optional = true }
 ordered = { version = "0.2.0", optional = true }
 # Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
 bitcoinconsensus = { version = "0.105.0+25.1", default-features = false, optional = true }
diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs
index 428461a9..4de4cf66 100644
--- a/bitcoin/examples/ecdsa-psbt.rs
+++ b/bitcoin/examples/ecdsa-psbt.rs
@@ -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/src/address/mod.rs b/bitcoin/src/address/mod.rs
index 89658144..ca67b99c 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -154,7 +154,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                     NetworkKind::Main => PUBKEY_ADDRESS_PREFIX_MAIN,
                     NetworkKind::Test => PUBKEY_ADDRESS_PREFIX_TEST,
                 };
-                prefixed[1..].copy_from_slice(hash.as_byte_array());
+                prefixed[1..].copy_from_slice(&hash[..]);
                 base58::encode_check_to_fmt(fmt, &prefixed[..])
             }
             P2sh { hash, network } => {
@@ -163,7 +163,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
                     NetworkKind::Main => SCRIPT_ADDRESS_PREFIX_MAIN,
                     NetworkKind::Test => SCRIPT_ADDRESS_PREFIX_TEST,
                 };
-                prefixed[1..].copy_from_slice(hash.as_byte_array());
+                prefixed[1..].copy_from_slice(&hash[..]);
                 base58::encode_check_to_fmt(fmt, &prefixed[..])
             }
             Segwit { program, hrp } => {
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index a913e44d..fa019bac 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -30,12 +30,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 +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 69d67f9b..56d07e69 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -61,7 +61,6 @@ 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(_);
 }
 
@@ -575,10 +574,6 @@ pub fn into_transaction(self) -> R { self.tx }
 
     /// Encodes the BIP341 signing data for any flag type into a given object implementing the
     /// [`io::Write`] trait.
-    ///
-    /// In order to sign, the data written by this function must be hashed using a tagged hash. This
-    /// can be achieved by using the [`TapSighash::engine()`] function, writing to the engine, then
-    /// finalizing the hash with [`TapSighash::from_engine()`].
     pub fn taproot_encode_signing_data_to<W: Write + ?Sized, T: Borrow<TxOut>>(
         &mut self,
         writer: &mut W,
@@ -770,10 +765,6 @@ pub fn taproot_script_spend_signature_hash<S: Into<TapLeafHash>, T: Borrow<TxOut
     /// `script_code` is dependent on the type of the spend transaction. For p2wpkh use
     /// [`Script::p2wpkh_script_code`], for p2wsh just pass in the witness script. (Also see
     /// [`Self::p2wpkh_signature_hash`] and [`SighashCache::p2wsh_signature_hash`].)
-    ///
-    /// In order to sign, the data written by this function must be hashed using a double SHA256
-    /// hash. This can be achieved either by using the [`hashes::sha256d::Hash`] type or one of the
-    /// custom sighash types in this module ([`SegwitV0Sighash`] and [`LegacySighash`]).
     pub fn segwit_v0_encode_signing_data_to<W: Write + ?Sized>(
         &mut self,
         writer: &mut W,
@@ -818,9 +809,9 @@ pub fn segwit_v0_encode_signing_data_to<W: Write + ?Sized>(
             let mut single_enc = LegacySighash::engine();
             self.tx.borrow().output[input_index].consensus_encode(&mut single_enc)?;
             let hash = LegacySighash::from_engine(single_enc);
-            writer.write_all(hash.as_byte_array())?;
+            writer.write_all(&hash[..])?;
         } else {
-            writer.write_all(zero_hash.as_byte_array())?;
+            writer.write_all(&zero_hash[..])?;
         }
 
         self.tx.borrow().lock_time.consensus_encode(writer)?;
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 60ce28ee..87318ada 100644
--- a/bitcoin/src/taproot/mod.rs
+++ b/bitcoin/src/taproot/mod.rs
@@ -36,7 +36,6 @@
     /// 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");
@@ -44,7 +43,6 @@
     /// Tagged hash used in taproot trees.
     ///
     /// See BIP-340 for tagging rules.
-    #[hash_newtype(forward)]
     pub struct TapNodeHash(_);
 
     pub struct TapTweakTag = hash_str("TapTweak");
@@ -52,7 +50,6 @@
     /// 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/run_task.sh b/contrib/run_task.sh
index 424dd902..094289be 100755
--- a/contrib/run_task.sh
+++ b/contrib/run_task.sh
@@ -1,52 +1,13 @@
-#!/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
+#!/bin/env bash
 
 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"
+    crate="$1"
+    task="$2"
 
     check_required_commands
 
@@ -56,27 +17,34 @@ main() {
     locale
     env
 
+    cd "$crate"
+
+    # Building the fuzz crate is more-or-less just a sanity check.
+    if [ "$crate" == "fuzz" ]
+    then
+        cargo --locked build
+        exit 0
+    fi
+
+    # Every crate must define EXAMPLES.
+    . contrib/test_vars.sh || exit 1
+
     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
-            ;;
+	test)
+	    do_test
+	    ;;
+
+	feature_matrix)
+	    do_feature_matrix
+	    ;;
+
+	lint)
+	    do_lint
+	    ;;
+
+	dup_deps)
+	    do_dup_deps
+	    ;;
 
 	docs)
             build_docs_with_stable_toolchain
@@ -86,51 +54,27 @@ main() {
             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
 	    ;;
 
+	bench)
+	    do_bench
+	    ;;
+
 	schemars)
-            # hashes crate only.
 	    do_schemars
 	    ;;
-
-        *)
-            err "Error: unknown task $task"
-            ;;
+	*)
+	    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"
@@ -139,15 +83,15 @@ do_test() {
     $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"
+    for example in $EXAMPLES; do
+	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
+	./contrib/extra_tests.sh
     fi
 }
 
@@ -159,8 +103,7 @@ do_feature_matrix() {
     $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
+    # All crates have a "std" feature.
     loop_features "std" "$FEATURES_WITH_STD"
 
     # All but `bitcoin` crate have an "alloc" feature, this tests it
@@ -203,15 +146,20 @@ loop_features() {
     fi
 }
 
-# Lint the workspace.
+# Lint the workspace then the individual crate examples.
 do_lint() {
     need_nightly
+
+    # Use the current (recent/minimal) lock file.
     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
+    $cargo clippy --workspace -- -D warnings
+
+    for example in $EXAMPLES; do
+	name=$(echo "$example" | cut -d ':' -f 1)
+	features=$(echo "$example" | cut -d ':' -f 2)
+	$cargo clippy --example "$name" --features="$features" -- -D warnings
+    done
 }
 
 # We should not have any duplicate dependencies. This catches mistakes made upgrading dependencies
@@ -225,7 +173,7 @@ do_dup_deps() {
         # 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.
+        # Temporarily allow 2 versions of `hashes` and `hex` while we upgrade.
         cargo tree  --target=all --all-features --duplicates \
             | grep '^[0-9A-Za-z]' \
             | grep -v 'syn' \
@@ -245,6 +193,7 @@ do_dup_deps() {
 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
 }
 
@@ -252,45 +201,20 @@ build_docs_with_nightly_toolchain() {
 # 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
+    RUSTDOCFLAGS="-D warnings" $cargo doc --all-features
 }
 
-# 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'                    \
@@ -302,8 +226,17 @@ do_asan() {
     # 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
+# Bench only works with a non-stable toolchain (nightly, beta).
+do_bench() {
+    RUSTFLAGS='--cfg=bench' cargo bench
+}
+
+# This is only relevant for hashes.
+do_schemars() {
+    cd "extended_tests/schemars" > /dev/null
+    cargo test
 }
 
 # Check all the commands we use are present in the current environment.
diff --git a/fuzz/README.md b/fuzz/README.md
index 38b7d854..f53a22e4 100644
--- a/fuzz/README.md
+++ b/fuzz/README.md
@@ -6,9 +6,7 @@ honggfuzz.
 To run the fuzz-tests as in CI -- briefly fuzzing every target -- simply
 run
 
-```bash
-./fuzz.sh
-```
+    ./fuzz.sh
 
 in this directory.
 
@@ -18,13 +16,11 @@ recently-released binutils 2.39 has changed their API in a breaking way.
 
 On Nix, you can obtain these libraries by running
 
-```bash
-nix-shell -p libopcodes_2_38 -p libunwind
-```
+    nix-shell -p libopcodes_2_38 -p libunwind
 
-and then run `fuzz.sh` as above.
+and then run fuzz.sh as above.
 
-## Fuzzing with weak cryptography
+# Fuzzing with weak cryptography
 
 You may wish to replace the hashing and signing code with broken crypto,
 which will be faster and enable the fuzzer to do otherwise impossible
@@ -39,43 +35,35 @@ Please let us know if you are interested in taking this on!
 Meanwhile, to use the broken crypto, simply compile (and run the fuzzing
 scripts) with
 
-```bash
-RUSTFLAGS="--cfg=hashes_fuzz --cfg=secp256k1_fuzz"
-```
+    RUSTFLAGS="--cfg=hashes_fuzz --cfg=secp256k1_fuzz"
 
 which will replace the hashing library with broken hashes, and the
-`secp256k1` library with broken cryptography.
+secp256k1 library with broken cryptography.
 
 Needless to say, NEVER COMPILE REAL CODE WITH THESE FLAGS because if a
 fuzzer can break your crypto, so can anybody.
 
-## Long-term fuzzing
+# Long-term fuzzing
 
 To see the full list of targets, the most straightforward way is to run
 
-```bash
-source ./fuzz-util.sh
-listTargetNames
-```
+    source ./fuzz-util.sh
+    listTargetNames
 
 To run each of them for an hour, run
 
-```bash
-./cycle.sh
-```
+    ./cycle.sh
 
 To run a single fuzztest indefinitely, run
 
-```bash
-HFUZZ_BUILD_ARGS='--features honggfuzz_fuzz' cargo hfuzz run <target>
-```
+    HFUZZ_BUILD_ARGS='--features honggfuzz_fuzz' cargo hfuzz run <target>
 
 This script uses the `chrt` utility to try to reduce the priority of the
 jobs. If you would like to run for longer, the most straightforward way
 is to edit `cycle.sh` before starting. To run the fuzz-tests in parallel,
 you will need to implement a custom harness.
 
-## Adding fuzz tests
+# Adding fuzz tests
 
 All fuzz tests can be found in the `fuzz_target/` directory. Adding a new
 one is as simple as copying an existing one and editing the `do_test`
@@ -90,25 +78,21 @@ it to the generated `Cargo.toml`.
 Once you've added a fuzztest, regenerate the `Cargo.toml` and CI job by
 running
 
-```bash
-./generate-files.sh
-```
+    ./generate-files.sh
 
 Then to test your fuzztest, run
 
-```bash
-./fuzz.sh <target>
-```
+    ./fuzz.sh <target>
 
 If it is working, you will see a rapid stream of data for many seconds
 (you can hit Ctrl+C to stop it early). If not, you should quickly see
 an error.
 
-## Reproducing Failures
+# Reproducing Failures
 
 If a fuzztest fails, it will exit with a summary which looks something like
 
-```text
+```
 ...
  fuzzTarget      : hfuzz_target/x86_64-unknown-linux-gnu/release/hashes_sha256 
 CRASH:
@@ -124,9 +108,8 @@ The final line is a hex-encoded version of the input that caused the crash. You
 can test this directly by editing the `duplicate_crash` test to copy/paste the
 hex output into the call to `extend_vec_from_hex`. Then run the test with
 
-```bash
-cargo test
-```
+    cargo test
 
 Note that if you set your `RUSTFLAGS` while fuzzing (see above) you must make
 sure they are set the same way when running `cargo test`.
+
diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh
index b3b32fb9..bbb36ea4 100755
--- a/fuzz/generate-files.sh
+++ b/fuzz/generate-files.sh
@@ -22,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"
@@ -40,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:
@@ -58,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)
         ]
@@ -67,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/hashes/CHANGELOG.md b/hashes/CHANGELOG.md
index ca96b020..887776a5 100644
--- a/hashes/CHANGELOG.md
+++ b/hashes/CHANGELOG.md
@@ -1,3 +1,20 @@
+# 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/README.md b/hashes/README.md
index 9b342bd1..4ce91fa9 100644
--- a/hashes/README.md
+++ b/hashes/README.md
@@ -20,8 +20,7 @@ Contributions are welcome, including additional hash function implementations.
 To assist devs in catching errors _before_ running CI we provide some githooks. If you do not
 already have locally configured githooks you can use the ones in this repository by running, in the
 root directory of the repository:
-
-```bash
+```
 git config --local core.hooksPath githooks/
 ```
 
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..9a83afa0 100644
--- a/hashes/src/lib.rs
+++ b/hashes/src/lib.rs
@@ -12,7 +12,8 @@
 //! Hashing a single byte slice or a string:
 //!
 //! ```rust
-//! use bitcoin_hashes::{sha256, Hash as _};
+//! use bitcoin_hashes::sha256;
+//! use bitcoin_hashes::Hash;
 //!
 //! let bytes = [0u8; 5];
 //! let hash_of_bytes = sha256::Hash::hash(&bytes);
@@ -23,7 +24,8 @@
 //! Hashing content from a reader:
 //!
 //! ```rust
-//! use bitcoin_hashes::{sha256, Hash as _};
+//! use bitcoin_hashes::sha256;
+//! use bitcoin_hashes::Hash;
 //!
 //! #[cfg(std)]
 //! # fn main() -> std::io::Result<()> {
@@ -42,7 +44,8 @@
 //! Hashing content by [`std::io::Write`] on HashEngine:
 //!
 //! ```rust
-//! use bitcoin_hashes::{sha256, Hash as _};
+//! use bitcoin_hashes::sha256;
+//! use bitcoin_hashes::Hash;
 //! use std::io::Write;
 //!
 //! #[cfg(std)]
@@ -123,7 +126,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 +161,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..2d03fdd7 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) }
 }
@@ -1027,9 +1027,10 @@ fn sha256_serde() {
 
     #[cfg(target_arch = "wasm32")]
     mod wasm_tests {
+        extern crate wasm_bindgen_test;
+        use self::wasm_bindgen_test::*;
         use super::*;
-        #[test]
-        #[wasm_bindgen_test::wasm_bindgen_test]
+        #[wasm_bindgen_test]
         fn sha256_tests() {
             test();
             midstate();
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 c98d4e61..13b19c64 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, true, T: Tag);
+crate::internal_macros::hash_trait_impls!(256, false, 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 in case of hash_newtype! macro.
-///     #[hash_newtype(forward)]
+///     // Direction works just like the hash_newtype! macro.
+///     #[hash_newtype(backward)]
 ///     pub struct FooHash(_);
 /// }
 /// ```
@@ -174,6 +174,13 @@ 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;
 
@@ -185,44 +192,45 @@ fn engine() -> sha256::HashEngine {
         }
     }
 
-    #[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()
-        }
-    }
-
-    /// A hash tagged with `$name`.
+    // 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 NewTypeTag = raw(TEST_MIDSTATE, 64);
+        struct NewTypeTagBackward = raw(TEST_MIDSTATE, 64);
 
         /// A test hash.
         #[hash_newtype(backward)]
-        struct NewTypeHash(_);
+        struct NewTypeHashBackward(_);
+    }
+
+    #[test]
+    #[cfg(feature = "alloc")]
+    fn macro_created_sha256t_hash_type_backward() {
+        assert_eq!(NewTypeHashBackward::hash(&[0]).to_string(), HASH_ZERO_BACKWARD);
+    }
+
+    // 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);
+
+        /// A test hash.
+        #[hash_newtype(forward)]
+        struct NewTypeHashForward(_);
     }
 
     #[test]
     #[cfg(feature = "alloc")]
-    fn test_sha256t() {
-        assert_eq!(
-            TestHash::hash(&[0]).to_string(),
-            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
-        );
-        assert_eq!(
-            NewTypeHash::hash(&[0]).to_string(),
-            "29589d5122ec666ab5b4695070b6debc63881a4f85d88d93ddc90078038213ed"
-        );
+    fn macro_created_sha256t_hash_type_prints_forward() {
+        assert_eq!(NewTypeHashForward::hash(&[0]).to_string(), HASH_ZERO_FORWARD);
     }
 }
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/internals/README.md b/internals/README.md
index 66aae69c..79aa2980 100644
--- a/internals/README.md
+++ b/internals/README.md
@@ -1,4 +1,5 @@
-# Rust Bitcoin Internals
+Rust Bitcoin Internals
+======================
 
 This crate is only meant to be used internally by crates in the
 [rust-bitcoin](https://github.com/rust-bitcoin) ecosystem.
diff --git a/io/README.md b/io/README.md
index f276437b..e38e2d06 100644
--- a/io/README.md
+++ b/io/README.md
@@ -1,8 +1,9 @@
-# Rust-Bitcoin IO Library
+Rust-Bitcoin IO Library
+=======================
 
 The `std::io` module is not exposed in `no-std` Rust so building `no-std` applications which require
 reading and writing objects via standard traits is not generally possible. Thus, this library exists
-to export a minimal version of `std::io`'s traits which we use in `rust-bitcoin` so that we can
+to export a minmal version of `std::io`'s traits which we use in `rust-bitcoin` so that we can
 support `no-std` applications.
 
 These traits are not one-for-one drop-ins, but are as close as possible while still implementing
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/logo/README.md b/logo/README.md
index e68633c7..71a563db 100644
--- a/logo/README.md
+++ b/logo/README.md
@@ -23,7 +23,7 @@ Rust Logo is licensed under CC-BY, which allows reuse and modifications for any
 
 ## Acknowledgements
 
-Acknowledgement for the runners up in this PR: <https://github.com/rust-bitcoin/rust-bitcoin/pull/891#issuecomment-1074476858>
+Acknowledgement for the runners up in this PR: https://github.com/rust-bitcoin/rust-bitcoin/pull/891#issuecomment-1074476858
 
 In particular, the Rust Bitcoin Wizard gear was an incredibly inspired piece of art. Also, the meshed gears design was beloved by some but not all.
 
diff --git a/nightly-version b/nightly-version
index e70649f1..31657a66 100644
--- a/nightly-version
+++ b/nightly-version
@@ -1,1 +1,1 @@
-nightly-2024-05-04
+nightly-2024-04-21
diff --git a/units/README.md b/units/README.md
index 177b82ff..4b419c4f 100644
--- a/units/README.md
+++ b/units/README.md
@@ -1,11 +1,14 @@
-# Bitcoin Units
+Bitcoin Units
+=============
 
 This crate provides basic Bitcoin numeric units such as `Amount`.
 
+
 ## Minimum Supported Rust Version (MSRV)
 
 This library should always compile with any combination of features on **Rust 1.56.1**.
 
+
 ## Licensing
 
 The code in this project is licensed under the [Creative Commons CC0 1.0 Universal license](LICENSE).


Test patch:
diff --git a/base58/contrib/test_vars.sh b/base58/contrib/test_vars.sh
index 0c25edc7..7701af20 100644
--- a/base58/contrib/test_vars.sh
+++ b/base58/contrib/test_vars.sh
@@ -6,5 +6,5 @@ FEATURES_WITH_STD=""
 # Test all these features without "std" enabled.
 FEATURES_WITHOUT_STD=""
 
-# Run these examples.
+# Run and lint these examples.
 EXAMPLES=""
diff --git a/bitcoin/contrib/test_vars.sh b/bitcoin/contrib/test_vars.sh
index e1d02314..26ef2480 100644
--- a/bitcoin/contrib/test_vars.sh
+++ b/bitcoin/contrib/test_vars.sh
@@ -6,5 +6,5 @@ FEATURES_WITH_STD="rand-std serde secp-recovery bitcoinconsensus-std base64 orde
 # Test all these features without "std" or "alloc" enabled.
 FEATURES_WITHOUT_STD="rand serde secp-recovery bitcoinconsensus base64 ordered"
 
-# Run these examples.
+# Run and lint these examples.
 EXAMPLES="ecdsa-psbt:std,bitcoinconsensus sign-tx-segwit-v0:rand-std sign-tx-taproot:rand-std taproot-psbt:bitcoinconsensus,rand-std sighash:std"
diff --git a/bitcoin/tests/data/README.md b/bitcoin/tests/data/README.md
index 0792fd14..70ed7a4e 100644
--- a/bitcoin/tests/data/README.md
+++ b/bitcoin/tests/data/README.md
@@ -1,3 +1,4 @@
-# Test vector data
+Test vector data
+================
 
 This file contains data (hex strings) taken from BIP test vectors.
diff --git a/bitcoin/tests/data/serde/README.md b/bitcoin/tests/data/serde/README.md
index 4d4a905a..a12793ba 100644
--- a/bitcoin/tests/data/serde/README.md
+++ b/bitcoin/tests/data/serde/README.md
@@ -1,11 +1,12 @@
-# Serialization input/output
+Serialization input/output
+==========================
 
 Files here contain hex strings and binary data representing types used for
 regression testing.
 
-- `*_hex`: consensus encoded types represented as hex strings
-- `*_ser`: consensus encoded types represented as binary data
-- `*_bincode`: types serialized with `serde` as bincode
+- *_hex: consensus encoded types represented as hex strings
+- *_ser: consensus encoded types represented as binary data
+- *_bincode: types serialized with serde as bincode
 
-We consensus deserialize, `serde` serialize, then check against the expected data
-to verify no `serde` regressions have been introduced.
+We consensus deserialize, serde serialize, then check against the expected data
+to verify no serde regressions have been introduced.
diff --git a/hashes/contrib/test_vars.sh b/hashes/contrib/test_vars.sh
index 5e89c21e..a75854cd 100644
--- a/hashes/contrib/test_vars.sh
+++ b/hashes/contrib/test_vars.sh
@@ -9,5 +9,5 @@ FEATURES_WITHOUT_STD="alloc serde small-hash"
 # Run address sanitizer with these features.
 ASAN_FEATURES="std io serde"
 
-# Run these examples.
+# Run and lint these examples.
 EXAMPLES=""
diff --git a/hashes/extended_tests/schemars/README.md b/hashes/extended_tests/schemars/README.md
index 51dc97c4..9e0a5970 100644
--- a/hashes/extended_tests/schemars/README.md
+++ b/hashes/extended_tests/schemars/README.md
@@ -6,8 +6,6 @@ Run as usual with `cargo test`.
 
 To run the tests with the MSRV you will need to pin `serde`:
 
-```bash
-cargo update -p serde --precise 1.0.156
-cargo update -p regex --precise 1.7.3
-cargo update -p chrono --precise 0.4.24
-```
+- `cargo update -p serde --precise 1.0.156`
+- `cargo update -p regex --precise 1.7.3`
+- `cargo update -p chrono --precise 0.4.24`
diff --git a/internals/contrib/test_vars.sh b/internals/contrib/test_vars.sh
index 759f5afb..22b5b7e3 100644
--- a/internals/contrib/test_vars.sh
+++ b/internals/contrib/test_vars.sh
@@ -6,5 +6,5 @@ FEATURES_WITH_STD="serde"
 # Test all these features without "std" enabled.
 FEATURES_WITHOUT_STD="alloc serde"
 
-# Run these examples.
+# Run and lint these examples.
 EXAMPLES=""
diff --git a/io/contrib/test_vars.sh b/io/contrib/test_vars.sh
index 0317fc3b..9eac7dcc 100644
--- a/io/contrib/test_vars.sh
+++ b/io/contrib/test_vars.sh
@@ -6,5 +6,5 @@ FEATURES_WITH_STD=""
 # Test all these features without "std" enabled.
 FEATURES_WITHOUT_STD="alloc"
 
-# Run these examples.
+# Run and lint these examples.
 EXAMPLES=""
diff --git a/units/contrib/test_vars.sh b/units/contrib/test_vars.sh
index 759f5afb..22b5b7e3 100644
--- a/units/contrib/test_vars.sh
+++ b/units/contrib/test_vars.sh
@@ -6,5 +6,5 @@ FEATURES_WITH_STD="serde"
 # Test all these features without "std" enabled.
 FEATURES_WITHOUT_STD="alloc serde"
 
-# Run these examples.
+# Run and lint these examples.
 EXAMPLES=""