Repository: rust-bitcoin/rust-bitcoin
Pull request #2206 (2023-11-20 23:08:54)

Add from_vb_const function
This function is can be used to construct a Weight type from_vb in const context.  Note I don't think it's possible to test the panic case since it's a compile time error work around currently to panic.


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2e0da229..813c57e7 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -23,7 +23,6 @@ changes to this document in a pull request.
   * [Naming conventions](#naming-conventions)
   * [Upgrading dependencies](#upgrading-dependencies)
   * [Unsafe code](#unsafe-code)
-  * [Policy](#policy)
 - [Security](#security)
 - [Testing](#testing)
 - [Going further](#going-further)
@@ -223,138 +222,6 @@ library maintainers on the exclusion from this rule. In such cases there is a
 requirement to test unsafe code with sanitizers including Miri.
 
 
-### Policy
-
-We have various `rust-bitcoin` specific coding styles and conventions that are
-grouped here loosely under the term 'policy'. These are things we try to adhere
-to but that you should not need to worry too much about if you are a new
-contributor. Think of this as a place to collect group knowledge that exists in
-the various PRs over the last few years.
-
-#### Import statements
-
-We use the following style for import statements, see
-(https://github.com/rust-bitcoin/rust-bitcoin/discussions/2088) for the discussion that led to this.
-
-```rust
-
-// Modules first, as they are part of the project's structure.
-pub mod aa_this;
-mod bb_private;
-pub mod cc_that;
-
-// Private imports, rustfmt will sort and merge them correctly.
-use crate::aa_this::{This, That};
-use crate::bb_that;
-
-// Public re-exports.
-#[rustfmt::skip] // Keeps public re-exports separate, because of this we have to sort manually.
-pub use {
-    crate::aa_aa_this,
-    crate::bb_bb::That,
-}
-```
-
-#### Errors
-
-Return as much context as possible with errors e.g., if an error was encountered parsing a string
-include the string in the returned error type. If a function consumes costly-to-compute input
-(allocations are also considered costly) it should return the input back in the error type.
-
-More specifically an error should
-
-- be `non_exhaustive` unless we _really_ never want to change it.
-- have private fields unless we are very confident they won't change.
-- derive `Debug, Clone, PartialEq, Eq` (and `Copy` iff not `non_exhaustive`).
-- implement Display using `write_err!()` macro if a variant contains an inner error source.
-- have `Error` suffix
-- implement `std::error::Error` if they are public (feature gated on "std").
-
-```rust
-/// Documentation for the `Error` type.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]  // Add liberally; if the error type may ever have new variants added.
-pub enum Error {
-    /// Documentation for variant A.
-    A,
-    /// Documentation for variant B.
-    B,
-}
-```
-
-
-#### Rustdocs
-
-Be liberal with references to BIPs or other documentation; the aim is that devs can learn about
-Bitcoin by hacking on this codebase as opposed to having to learn about Bitcoin first and then start
-hacking on this codebase. Consider the following format, not all sections will be required for all types.
-
-
-```rust
-/// The Bitcoin foobar.
-///
-/// Contains all the data used when passing a foobar around the Bitcoin network.
-///
-/// <details>
-/// <summary>FooBar Original Design</summary>
-///
-/// The foobar was introduced in Bitcoin x.y.z to increase the amount of foo in bar.
-///
-/// </details>
-///
-/// ### Relevant BIPs
-///
-/// * [BIP X - FooBar in Bitcoin](https://github.com/bitcoin/bips/blob/master/bip-0000.mediawiki)
-pub struct FooBar {
-    /// The version in use.
-    pub version: Version
-}
-```
-
-Do use rustdoc subheadings. Do put an empty newline below each heading e.g.,
-
-```rust
-impl FooBar {
-    /// Constructs a `FooBar` from a [`Baz`].
-    ///
-    /// # Errors
-    ///
-    /// Returns an error if `Baz` is not ...
-    ///
-    /// # Panics
-    ///
-    /// If the `Baz`, converted to a `usize`, is out of bounds.
-    pub fn from_baz(baz: Baz) -> Result<Self, Error> {
-        ...
-    }
-}
-```
-
-Add Panics section if any input to the function can trigger a panic.
-
-Generally we prefer to have non-panicking APIs but it is impractical in some cases. If you're not
-sure, feel free to ask. If we determine panicking is more practical it must be documented. Internal
-panics that could theoretically occur because of bugs in our code must not be documented.
-
-
-#### Attributes
-
-- `#[track_caller]`: Used on functions that panic on invalid arguments
-  (see https://rustc-dev-guide.rust-lang.org/backend/implicit-caller-location.html)
-
-- `#[cfg(rust_v_1_60)]`: Used to guard code that should only be built in if the toolchain is
-  compatible. These configuration conditionals are set at build time in `bitcoin/build.rs`. New
-  version attributes may be added as needed.
-
-
-#### Licensing
-
-We use SPDX license tags, all files should start with
-
-```
-// SPDX-License-Identifier: CC0-1.0
-```
-
 ## Security
 
 Security is the primary focus for this library; disclosure of security
@@ -374,7 +241,7 @@ easy and good test coverage of the codebase is an important goal. Refactoring
 the project to enable fine-grained unit testing is also an ongoing effort.
 
 Various methods of testing are in use (e.g. fuzzing, mutation), please see
-the [readme](./README.md) for more information.
+the [readme](./REAME.md) for more information.
 
 
 ## Going further
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index 52268334..9cc71555 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -32,15 +32,15 @@ dependencies = [
 
 [[package]]
 name = "bitcoin"
-version = "0.31.0"
+version = "0.31.0-rc2"
 dependencies = [
  "base64",
  "bech32",
  "bincode",
  "bitcoin-internals",
- "bitcoin-io",
  "bitcoin_hashes",
  "bitcoinconsensus",
+ "core2",
  "hex-conservative",
  "hex_lit",
  "mutagen",
@@ -69,15 +69,11 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "bitcoin-io"
-version = "0.1.0"
-
 [[package]]
 name = "bitcoin_hashes"
 version = "0.13.0"
 dependencies = [
- "bitcoin-io",
+ "core2",
  "hex-conservative",
  "schemars",
  "serde",
@@ -113,6 +109,15 @@ version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
 
+[[package]]
+name = "core2"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2cf12d2dad3ed124aa116f59561428478993d69ab81ae4d30e5349c9c5b5a5f6"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.0"
@@ -141,6 +146,9 @@ name = "hex-conservative"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2"
+dependencies = [
+ "core2",
+]
 
 [[package]]
 name = "hex_lit"
@@ -183,6 +191,12 @@ version = "0.2.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "74dfca3d9957906e8d1e6a0b641dc9a59848e793f1da2165889fd4f62d10d79c"
 
+[[package]]
+name = "memchr"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6"
+
 [[package]]
 name = "memmap2"
 version = "0.5.10"
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 27677e11..9a197ba7 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -31,15 +31,15 @@ dependencies = [
 
 [[package]]
 name = "bitcoin"
-version = "0.31.0"
+version = "0.31.0-rc2"
 dependencies = [
  "base64",
  "bech32",
  "bincode",
  "bitcoin-internals",
- "bitcoin-io",
  "bitcoin_hashes",
  "bitcoinconsensus",
+ "core2",
  "hex-conservative",
  "hex_lit",
  "mutagen",
@@ -68,15 +68,11 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "bitcoin-io"
-version = "0.1.0"
-
 [[package]]
 name = "bitcoin_hashes"
 version = "0.13.0"
 dependencies = [
- "bitcoin-io",
+ "core2",
  "hex-conservative",
  "schemars",
  "serde",
@@ -112,6 +108,15 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "core2"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "dyn-clone"
 version = "1.0.11"
@@ -140,6 +145,9 @@ name = "hex-conservative"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2"
+dependencies = [
+ "core2",
+]
 
 [[package]]
 name = "hex_lit"
@@ -182,6 +190,12 @@ version = "0.2.142"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
 
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
 [[package]]
 name = "memmap2"
 version = "0.5.10"
diff --git a/Cargo.toml b/Cargo.toml
index 42895c92..643dc29c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [workspace]
-members = ["bitcoin", "hashes", "internals", "fuzz", "io"]
+members = ["bitcoin", "hashes", "internals", "fuzz"]
 
 [patch.crates-io.bitcoin]
 path = "bitcoin"
@@ -9,6 +9,3 @@ path = "hashes"
 
 [patch.crates-io.bitcoin-internals]
 path = "internals"
-
-[patch.crates-io.bitcoin-io]
-path = "io"
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 0b9933cc..c10e4f33 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "bitcoin"
-version = "0.31.0"
+version = "0.31.0-rc2"
 authors = ["Andrew Poelstra <apoelstra@wpsoftware.net>"]
 license = "CC0-1.0"
 repository = "https://github.com/rust-bitcoin/rust-bitcoin/"
@@ -26,8 +26,8 @@ bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
 # The no-std feature doesn't disable std - you need to turn off the std feature for that by disabling default.
 # Instead no-std enables additional features required for this crate to be usable without std.
 # As a result, both can be enabled without conflict.
-std = ["secp256k1/std", "bitcoin-io/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
-no-std = ["hashes/alloc", "hashes/io", "bitcoin-io/alloc", "bech32/alloc", "secp256k1/alloc", "hex/alloc"]
+std = ["secp256k1/std", "hashes/std", "bech32/std", "internals/std", "hex/std"]
+no-std = ["core2", "hashes/alloc", "hashes/core2", "bech32/alloc", "secp256k1/alloc", "hex/alloc", "hex/core2"]
 
 [package.metadata.docs.rs]
 all-features = true
@@ -41,12 +41,12 @@ hashes = { package = "bitcoin_hashes", version = "0.13.0", default-features = fa
 secp256k1 = { version = "0.28.0", default-features = false, features = ["hashes"] }
 hex_lit = "0.1.1"
 
-bitcoin-io = { version = "0.1", default-features = false }
-
 base64 = { version = "0.21.3", optional = true }
 # Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
 bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }
 
+# There is no reason to use this dependency directly, it is activated by the "no-std" feature.
+core2 = { version = "0.3.2", default-features = false, features = ["alloc"], optional = true }
 # Do NOT use this as a feature! Use the `serde` feature instead.
 actual-serde = { package = "serde", version = "1.0.103", default-features = false, features = [ "derive", "alloc" ], optional = true }
 
diff --git a/bitcoin/build.rs b/bitcoin/build.rs
index b5e2f5f6..4621b7f1 100644
--- a/bitcoin/build.rs
+++ b/bitcoin/build.rs
@@ -1,13 +1,10 @@
-const MSRV_MINOR: u64 = 48;
-
 fn main() {
-    let rustc = std::env::var_os("RUSTC");
-    let rustc = rustc.as_ref().map(std::path::Path::new).unwrap_or_else(|| "rustc".as_ref());
+    let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into());
     let output = std::process::Command::new(rustc)
         .arg("--version")
         .output()
-        .unwrap_or_else(|error| panic!("Failed to run `{:?} --version`: {:?}", rustc, error));
-    assert!(output.status.success(), "{:?} -- version returned non-zero exit code", rustc);
+        .expect("Failed to run rustc --version");
+    assert!(output.status.success(), "Failed to get rust version");
     let stdout = String::from_utf8(output.stdout).expect("rustc produced non-UTF-8 output");
     let version_prefix = "rustc ";
     if !stdout.starts_with(version_prefix) {
@@ -19,15 +16,16 @@ fn main() {
     let version = &version[..end];
     let mut version_components = version.split('.');
     let major = version_components.next().unwrap();
-    assert_eq!(major, "1", "Unexpected Rust major version");
+    assert_eq!(major, "1", "Unexpected Rust version");
     let minor = version_components
         .next()
         .unwrap_or("0")
         .parse::<u64>()
         .expect("invalid Rust minor version");
 
-    // print cfg for all interesting versions less than or equal to minor
-    for version in MSRV_MINOR..=minor {
-        println!("cargo:rustc-cfg=rust_v_1_{}", version);
+    for activate_version in &[53, 60] {
+        if minor >= *activate_version {
+            println!("cargo:rustc-cfg=rust_v_1_{}", activate_version);
+        }
     }
 }
diff --git a/bitcoin/embedded/Cargo.toml b/bitcoin/embedded/Cargo.toml
index 9ea41c17..f68d7f3f 100644
--- a/bitcoin/embedded/Cargo.toml
+++ b/bitcoin/embedded/Cargo.toml
@@ -17,6 +17,7 @@ panic-halt = "0.2.0"
 alloc-cortex-m = "0.4.1"
 bitcoin = { path="../", default-features = false, features = ["no-std", "secp-lowmemory"] }
 
+
 [[bin]]
 name = "embedded"
 test = false
@@ -32,6 +33,3 @@ path = "../../hashes"
 
 [patch.crates-io.bitcoin-internals]
 path = "../../internals"
-
-[patch.crates-io.bitcoin-io]
-path = "../../io"
diff --git a/bitcoin/src/address/error.rs b/bitcoin/src/address/error.rs
index 7e01868e..964edb21 100644
--- a/bitcoin/src/address/error.rs
+++ b/bitcoin/src/address/error.rs
@@ -1,5 +1,3 @@
-//! Error code for the address module.
-
 use core::fmt;
 
 use internals::write_err;
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index 07115408..1152338e 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -26,8 +26,6 @@
 //! bitcoin = { version = "...", features = ["rand-std"] }
 //! ```
 
-pub mod error;
-
 use core::convert::{TryFrom, TryInto};
 use core::fmt;
 use core::marker::PhantomData;
@@ -51,11 +49,9 @@
 use crate::script::PushBytesBuf;
 use crate::taproot::TapNodeHash;
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
-    error::{Error, ParseError, UnknownAddressTypeError},
-};
+/// Error code for the address module.
+pub mod error;
+pub use self::error::{Error, ParseError, UnknownAddressTypeError};
 
 /// The different types of addresses.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -101,7 +97,8 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
 
 /// The method used to produce an address.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-enum Payload {
+#[non_exhaustive]
+pub enum Payload {
     /// P2PKH address.
     PubkeyHash(PubkeyHash),
     /// P2SH address.
@@ -110,17 +107,139 @@ enum Payload {
     WitnessProgram(WitnessProgram),
 }
 
+impl Payload {
+    /// Constructs a [Payload] from an output script (`scriptPubkey`).
+    pub fn from_script(script: &Script) -> Result<Payload, Error> {
+        Ok(if script.is_p2pkh() {
+            let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long");
+            Payload::PubkeyHash(PubkeyHash::from_byte_array(bytes))
+        } else if script.is_p2sh() {
+            let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long");
+            Payload::ScriptHash(ScriptHash::from_byte_array(bytes))
+        } else if script.is_witness_program() {
+            let opcode = script.first_opcode().expect("witness_version guarantees len() > 4");
+
+            let witness_program = script.as_bytes()[2..].to_vec();
+
+            let witness_program =
+                WitnessProgram::new(WitnessVersion::try_from(opcode)?, witness_program)?;
+            Payload::WitnessProgram(witness_program)
+        } else {
+            return Err(Error::UnrecognizedScript);
+        })
+    }
+
+    /// Generates a script pubkey spending to this [Payload].
+    pub fn script_pubkey(&self) -> ScriptBuf {
+        match *self {
+            Payload::PubkeyHash(ref hash) => ScriptBuf::new_p2pkh(hash),
+            Payload::ScriptHash(ref hash) => ScriptBuf::new_p2sh(hash),
+            Payload::WitnessProgram(ref prog) => ScriptBuf::new_witness_program(prog),
+        }
+    }
+
+    /// Returns true if the address creates a particular script
+    /// This function doesn't make any allocations.
+    pub fn matches_script_pubkey(&self, script: &Script) -> bool {
+        match *self {
+            Payload::PubkeyHash(ref hash) if script.is_p2pkh() =>
+                &script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
+            Payload::ScriptHash(ref hash) if script.is_p2sh() =>
+                &script.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash),
+            Payload::WitnessProgram(ref prog) if script.is_witness_program() =>
+                &script.as_bytes()[2..] == prog.program().as_bytes(),
+            Payload::PubkeyHash(_) | Payload::ScriptHash(_) | Payload::WitnessProgram(_) => false,
+        }
+    }
+
+    /// Creates a pay to (compressed) public key hash payload from a public key
+    #[inline]
+    pub fn p2pkh(pk: &PublicKey) -> Payload { Payload::PubkeyHash(pk.pubkey_hash()) }
+
+    /// Creates a pay to script hash P2SH payload from a script
+    #[inline]
+    pub fn p2sh(script: &Script) -> Result<Payload, Error> {
+        if script.len() > MAX_SCRIPT_ELEMENT_SIZE {
+            return Err(Error::ExcessiveScriptSize);
+        }
+        Ok(Payload::ScriptHash(script.script_hash()))
+    }
+
+    /// Create a witness pay to public key payload from a public key
+    pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
+        let prog = WitnessProgram::new(
+            WitnessVersion::V0,
+            pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?,
+        )?;
+        Ok(Payload::WitnessProgram(prog))
+    }
+
+    /// Create a pay to script payload that embeds a witness pay to public key
+    pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
+        let builder = script::Builder::new()
+            .push_int(0)
+            .push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
+
+        Ok(Payload::ScriptHash(builder.into_script().script_hash()))
+    }
+
+    /// Create a witness pay to script hash payload.
+    pub fn p2wsh(script: &Script) -> Payload {
+        let prog = WitnessProgram::new(WitnessVersion::V0, script.wscript_hash())
+            .expect("wscript_hash has len 32 compatible with segwitv0");
+        Payload::WitnessProgram(prog)
+    }
+
+    /// Create a pay to script payload that embeds a witness pay to script hash address
+    pub fn p2shwsh(script: &Script) -> Payload {
+        let ws = script::Builder::new().push_int(0).push_slice(script.wscript_hash()).into_script();
+
+        Payload::ScriptHash(ws.script_hash())
+    }
+
+    /// Create a pay to taproot payload from untweaked key
+    pub fn p2tr<C: Verification>(
+        secp: &Secp256k1<C>,
+        internal_key: UntweakedPublicKey,
+        merkle_root: Option<TapNodeHash>,
+    ) -> Payload {
+        let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
+        let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize())
+            .expect("taproot output key has len 32 <= 40");
+        Payload::WitnessProgram(prog)
+    }
+
+    /// Create a pay to taproot payload from a pre-tweaked output key.
+    ///
+    /// This method is not recommended for use and [Payload::p2tr()] should be used where possible.
+    pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Payload {
+        let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize())
+            .expect("taproot output key has len 32 <= 40");
+        Payload::WitnessProgram(prog)
+    }
+
+    /// Returns a byte slice of the inner program of the payload. If the payload
+    /// is a script hash or pubkey hash, a reference to the hash is returned.
+    fn inner_prog_as_bytes(&self) -> &[u8] {
+        match self {
+            Payload::ScriptHash(hash) => hash.as_ref(),
+            Payload::PubkeyHash(hash) => hash.as_ref(),
+            Payload::WitnessProgram(prog) => prog.program().as_bytes(),
+        }
+    }
+}
+
 /// A utility struct to encode an address payload with the given parameters.
 /// This is a low-level utility struct. Consider using `Address` instead.
-struct AddressEncoding<'a> {
+pub struct AddressEncoding<'a> {
     /// The address payload to encode.
-    payload: &'a Payload,
+    pub payload: &'a Payload,
     /// base58 version byte for p2pkh payloads (e.g. 0x00 for "1..." addresses).
-    p2pkh_prefix: u8,
+    pub p2pkh_prefix: u8,
     /// base58 version byte for p2sh payloads (e.g. 0x05 for "3..." addresses).
-    p2sh_prefix: u8,
+    pub p2sh_prefix: u8,
     /// The bech32 human-readable part.
-    hrp: Hrp,
+    pub hrp: Hrp,
 }
 
 /// Formats bech32 as upper case if alternate formatting is chosen (`{:#}`).
@@ -318,7 +437,7 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 /// `Address<NetworkUnchecked>`.
 impl<V: NetworkValidation> Address<V> {
     /// Returns a reference to the payload of this address.
-    fn payload(&self) -> &Payload { &self.0.payload }
+    pub fn payload(&self) -> &Payload { &self.0.payload }
 
     /// Returns a reference to the network of this address.
     pub fn network(&self) -> &Network { &self.0.network }
@@ -330,7 +449,7 @@ pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
     }
 
     /// Extracts and returns the network and payload components of the `Address`.
-    fn into_parts(self) -> (Network, Payload) {
+    pub fn into_parts(self) -> (Network, Payload) {
         let AddressInner { payload, network } = self.0;
         (network, payload)
     }
@@ -388,7 +507,8 @@ fn fmt_internal(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
 
     /// Create new address from given components, infering the network validation
     /// marker type of the address.
-    fn new(network: Network, payload: Payload) -> Self {
+    #[inline]
+    pub fn new(network: Network, payload: Payload) -> Self {
         Self(AddressInner { network, payload }, PhantomData)
     }
 }
@@ -400,8 +520,7 @@ impl Address {
     /// This is the preferred non-witness type address.
     #[inline]
     pub fn p2pkh(pk: &PublicKey, network: Network) -> Address {
-        let payload = Payload::PubkeyHash(pk.pubkey_hash());
-        Address(AddressInner { network, payload }, PhantomData)
+        Address::new(network, Payload::p2pkh(pk))
     }
 
     /// Creates a pay to script hash P2SH address from a script.
@@ -410,11 +529,7 @@ pub fn p2pkh(pk: &PublicKey, network: Network) -> Address {
     /// these days.
     #[inline]
     pub fn p2sh(script: &Script, network: Network) -> Result<Address, Error> {
-        if script.len() > MAX_SCRIPT_ELEMENT_SIZE {
-            return Err(Error::ExcessiveScriptSize);
-        }
-        let payload = Payload::ScriptHash(script.script_hash());
-        Ok(Address(AddressInner { network, payload }, PhantomData))
+        Ok(Address::new(network, Payload::p2sh(script)?))
     }
 
     /// Creates a witness pay to public key address from a public key.
@@ -424,12 +539,7 @@ pub fn p2sh(script: &Script, network: Network) -> Result<Address, Error> {
     /// # Errors
     /// Will only return an error if an uncompressed public key is provided.
     pub fn p2wpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
-        let prog = WitnessProgram::new(
-            WitnessVersion::V0,
-            pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?,
-        )?;
-        let payload = Payload::WitnessProgram(prog);
-        Ok(Address(AddressInner { network, payload }, PhantomData))
+        Ok(Address::new(network, Payload::p2wpkh(pk)?))
     }
 
     /// Creates a pay to script address that embeds a witness pay to public key.
@@ -439,29 +549,19 @@ pub fn p2wpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
     /// # Errors
     /// Will only return an Error if an uncompressed public key is provided.
     pub fn p2shwpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
-        let builder = script::Builder::new()
-            .push_int(0)
-            .push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
-
-        let payload = Payload::ScriptHash(builder.into_script().script_hash());
-        Ok(Address(AddressInner { network, payload }, PhantomData))
+        Ok(Address::new(network, Payload::p2shwpkh(pk)?))
     }
 
     /// Creates a witness pay to script hash address.
     pub fn p2wsh(script: &Script, network: Network) -> Address {
-        let prog = WitnessProgram::new(WitnessVersion::V0, script.wscript_hash())
-            .expect("wscript_hash has len 32 compatible with segwitv0");
-        let payload = Payload::WitnessProgram(prog);
-        Address(AddressInner { network, payload }, PhantomData)
+        Address::new(network, Payload::p2wsh(script))
     }
 
     /// Creates a pay to script address that embeds a witness pay to script hash address.
     ///
     /// This is a segwit address type that looks familiar (as p2sh) to legacy clients.
     pub fn p2shwsh(script: &Script, network: Network) -> Address {
-        let ws = script::Builder::new().push_int(0).push_slice(script.wscript_hash()).into_script();
-        let payload = Payload::ScriptHash(ws.script_hash());
-        Address(AddressInner { network, payload }, PhantomData)
+        Address::new(network, Payload::p2shwsh(script))
     }
 
     /// Creates a pay to taproot address from an untweaked key.
@@ -471,30 +571,14 @@ pub fn p2tr<C: Verification>(
         merkle_root: Option<TapNodeHash>,
         network: Network,
     ) -> Address {
-        let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
-        let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize())
-            .expect("taproot output key has len 32 <= 40");
-        let payload = Payload::WitnessProgram(prog);
-        Address(AddressInner { network, payload }, PhantomData)
+        Address::new(network, Payload::p2tr(secp, internal_key, merkle_root))
     }
 
     /// Creates a pay to taproot address from a pre-tweaked output key.
     ///
     /// This method is not recommended for use, [`Address::p2tr()`] should be used where possible.
     pub fn p2tr_tweaked(output_key: TweakedPublicKey, network: Network) -> Address {
-        let prog = WitnessProgram::new(WitnessVersion::V1, output_key.to_inner().serialize())
-            .expect("taproot output key has len 32 <= 40");
-        let payload = Payload::WitnessProgram(prog);
-        Address(AddressInner { network, payload }, PhantomData)
-    }
-
-    /// Creates an address from an arbitrary witness program.
-    ///
-    /// This only exists to support future witness versions. If you are doing normal mainnet things
-    /// then you likely do not need this constructor.
-    pub fn from_witness_program(program: WitnessProgram, network: Network) -> Address {
-        let inner = AddressInner { payload: Payload::WitnessProgram(program), network };
-        Address(inner, PhantomData)
+        Address::new(network, Payload::p2tr_tweaked(output_key))
     }
 
     /// Gets the address type of the address.
@@ -521,34 +605,11 @@ pub fn is_spend_standard(&self) -> bool { self.address_type().is_some() }
 
     /// Constructs an [`Address`] from an output script (`scriptPubkey`).
     pub fn from_script(script: &Script, network: Network) -> Result<Address, Error> {
-        let payload = if script.is_p2pkh() {
-            let bytes = script.as_bytes()[3..23].try_into().expect("statically 20B long");
-            Payload::PubkeyHash(PubkeyHash::from_byte_array(bytes))
-        } else if script.is_p2sh() {
-            let bytes = script.as_bytes()[2..22].try_into().expect("statically 20B long");
-            Payload::ScriptHash(ScriptHash::from_byte_array(bytes))
-        } else if script.is_witness_program() {
-            let opcode = script.first_opcode().expect("witness_version guarantees len() > 4");
-
-            let witness_program = script.as_bytes()[2..].to_vec();
-
-            let witness_program =
-                WitnessProgram::new(WitnessVersion::try_from(opcode)?, witness_program)?;
-            Payload::WitnessProgram(witness_program)
-        } else {
-            return Err(Error::UnrecognizedScript);
-        };
-        Ok(Address(AddressInner { network, payload }, PhantomData))
+        Ok(Address::new(network, Payload::from_script(script)?))
     }
 
     /// Generates a script pubkey spending to this address.
-    pub fn script_pubkey(&self) -> ScriptBuf {
-        match self.payload() {
-            Payload::PubkeyHash(ref hash) => ScriptBuf::new_p2pkh(hash),
-            Payload::ScriptHash(ref hash) => ScriptBuf::new_p2sh(hash),
-            Payload::WitnessProgram(ref prog) => ScriptBuf::new_witness_program(prog),
-        }
-    }
+    pub fn script_pubkey(&self) -> ScriptBuf { self.payload().script_pubkey() }
 
     /// Creates a URI string *bitcoin:address* optimized to be encoded in QR codes.
     ///
@@ -586,7 +647,7 @@ pub fn to_qr_uri(&self) -> String { format!("bitcoin:{:#}", self) }
     /// given key. For taproot addresses, the supplied key is assumed to be tweaked
     pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool {
         let pubkey_hash = pubkey.pubkey_hash();
-        let payload = self.inner_prog_as_bytes();
+        let payload = self.payload().inner_prog_as_bytes();
         let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
 
         (*pubkey_hash.as_byte_array() == *payload)
@@ -599,32 +660,14 @@ pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool {
     /// This will only work for Taproot addresses. The Public Key is
     /// assumed to have already been tweaked.
     pub fn is_related_to_xonly_pubkey(&self, xonly_pubkey: &XOnlyPublicKey) -> bool {
-        let payload = self.inner_prog_as_bytes();
+        let payload = self.payload().inner_prog_as_bytes();
         payload == xonly_pubkey.serialize()
     }
 
     /// Returns true if the address creates a particular script
     /// This function doesn't make any allocations.
     pub fn matches_script_pubkey(&self, script_pubkey: &Script) -> bool {
-        match &self.payload() {
-            Payload::PubkeyHash(ref hash) if script_pubkey.is_p2pkh() =>
-                &script_pubkey.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
-            Payload::ScriptHash(ref hash) if script_pubkey.is_p2sh() =>
-                &script_pubkey.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash),
-            Payload::WitnessProgram(ref prog) if script_pubkey.is_witness_program() =>
-                &script_pubkey.as_bytes()[2..] == prog.program().as_bytes(),
-            Payload::PubkeyHash(_) | Payload::ScriptHash(_) | Payload::WitnessProgram(_) => false,
-        }
-    }
-
-    /// Returns a byte slice of the inner program of the payload. If the payload
-    /// is a script hash or pubkey hash, a reference to the hash is returned.
-    fn inner_prog_as_bytes(&self) -> &[u8] {
-        match &self.payload() {
-            Payload::ScriptHash(hash) => hash.as_ref(),
-            Payload::PubkeyHash(hash) => hash.as_ref(),
-            Payload::WitnessProgram(prog) => prog.program().as_bytes(),
-        }
+        self.payload().matches_script_pubkey(script_pubkey)
     }
 }
 
diff --git a/bitcoin/src/amount.rs b/bitcoin/src/amount.rs
index 7ca3e708..dd449b1a 100644
--- a/bitcoin/src/amount.rs
+++ b/bitcoin/src/amount.rs
@@ -519,6 +519,14 @@ pub const fn from_sat(satoshi: u64) -> Amount { Amount(satoshi) }
     /// Gets the number of satoshis in this [`Amount`].
     pub fn to_sat(self) -> u64 { self.0 }
 
+    /// The maximum value of an [Amount].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Amount { Amount(u64::max_value()) }
+
+    /// The minimum value of an [Amount].
+    #[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
+    pub const fn min_value() -> Amount { Amount(u64::min_value()) }
+
     /// Convert from a value expressing bitcoins to an [Amount].
     pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
         Amount::from_float_in(btc, Denomination::Bitcoin)
@@ -888,6 +896,14 @@ pub const fn from_sat(satoshi: i64) -> SignedAmount { SignedAmount(satoshi) }
     /// Gets the number of satoshis in this [`SignedAmount`].
     pub fn to_sat(self) -> i64 { self.0 }
 
+    /// The maximum value of an [SignedAmount].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> SignedAmount { SignedAmount(i64::max_value()) }
+
+    /// The minimum value of an [SignedAmount].
+    #[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
+    pub const fn min_value() -> SignedAmount { SignedAmount(i64::min_value()) }
+
     /// Convert from a value expressing bitcoins to an [SignedAmount].
     pub fn from_btc(btc: f64) -> Result<SignedAmount, ParseAmountError> {
         SignedAmount::from_float_in(btc, Denomination::Bitcoin)
diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs
index 49347977..2028070d 100644
--- a/bitcoin/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -271,7 +271,9 @@ pub fn match_any<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
         I::Item: Borrow<[u8]>,
         R: io::Read + ?Sized,
     {
-        let n_elements: VarInt = Decodable::consensus_decode(reader).unwrap_or(VarInt(0));
+        let mut decoder = reader;
+        let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0));
+        let reader = &mut decoder;
         // map hashes to [0, n_elements << grp]
         let nm = n_elements.0 * self.m;
         let mut mapped =
@@ -314,7 +316,9 @@ pub fn match_all<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
         I::Item: Borrow<[u8]>,
         R: io::Read + ?Sized,
     {
-        let n_elements: VarInt = Decodable::consensus_decode(reader).unwrap_or(VarInt(0));
+        let mut decoder = reader;
+        let n_elements: VarInt = Decodable::consensus_decode(&mut decoder).unwrap_or(VarInt(0));
+        let reader = &mut decoder;
         // map hashes to [0, n_elements << grp]
         let nm = n_elements.0 * self.m;
         let mut mapped =
@@ -389,7 +393,7 @@ pub fn finish(&mut self) -> Result<usize, io::Error> {
         mapped.sort_unstable();
 
         // write number of elements as varint
-        let mut wrote = VarInt::from(mapped.len()).consensus_encode(self.writer)?;
+        let mut wrote = VarInt::from(mapped.len()).consensus_encode(&mut self.writer)?;
 
         // write out deltas of sorted values into a Golonb-Rice coded bit stream
         let mut writer = BitStreamWriter::new(self.writer);
@@ -438,7 +442,7 @@ fn golomb_rice_encode<W>(
     /// Golomb-Rice decodes a number from a bit stream (parameter 2^k).
     fn golomb_rice_decode<R>(&self, reader: &mut BitStreamReader<R>) -> Result<u64, io::Error>
     where
-        R: io::Read + ?Sized,
+        R: io::Read,
     {
         let mut q = 0u64;
         while reader.read(1)? == 1 {
@@ -455,13 +459,13 @@ fn hash(&self, element: &[u8]) -> u64 {
 }
 
 /// Bitwise stream reader.
-pub struct BitStreamReader<'a, R: ?Sized> {
+pub struct BitStreamReader<'a, R> {
     buffer: [u8; 1],
     offset: u8,
     reader: &'a mut R,
 }
 
-impl<'a, R: io::Read + ?Sized> BitStreamReader<'a, R> {
+impl<'a, R: io::Read> BitStreamReader<'a, R> {
     /// Creates a new [`BitStreamReader`] that reads bitwise from a given `reader`.
     pub fn new(reader: &'a mut R) -> BitStreamReader<'a, R> {
         BitStreamReader { buffer: [0u8], reader, offset: 8 }
diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs
index 059278a5..7a2c4be9 100644
--- a/bitcoin/src/bip32.rs
+++ b/bitcoin/src/bip32.rs
@@ -6,6 +6,8 @@
 //! at <https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki>.
 //!
 
+#![allow(deprecated)] // Remove once we remove XpubIdentifier.
+
 use core::convert::TryInto;
 use core::default::Default;
 use core::ops::Index;
@@ -53,6 +55,10 @@ fn from_hmac(hmac: Hmac<sha512::Hash>) -> Self {
 impl_bytes_newtype!(Fingerprint, 4);
 
 hash_newtype! {
+    /// XpubIdentifier as defined in BIP-32.
+    #[deprecated(since = "0.0.0-NEXT-RELEASE", note = "use XKeyIdentifier instead")]
+    pub struct XpubIdentifier(hash160::Hash);
+
     /// Extended key identifier as defined in BIP-32.
     pub struct XKeyIdentifier(hash160::Hash);
 }
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index 9adde9d8..b08bc5c7 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -16,17 +16,12 @@
 use crate::blockdata::script;
 use crate::blockdata::transaction::Transaction;
 use crate::consensus::{encode, Decodable, Encodable};
+pub use crate::hash_types::BlockHash;
 use crate::hash_types::{TxMerkleNode, WitnessCommitment, WitnessMerkleNode, Wtxid};
 use crate::internal_macros::impl_consensus_encoding;
 use crate::pow::{CompactTarget, Target, Work};
 use crate::prelude::*;
-use crate::{io, merkle_tree, Network, VarInt};
-
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use crate::{
-    hash_types::BlockHash,
-};
+use crate::{io, merkle_tree, VarInt};
 
 /// Bitcoin block header.
 ///
@@ -74,10 +69,7 @@ pub fn block_hash(&self) -> BlockHash {
     pub fn target(&self) -> Target { self.bits.into() }
 
     /// Computes the popular "difficulty" measure for mining.
-    ///
-    /// Difficulty represents how difficult the current target makes it to find a block, relative to
-    /// how difficult it would be at the highest possible target (highest target == lowest difficulty).
-    pub fn difficulty(&self, network: Network) -> u128 { self.target().difficulty(network) }
+    pub fn difficulty(&self) -> u128 { self.target().difficulty() }
 
     /// Computes the popular "difficulty" measure for mining and returns a float value of f64.
     pub fn difficulty_float(&self) -> f64 { self.target().difficulty_float() }
@@ -336,6 +328,10 @@ pub fn total_size(&self) -> usize {
         size
     }
 
+    /// Returns the stripped size of the block.
+    #[deprecated(since = "0.31.0", note = "use Block::base_size() instead")]
+    pub fn strippedsize(&self) -> usize { self.base_size() }
+
     /// Returns the coinbase transaction, if one is present.
     pub fn coinbase(&self) -> Option<&Transaction> { self.txdata.first() }
 
@@ -489,7 +485,6 @@ fn test_coinbase_and_bip34() {
 
     #[test]
     fn block_test() {
-        let network = Network::Bitcoin;
         // Mainnet block 00000000b0c5a240b2a61d2e75692224efd4cbecdf6eaf4cc2cf477ca7c270e7
         let some_block = hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac00000000");
         let cutoff_block = hex!("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0804ffff001d026e04ffffffff0100f2052a0100000043410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac00000000010000000321f75f3139a013f50f315b23b0c9a2b6eac31e2bec98e5891c924664889942260000000049483045022100cb2c6b346a978ab8c61b18b5e9397755cbd17d6eb2fe0083ef32e067fa6c785a02206ce44e613f31d9a6b0517e46f3db1576e9812cc98d159bfdaf759a5014081b5c01ffffffff79cda0945903627c3da1f85fc95d0b8ee3e76ae0cfdc9a65d09744b1f8fc85430000000049483045022047957cdd957cfd0becd642f6b84d82f49b6cb4c51a91f49246908af7c3cfdf4a022100e96b46621f1bffcf5ea5982f88cef651e9354f5791602369bf5a82a6cd61a62501fffffffffe09f5fe3ffbf5ee97a54eb5e5069e9da6b4856ee86fc52938c2f979b0f38e82000000004847304402204165be9a4cbab8049e1af9723b96199bfd3e85f44c6b4c0177e3962686b26073022028f638da23fc003760861ad481ead4099312c60030d4cb57820ce4d33812a5ce01ffffffff01009d966b01000000434104ea1feff861b51fe3f5f8a3b12d0f4712db80e919548a80839fc47c6a21e66d957e9c5d8cd108c7a2d2324bad71f9904ac0ae7336507d785b17a2c115e427a32fac");
@@ -516,7 +511,7 @@ fn block_test() {
             real_decode.header.validate_pow(real_decode.header.target()).unwrap(),
             real_decode.block_hash()
         );
-        assert_eq!(real_decode.header.difficulty(network), 1);
+        assert_eq!(real_decode.header.difficulty(), 1);
         assert_eq!(real_decode.header.difficulty_float(), 1.0);
         // [test] TODO: check the transaction data
 
@@ -536,7 +531,6 @@ fn block_test() {
     // Check testnet block 000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b
     #[test]
     fn segwit_block_test() {
-        let network = Network::Testnet;
         let segwit_block = include_bytes!("../../tests/data/testnet_block_000000000000045e0b1660b6445b5e5c5ab63c9a4f956be7e1e69be04fa4497b.raw").to_vec();
 
         let decode: Result<Block, _> = deserialize(&segwit_block);
@@ -559,7 +553,7 @@ fn segwit_block_test() {
             real_decode.header.validate_pow(real_decode.header.target()).unwrap(),
             real_decode.block_hash()
         );
-        assert_eq!(real_decode.header.difficulty(network), 2456598);
+        assert_eq!(real_decode.header.difficulty(), 2456598);
         assert_eq!(real_decode.header.difficulty_float(), 2456598.4399242126);
         // [test] TODO: check the transaction data
 
@@ -647,7 +641,7 @@ mod benches {
 
     use super::Block;
     use crate::consensus::{deserialize, Decodable, Encodable};
-    use crate::io::sink;
+    use crate::EmptyWrite;
 
     #[bench]
     pub fn bench_stream_reader(bh: &mut Bencher) {
@@ -684,7 +678,7 @@ pub fn bench_block_serialize_logic(bh: &mut Bencher) {
         let block: Block = deserialize(&raw_block[..]).unwrap();
 
         bh.iter(|| {
-            let size = block.consensus_encode(&mut sink());
+            let size = block.consensus_encode(&mut EmptyWrite);
             black_box(&size);
         });
     }
diff --git a/bitcoin/src/blockdata/constants.rs b/bitcoin/src/blockdata/constants.rs
index a74e029a..87bca277 100644
--- a/bitcoin/src/blockdata/constants.rs
+++ b/bitcoin/src/blockdata/constants.rs
@@ -31,6 +31,14 @@
 /// How much time on average should occur between diffchanges.
 pub const DIFFCHANGE_TIMESPAN: u32 = 14 * 24 * 3600;
 
+#[deprecated(since = "0.31.0", note = "Use Weight::MAX_BLOCK instead")]
+/// The maximum allowed weight for a block, see BIP 141 (network rule).
+pub const MAX_BLOCK_WEIGHT: u32 = 4_000_000;
+
+#[deprecated(since = "0.31.0", note = "Use Weight::MIN_TRANSACTION instead")]
+/// The minimum transaction weight for a valid serialized transaction.
+pub const MIN_TRANSACTION_WEIGHT: u32 = 4 * 60;
+
 /// The factor that non-witness serialization data is multiplied by during weight calculation.
 pub const WITNESS_SCALE_FACTOR: usize = 4;
 /// The maximum allowed number of signature check operations in a block.
diff --git a/bitcoin/src/blockdata/fee_rate.rs b/bitcoin/src/blockdata/fee_rate.rs
index aa8d3c44..1c3b8a9e 100644
--- a/bitcoin/src/blockdata/fee_rate.rs
+++ b/bitcoin/src/blockdata/fee_rate.rs
@@ -82,12 +82,10 @@ pub fn checked_div(self, rhs: u64) -> Option<Self> { self.0.checked_div(rhs).map
 
     /// Checked weight multiplication.
     ///
-    /// Computes the absolute fee amount for a given [`Weight`] at this fee rate.
-    ///
-    /// `None` is returned if an overflow occurred.
+    /// Computes `self * rhs` where rhs is of type Weight. `None` is returned if an overflow
+    /// occurred.
     pub fn checked_mul_by_weight(self, rhs: Weight) -> Option<Amount> {
-        let sats = self.0.checked_mul(rhs.to_wu())?.checked_add(999)? / 1000;
-        Some(Amount::from_sat(sats))
+        self.0.checked_mul(rhs.to_wu()).map(Amount::from_sat)
     }
 
     /// Calculates fee by multiplying this fee rate by weight, in weight units, returning `None`
@@ -97,14 +95,13 @@ pub fn checked_mul_by_weight(self, rhs: Weight) -> Option<Amount> {
     ///
     /// # Examples
     ///
-    /// ```
+    /// ```no_run
     /// # use bitcoin::{absolute, transaction, FeeRate, Transaction};
     /// # // Dummy transaction.
     /// # let tx = Transaction { version: transaction::Version::ONE, lock_time: absolute::LockTime::ZERO, input: vec![], output: vec![] };
     ///
     /// let rate = FeeRate::from_sat_per_vb(1).expect("1 sat/vbyte is valid");
-    /// let fee = rate.fee_wu(tx.weight()).unwrap();
-    /// assert_eq!(fee.to_sat(), tx.vsize() as u64);
+    /// let fee = rate.fee_wu(tx.weight());
     /// ```
     pub fn fee_wu(self, weight: Weight) -> Option<Amount> { self.checked_mul_by_weight(weight) }
 
@@ -212,20 +209,12 @@ fn checked_mul_test() {
 
     #[test]
     fn checked_weight_mul_test() {
-        let weight = Weight::from_vb(10).unwrap();
-        let fee: Amount = FeeRate::from_sat_per_vb(10)
-            .unwrap()
-            .checked_mul_by_weight(weight)
-            .expect("expected Amount");
+        let weight = Weight::from_wu(10);
+        let fee: Amount = FeeRate(10).checked_mul_by_weight(weight).expect("expected Amount");
         assert_eq!(Amount::from_sat(100), fee);
 
         let fee = FeeRate(10).checked_mul_by_weight(Weight::MAX);
         assert!(fee.is_none());
-
-        let weight = Weight::from_vb(3).unwrap();
-        let fee_rate = FeeRate::from_sat_per_vb(3).unwrap();
-        let fee = fee_rate.checked_mul_by_weight(weight).unwrap();
-        assert_eq!(Amount::from_sat(9), fee);
     }
 
     #[test]
diff --git a/bitcoin/src/blockdata/locktime/absolute.rs b/bitcoin/src/blockdata/locktime/absolute.rs
index ceb25121..c6a9e112 100644
--- a/bitcoin/src/blockdata/locktime/absolute.rs
+++ b/bitcoin/src/blockdata/locktime/absolute.rs
@@ -407,6 +407,18 @@ impl Height {
     /// The maximum absolute block height.
     pub const MAX: Self = Height(LOCK_TIME_THRESHOLD - 1);
 
+    /// The minimum absolute block height (0), the genesis block.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Height::MIN`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
+    pub const fn min_value() -> Self { Self::MIN }
+
+    /// The maximum absolute block height.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Height::MAX`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Self { Self::MAX }
+
     /// Constructs a new block height.
     ///
     /// # Errors
@@ -477,6 +489,18 @@ impl Time {
     /// The maximum absolute block time (Sun Feb 07 2106 06:28:15 GMT+0000).
     pub const MAX: Self = Time(u32::max_value());
 
+    /// The minimum absolute block time.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Time::MIN`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
+    pub const fn min_value() -> Self { Self::MIN }
+
+    /// The maximum absolute block time.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Time::MAX`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Self { Self::MAX }
+
     /// Constructs a new block time.
     ///
     /// # Errors
diff --git a/bitcoin/src/blockdata/locktime/relative.rs b/bitcoin/src/blockdata/locktime/relative.rs
index d8f3bfa4..198091de 100644
--- a/bitcoin/src/blockdata/locktime/relative.rs
+++ b/bitcoin/src/blockdata/locktime/relative.rs
@@ -206,6 +206,18 @@ impl Height {
     /// The maximum relative block height.
     pub const MAX: Self = Height(u16::max_value());
 
+    /// The minimum relative block height (0), can be included in any block.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Height::MIN`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
+    pub const fn min_value() -> Self { Self::MIN }
+
+    /// The maximum relative block height.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Height::MAX`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Self { Self::MAX }
+
     /// Returns the inner `u16` value.
     #[inline]
     pub fn value(self) -> u16 { self.0 }
@@ -240,6 +252,18 @@ impl Time {
     /// The maximum relative block time (33,554,432 seconds or approx 388 days).
     pub const MAX: Self = Time(u16::max_value());
 
+    /// The minimum relative block time.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Time::MIN`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MIN instead")]
+    pub const fn min_value() -> Self { Self::MIN }
+
+    /// The maximum relative block time.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Time::MAX`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Self { Self::MAX }
+
     /// Create a [`Time`] using time intervals where each interval is equivalent to 512 seconds.
     ///
     /// Encoding finer granularity of time for relative lock-times is not supported in Bitcoin.
diff --git a/bitcoin/src/blockdata/mod.rs b/bitcoin/src/blockdata/mod.rs
index 943de54a..234f1623 100644
--- a/bitcoin/src/blockdata/mod.rs
+++ b/bitcoin/src/blockdata/mod.rs
@@ -16,9 +16,5 @@
 pub mod weight;
 pub mod witness;
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
-    fee_rate::FeeRate,
-    weight::Weight
-};
+pub use fee_rate::FeeRate;
+pub use weight::Weight;
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index b9da4c56..337eb89b 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -146,11 +146,29 @@ pub fn to_bytes(&self) -> Vec<u8> { self.0.to_owned() }
     #[inline]
     pub fn bytes(&self) -> Bytes<'_> { Bytes(self.as_bytes().iter().copied()) }
 
+    /// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem
+    /// script").
+    #[inline]
+    #[deprecated(since = "0.31.0", note = "use to_p2wsh instead")]
+    pub fn to_v0_p2wsh(&self) -> ScriptBuf { self.to_p2wsh() }
+
     /// Computes the P2WSH output corresponding to this witnessScript (aka the "witness redeem
     /// script").
     #[inline]
     pub fn to_p2wsh(&self) -> ScriptBuf { ScriptBuf::new_p2wsh(&self.wscript_hash()) }
 
+    /// Computes P2TR output with a given internal key and a single script spending path equal to
+    /// the current script, assuming that the script is a Tapscript.
+    #[inline]
+    #[deprecated(since = "0.31.0", note = "use to_p2tr instead")]
+    pub fn to_v1_p2tr<C: Verification>(
+        &self,
+        secp: &Secp256k1<C>,
+        internal_key: UntweakedPublicKey,
+    ) -> ScriptBuf {
+        self.to_p2tr(secp, internal_key)
+    }
+
     /// Computes P2TR output with a given internal key and a single script spending path equal to
     /// the current script, assuming that the script is a Tapscript.
     #[inline]
@@ -220,7 +238,7 @@ pub fn is_p2pk(&self) -> bool { self.p2pk_pubkey_bytes().is_some() }
     ///
     /// This may return `None` even when [`is_p2pk()`](Self::is_p2pk) returns true.
     /// This happens when the public key is invalid (e.g. the point not being on the curve).
-    /// In this situation the script is unspendable.
+    /// It also implies the script is unspendable.
     #[inline]
     pub fn p2pk_public_key(&self) -> Option<PublicKey> {
         PublicKey::from_slice(self.p2pk_pubkey_bytes()?).ok()
@@ -311,6 +329,11 @@ pub fn is_witness_program(&self) -> bool {
             && script_len - 2 == push_opbyte as usize
     }
 
+    /// Checks whether a script pubkey is a P2WSH output.
+    #[inline]
+    #[deprecated(since = "0.31.0", note = "use is_p2wsh instead")]
+    pub fn is_v0_p2wsh(&self) -> bool { self.is_p2wsh() }
+
     /// Checks whether a script pubkey is a P2WSH output.
     #[inline]
     pub fn is_p2wsh(&self) -> bool {
@@ -319,6 +342,11 @@ pub fn is_p2wsh(&self) -> bool {
             && self.0[1] == OP_PUSHBYTES_32.to_u8()
     }
 
+    /// Checks whether a script pubkey is a P2WPKH output.
+    #[inline]
+    #[deprecated(since = "0.31.0", note = "use is_p2wpkh instead")]
+    pub fn is_v0_p2wpkh(&self) -> bool { self.is_p2wpkh() }
+
     /// Checks whether a script pubkey is a P2WPKH output.
     #[inline]
     pub fn is_p2wpkh(&self) -> bool {
@@ -335,6 +363,11 @@ pub fn is_p2wpkh(&self) -> bool {
         }
     }
 
+    /// Checks whether a script pubkey is a P2TR output.
+    #[inline]
+    #[deprecated(since = "0.31.0", note = "use is_p2tr instead")]
+    pub fn is_v1_p2tr(&self) -> bool { self.is_p2tr() }
+
     /// Checks whether a script pubkey is a P2TR output.
     #[inline]
     pub fn is_p2tr(&self) -> bool {
@@ -521,12 +554,12 @@ pub fn instruction_indices_minimal(&self) -> InstructionIndices {
         InstructionIndices::from_instructions(self.instructions_minimal())
     }
 
-    /// Writes the human-readable assembly representation of the script to the formatter.
+    /// Writes the assembly decoding of the script to the formatter.
     pub fn fmt_asm(&self, f: &mut dyn fmt::Write) -> fmt::Result {
         bytes_to_asm_fmt(self.as_ref(), f)
     }
 
-    /// Returns the human-readable assembly representation of the script.
+    /// Returns the assembly decoding of the script.
     pub fn to_asm_string(&self) -> String {
         let mut buf = String::new();
         self.fmt_asm(&mut buf).unwrap();
@@ -537,7 +570,7 @@ pub fn to_asm_string(&self) -> String {
     ///
     /// This is a more convenient and performant way to write `format!("{:x}", script)`.
     /// For better performance you should generally prefer displaying the script but if `String` is
-    /// required (this is common in tests) this method can be used.
+    /// required (this is common in tests) this method is can be used.
     pub fn to_hex_string(&self) -> String { self.as_bytes().to_lower_hex_string() }
 
     /// Returns the first opcode of the script (if there is any).
diff --git a/bitcoin/src/blockdata/script/mod.rs b/bitcoin/src/blockdata/script/mod.rs
index 413f06cc..489bc716 100644
--- a/bitcoin/src/blockdata/script/mod.rs
+++ b/bitcoin/src/blockdata/script/mod.rs
@@ -47,16 +47,6 @@
 //! At the time of writing there's only one operation using the cache - `push_verify`, so the cache
 //! is minimal but we may extend it in the future if needed.
 
-mod borrowed;
-mod builder;
-mod instruction;
-mod owned;
-mod push_bytes;
-#[cfg(test)]
-mod tests;
-pub mod witness_program;
-pub mod witness_version;
-
 use alloc::rc::Rc;
 #[cfg(any(not(rust_v_1_60), target_has_atomic = "ptr"))]
 use alloc::sync::Arc;
@@ -75,15 +65,21 @@
 use crate::prelude::*;
 use crate::{io, OutPoint};
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
-    borrowed::*,
-    builder::*,
-    instruction::*,
-    owned::*,
-    push_bytes::*,
-};
+mod borrowed;
+mod builder;
+mod instruction;
+mod owned;
+mod push_bytes;
+#[cfg(test)]
+mod tests;
+pub mod witness_program;
+pub mod witness_version;
+
+pub use self::borrowed::*;
+pub use self::builder::*;
+pub use self::instruction::*;
+pub use self::owned::*;
+pub use self::push_bytes::*;
 
 hashes::hash_newtype! {
     /// A hash of Bitcoin Script bytecode.
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index 904c0137..51f89c81 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -98,18 +98,37 @@ pub fn new_p2sh(script_hash: &ScriptHash) -> Self {
             .into_script()
     }
 
+    /// Generates P2WPKH-type of scriptPubkey.
+    #[deprecated(since = "0.31.0", note = "use new_p2wpkh instead")]
+    pub fn new_v0_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self { Self::new_p2wpkh(pubkey_hash) }
+
     /// Generates P2WPKH-type of scriptPubkey.
     pub fn new_p2wpkh(pubkey_hash: &WPubkeyHash) -> Self {
         // pubkey hash is 20 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
         ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, pubkey_hash)
     }
 
+    /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script.
+    #[deprecated(since = "0.31.0", note = "use new_p2wsh instead")]
+    pub fn new_v0_p2wsh(script_hash: &WScriptHash) -> Self { Self::new_p2wsh(script_hash) }
+
     /// Generates P2WSH-type of scriptPubkey with a given hash of the redeem script.
     pub fn new_p2wsh(script_hash: &WScriptHash) -> Self {
         // script hash is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv0)
         ScriptBuf::new_witness_program_unchecked(WitnessVersion::V0, script_hash)
     }
 
+    /// Generates P2TR for script spending path using an internal public key and some optional
+    /// script tree merkle root.
+    #[deprecated(since = "0.31.0", note = "use new_p2tr instead")]
+    pub fn new_v1_p2tr<C: Verification>(
+        secp: &Secp256k1<C>,
+        internal_key: UntweakedPublicKey,
+        merkle_root: Option<TapNodeHash>,
+    ) -> Self {
+        Self::new_p2tr(secp, internal_key, merkle_root)
+    }
+
     /// Generates P2TR for script spending path using an internal public key and some optional
     /// script tree merkle root.
     pub fn new_p2tr<C: Verification>(
@@ -122,6 +141,12 @@ pub fn new_p2tr<C: Verification>(
         ScriptBuf::new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())
     }
 
+    /// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
+    #[deprecated(since = "0.31.0", note = "use new_p2tr_tweaked instead")]
+    pub fn new_v1_p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
+        Self::new_p2tr_tweaked(output_key)
+    }
+
     /// Generates P2TR for key spending path for a known [`TweakedPublicKey`].
     pub fn new_p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
         // output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
diff --git a/bitcoin/src/blockdata/script/push_bytes.rs b/bitcoin/src/blockdata/script/push_bytes.rs
index 3baec542..07cd6f28 100644
--- a/bitcoin/src/blockdata/script/push_bytes.rs
+++ b/bitcoin/src/blockdata/script/push_bytes.rs
@@ -5,13 +5,11 @@
 use core::borrow::{Borrow, BorrowMut};
 use core::ops::{Deref, DerefMut};
 
+pub use primitive::*;
+
 #[allow(unused)]
 use crate::prelude::*;
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::primitive::*;
-
 /// This module only contains required operations so that outside functions wouldn't accidentally
 /// break invariants. Therefore auditing this module should be sufficient.
 mod primitive {
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 68622ef1..6e38d250 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -23,6 +23,8 @@
 use crate::blockdata::locktime::relative;
 use crate::blockdata::script::{Script, ScriptBuf};
 use crate::blockdata::witness::Witness;
+#[cfg(feature = "bitcoinconsensus")]
+pub use crate::consensus::validation::TxVerifyError;
 use crate::consensus::{encode, Decodable, Encodable};
 use crate::hash_types::{Txid, Wtxid};
 use crate::internal_macros::impl_consensus_encoding;
@@ -34,11 +36,6 @@
 use crate::string::FromHexStr;
 use crate::{io, Amount, VarInt};
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[cfg(feature = "bitcoinconsensus")]
-#[doc(inline)]
-pub use crate::consensus::validation::TxVerifyError;
-
 /// The marker MUST be a 1-byte zero value: 0x00. (BIP-141)
 const SEGWIT_MARKER: u8 = 0x00;
 /// The flag MUST be a 1-byte non-zero value. Currently, 0x01 MUST be used. (BIP-141)
@@ -332,6 +329,12 @@ impl Sequence {
     /// BIP-68 relative lock time type flag mask.
     const LOCK_TYPE_MASK: u32 = 0x00400000;
 
+    /// The maximum allowable sequence number.
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Sequence::MAX`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Self { Self::MAX }
+
     /// Returns `true` if the sequence number enables absolute lock-time ([`Transaction::lock_time`]).
     #[inline]
     pub fn enables_absolute_lock_time(&self) -> bool { *self != Sequence::MAX }
@@ -786,6 +789,10 @@ pub fn vsize(&self) -> usize {
         self.weight().to_vbytes_ceil() as usize
     }
 
+    /// Returns the size of this transaction excluding the witness data.
+    #[deprecated(since = "0.31.0", note = "Use Transaction::base_size() instead")]
+    pub fn strippedsize(&self) -> usize { self.base_size() }
+
     /// Checks if this is a coinbase transaction.
     ///
     /// The first transaction in the block distributes the mining reward and is called the coinbase
@@ -797,6 +804,10 @@ pub fn is_coinbase(&self) -> bool {
         self.input.len() == 1 && self.input[0].previous_output.is_null()
     }
 
+    /// Checks if this is a coinbase transaction.
+    #[deprecated(since = "0.31.0", note = "use is_coinbase instead")]
+    pub fn is_coin_base(&self) -> bool { self.is_coinbase() }
+
     /// Returns `true` if the transaction itself opted in to be BIP-125-replaceable (RBF).
     ///
     /// # Warning
@@ -1323,26 +1334,6 @@ impl InputWeightPrediction {
     /// [signature grinding]: https://bitcoin.stackexchange.com/questions/111660/what-is-signature-grinding
     pub const P2WPKH_MAX: Self = InputWeightPrediction::from_slice(0, &[73, 33]);
 
-    /// Input weight prediction corresponding to spending of a P2PKH output with the largest possible
-    /// DER-encoded signature, and a compressed public key.
-    ///
-    /// If the input in your transaction uses P2PKH with a compressed key, you can use this instead of
-    /// [`InputWeightPrediction::new`].
-    ///
-    /// This is useful when you **do not** use [signature grinding] and want to ensure you are not
-    /// under-paying. See [`ground_p2pkh_compressed`](Self::ground_p2pkh_compressed) if you do use
-    /// signature grinding.
-    ///
-    /// [signature grinding]: https://bitcoin.stackexchange.com/questions/111660/what-is-signature-grinding
-    pub const P2PKH_COMPRESSED_MAX: Self = InputWeightPrediction::from_slice(107, &[]);
-
-    /// Input weight prediction corresponding to spending of a P2PKH output with the largest possible
-    /// DER-encoded signature, and an uncompressed public key.
-    ///
-    /// If the input in your transaction uses P2PKH with an uncompressed key, you can use this instead of
-    /// [`InputWeightPrediction::new`].
-    pub const P2PKH_UNCOMPRESSED_MAX: Self = InputWeightPrediction::from_slice(139, &[]);
-
     /// Input weight prediction corresponding to spending of taproot output using the key and
     /// default sighash.
     ///
@@ -1377,27 +1368,6 @@ pub const fn ground_p2wpkh(bytes_to_grind: usize) -> Self {
         InputWeightPrediction::from_slice(0, &[der_signature_size, 33])
     }
 
-    /// Input weight prediction corresponding to spending of a P2PKH output using [signature
-    /// grinding], and a compressed public key.
-    ///
-    /// If the input in your transaction uses compressed P2PKH and you use signature grinding you
-    /// can use this instead of [`InputWeightPrediction::new`]. See
-    /// [`P2PKH_COMPRESSED_MAX`](Self::P2PKH_COMPRESSED_MAX) if you don't use signature grinding.
-    ///
-    /// Note: `bytes_to_grind` is usually `1` because of exponential cost of higher values.
-    ///
-    /// # Panics
-    ///
-    /// The funcion panics in const context and debug builds if `bytes_to_grind` is higher than 62.
-    ///
-    /// [signature grinding]: https://bitcoin.stackexchange.com/questions/111660/what-is-signature-grinding
-    pub const fn ground_p2pkh_compressed(bytes_to_grind: usize) -> Self {
-        // Written to trigger const/debug panic for unreasonably high values.
-        let der_signature_size = 10 + (62 - bytes_to_grind);
-
-        InputWeightPrediction::from_slice(2 + 33 + der_signature_size, &[])
-    }
-
     /// Computes the prediction for a single input.
     pub fn new<T>(input_script_len: usize, witness_element_lengths: T) -> Self
     where
@@ -2156,7 +2126,7 @@ mod benches {
 
     use super::Transaction;
     use crate::consensus::{deserialize, Encodable};
-    use crate::io::sink;
+    use crate::EmptyWrite;
 
     const SOME_TX: &str = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
 
@@ -2191,7 +2161,7 @@ pub fn bench_transaction_serialize_logic(bh: &mut Bencher) {
         let tx: Transaction = deserialize(&raw_tx).unwrap();
 
         bh.iter(|| {
-            let size = tx.consensus_encode(&mut sink());
+            let size = tx.consensus_encode(&mut EmptyWrite);
             black_box(&size);
         });
     }
diff --git a/bitcoin/src/blockdata/weight.rs b/bitcoin/src/blockdata/weight.rs
index 6f6fa872..fa06c96e 100644
--- a/bitcoin/src/blockdata/weight.rs
+++ b/bitcoin/src/blockdata/weight.rs
@@ -54,6 +54,21 @@ pub fn from_vb(vb: u64) -> Option<Self> {
         vb.checked_mul(Self::WITNESS_SCALE_FACTOR).map(Weight::from_wu)
     }
 
+    /// Constructs `Weight` from virtual bytes in const context.
+    pub const fn from_vb_const(vb: u64) -> Weight {
+        match vb.checked_mul(Self::WITNESS_SCALE_FACTOR) {
+            Some(weight) => Weight(weight),
+            None => {
+                // TODO replace with panic!() when MSRV = 1.57+
+                #[allow(unconditional_panic)]
+                // disabling this lint until panic!() can be used.
+                #[allow(clippy::let_unit_value)]
+                let _int_overflow_scaling_weight = [(); 0][1];
+                Weight(0)
+            }
+        }
+    }
+
     /// Constructs `Weight` from virtual bytes without an overflow check.
     pub const fn from_vb_unchecked(vb: u64) -> Self { Weight::from_wu(vb * 4) }
 
@@ -150,6 +165,12 @@ fn from_vb() {
         assert_eq!(None, vb);
     }
 
+    #[test]
+    fn from_vb_const() {
+        const WU: Weight = Weight::from_vb_const(1);
+        assert_eq!(Weight(4), WU);
+    }
+
     #[test]
     fn from_vb_unchecked() {
         let vb = Weight::from_vb_unchecked(1);
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index ef72b551..041c6590 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -14,6 +14,7 @@
 use crate::crypto::ecdsa;
 use crate::io::{self, Read, Write};
 use crate::prelude::*;
+use crate::sighash::EcdsaSighashType;
 use crate::taproot::TAPROOT_ANNEX_PREFIX;
 use crate::{Script, VarInt};
 
@@ -287,6 +288,10 @@ pub fn iter(&self) -> Iter {
     /// Returns the number of elements this witness holds.
     pub fn len(&self) -> usize { self.witness_elements }
 
+    /// Returns the bytes required when this Witness is consensus encoded.
+    #[deprecated(since = "0.31.0", note = "use size instead")]
+    pub fn serialized_len(&self) -> usize { self.size() }
+
     /// Returns the number of bytes this witness contributes to a transactions total size.
     pub fn size(&self) -> usize {
         let mut size: usize = 0;
@@ -339,6 +344,21 @@ fn push_slice(&mut self, new_element: &[u8]) {
         self.content[end_varint..end_varint + new_element.len()].copy_from_slice(new_element);
     }
 
+    /// Pushes a DER-encoded ECDSA signature with a signature hash type as a new element on the
+    /// witness, requires an allocation.
+    #[deprecated(since = "0.30.0", note = "use push_ecdsa_signature instead")]
+    pub fn push_bitcoin_signature(
+        &mut self,
+        signature: &secp256k1::ecdsa::SerializedSignature,
+        hash_type: EcdsaSighashType,
+    ) {
+        // Note that a maximal length ECDSA signature is 72 bytes, plus the sighash type makes 73
+        let mut sig = [0; 73];
+        sig[..signature.len()].copy_from_slice(signature);
+        sig[signature.len()] = hash_type as u8;
+        self.push(&sig[..signature.len() + 1]);
+    }
+
     /// Pushes, as a new element on the witness, an ECDSA signature.
     ///
     /// Pushes the DER encoded signature + sighash_type, requires an allocation.
@@ -381,7 +401,7 @@ pub fn nth(&self, index: usize) -> Option<&[u8]> {
     /// This does not guarantee that this represents a P2TR [`Witness`]. It
     /// merely gets the second to last or third to last element depending on
     /// the first byte of the last element being equal to 0x50. See
-    /// [Script::is_p2tr](crate::blockdata::script::Script::is_p2tr) to
+    /// [Script::is_v1_p2tr](crate::blockdata::script::Script::is_v1_p2tr) to
     /// check whether this is actually a Taproot witness.
     pub fn tapscript(&self) -> Option<&Script> {
         let len = self.len();
@@ -542,7 +562,6 @@ mod test {
 
     use super::*;
     use crate::consensus::{deserialize, serialize};
-    use crate::sighash::EcdsaSighashType;
     use crate::Transaction;
 
     fn append_u32_vec(mut v: Vec<u8>, n: &[u32]) -> Vec<u8> {
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index e618c925..78fa7a19 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -320,7 +320,7 @@ fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
     /// instead.
     #[inline]
     fn consensus_decode<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
-        Self::consensus_decode_from_finite_reader(&mut reader.take(MAX_VEC_SIZE as u64))
+        Self::consensus_decode_from_finite_reader(reader.take(MAX_VEC_SIZE as u64).by_ref())
     }
 }
 
@@ -385,6 +385,11 @@ fn consensus_encode<W: io::Write + ?Sized>(
 
 #[allow(clippy::len_without_is_empty)] // VarInt has on concept of 'is_empty'.
 impl VarInt {
+    /// Gets the length of this VarInt when encoded.
+    #[inline]
+    #[deprecated(since = "0.31.0", note = "use size instead")]
+    pub const fn len(&self) -> usize { self.size() }
+
     /// Returns the number of bytes this varint contributes to a transaction size.
     ///
     /// Returns 1 for 0..=0xFC, 3 for 0xFD..=(2^16-1), 5 for 0x10000..=(2^32-1), and 9 otherwise.
@@ -643,11 +648,11 @@ fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
 #[cfg(feature = "std")]
 impl_vec!(AddrV2Message);
 
-pub(crate) fn consensus_encode_with_size<W: io::Write + ?Sized>(
+pub(crate) fn consensus_encode_with_size<W: io::Write>(
     data: &[u8],
-    w: &mut W,
+    mut w: W,
 ) -> Result<usize, io::Error> {
-    let vi_len = VarInt(data.len() as u64).consensus_encode(w)?;
+    let vi_len = VarInt(data.len() as u64).consensus_encode(&mut w)?;
     w.emit_slice(data)?;
     Ok(vi_len + data.len())
 }
@@ -662,8 +667,8 @@ struct ReadBytesFromFiniteReaderOpts {
 /// This function relies on reader being bound in amount of data
 /// it returns for OOM protection. See [`Decodable::consensus_decode_from_finite_reader`].
 #[inline]
-fn read_bytes_from_finite_reader<D: io::Read + ?Sized>(
-    d: &mut D,
+fn read_bytes_from_finite_reader<D: io::Read>(
+    mut d: D,
     mut opts: ReadBytesFromFiniteReaderOpts,
 ) -> Result<Vec<u8>, Error> {
     let mut ret = vec![];
@@ -1234,7 +1239,7 @@ fn test_read_bytes_from_finite_reader() {
         for chunk_size in 1..20 {
             assert_eq!(
                 read_bytes_from_finite_reader(
-                    &mut io::Cursor::new(&data),
+                    io::Cursor::new(&data),
                     ReadBytesFromFiniteReaderOpts { len: data.len(), chunk_size }
                 )
                 .unwrap(),
diff --git a/bitcoin/src/consensus/mod.rs b/bitcoin/src/consensus/mod.rs
index 1155a612..454ac924 100644
--- a/bitcoin/src/consensus/mod.rs
+++ b/bitcoin/src/consensus/mod.rs
@@ -8,20 +8,17 @@
 
 pub mod encode;
 pub mod params;
-#[cfg(feature = "serde")]
-pub mod serde;
 #[cfg(feature = "bitcoinconsensus")]
 pub mod validation;
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
-    encode::{deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt},
-    params::Params,
+pub use self::encode::{
+    deserialize, deserialize_partial, serialize, Decodable, Encodable, ReadExt, WriteExt,
 };
-
+pub use self::params::Params;
 #[cfg(feature = "bitcoinconsensus")]
-#[doc(inline)]
 pub use self::validation::{
     verify_script, verify_script_with_flags, verify_transaction, verify_transaction_with_flags,
 };
+
+#[cfg(feature = "serde")]
+pub mod serde;
diff --git a/bitcoin/src/consensus/params.rs b/bitcoin/src/consensus/params.rs
index f181ab55..e2d747f8 100644
--- a/bitcoin/src/consensus/params.rs
+++ b/bitcoin/src/consensus/params.rs
@@ -50,7 +50,7 @@ pub struct Params {
 
 impl Params {
     /// Creates parameters set for the given network.
-    pub const fn new(network: Network) -> Self {
+    pub fn new(network: Network) -> Self {
         match network {
             Network::Bitcoin => Params {
                 network: Network::Bitcoin,
@@ -116,19 +116,3 @@ pub fn difficulty_adjustment_interval(&self) -> u64 {
         self.pow_target_timespan / self.pow_target_spacing
     }
 }
-
-impl From<Network> for Params {
-    fn from(value: Network) -> Self { Self::new(value) }
-}
-
-impl From<&Network> for Params {
-    fn from(value: &Network) -> Self { Self::new(*value) }
-}
-
-impl From<Network> for &'static Params {
-    fn from(value: Network) -> Self { value.params() }
-}
-
-impl From<&Network> for &'static Params {
-    fn from(value: &Network) -> Self { value.params() }
-}
diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs
index 1b201996..62c17469 100644
--- a/bitcoin/src/consensus/serde.rs
+++ b/bitcoin/src/consensus/serde.rs
@@ -174,8 +174,10 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         self.0.consensus_encode(&mut writer).map_err(|error| {
             #[cfg(debug_assertions)]
             {
+                use crate::StdError;
+
                 if error.kind() != io::ErrorKind::Other
-                    || error.get_ref().is_some()
+                    || error.source().is_some()
                     || !writer.writer.was_error
                 {
                     panic!(
@@ -427,6 +429,8 @@ impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> IterReader<E, I> {
     fn new(iterator: I) -> Self { IterReader { iterator: iterator.fuse(), error: None } }
 
     fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
+        use crate::StdError;
+
         let result = T::consensus_decode(&mut self);
         match (result, self.error) {
             (Ok(_), None) if self.iterator.next().is_some() => {
@@ -434,7 +438,7 @@ fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
             },
             (Ok(value), None) => Ok(value),
             (Ok(_), Some(error)) => panic!("{} silently ate the error: {:?}", core::any::type_name::<T>(), error),
-            (Err(ConsensusError::Io(io_error)), Some(de_error)) if io_error.kind() == io::ErrorKind::Other && io_error.get_ref().is_none() => Err(DecodeError::Other(de_error)),
+            (Err(ConsensusError::Io(io_error)), Some(de_error)) if io_error.kind() == io::ErrorKind::Other && io_error.source().is_none() => Err(DecodeError::Other(de_error)),
             (Err(consensus_error), None) => Err(DecodeError::Consensus(consensus_error)),
             (Err(ConsensusError::Io(io_error)), de_error) => panic!("Unexpected IO error {:?} returned from {}::consensus_decode(), deserialization error: {:?}", io_error, core::any::type_name::<T>(), de_error),
             (Err(consensus_error), Some(de_error)) => panic!("{} should've returned `Other` IO error because of deserialization error {:?} but it returned consensus error {:?} instead", core::any::type_name::<T>(), de_error, consensus_error),
@@ -490,6 +494,8 @@ pub fn serialize<T: Encodable, S: Serializer>(
         if serializer.is_human_readable() {
             serializer.collect_str(&DisplayWrapper::<'_, _, E>(value, Default::default()))
         } else {
+            use crate::StdError;
+
             let serializer = serializer.serialize_seq(None)?;
             let mut writer = BinWriter { serializer, error: None };
 
@@ -499,7 +505,7 @@ pub fn serialize<T: Encodable, S: Serializer>(
                 (Ok(_), Some(error)) =>
                     panic!("{} silently ate an IO error: {:?}", core::any::type_name::<T>(), error),
                 (Err(io_error), Some(ser_error))
-                    if io_error.kind() == io::ErrorKind::Other && io_error.get_ref().is_none() =>
+                    if io_error.kind() == io::ErrorKind::Other && io_error.source().is_none() =>
                     Err(ser_error),
                 (Err(io_error), ser_error) => panic!(
                     "{} returned an unexpected IO error: {:?} serialization error: {:?}",
diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs
index 14251fac..e982e522 100644
--- a/bitcoin/src/crypto/ecdsa.rs
+++ b/bitcoin/src/crypto/ecdsa.rs
@@ -2,7 +2,7 @@
 
 //! ECDSA Bitcoin signatures.
 //!
-//! This module provides ECDSA signatures used by Bitcoin that can be roundtrip (de)serialized.
+//! This module provides ECDSA signatures used Bitcoin that can be roundtrip (de)serialized.
 
 use core::str::FromStr;
 use core::{fmt, iter};
@@ -29,7 +29,7 @@ pub struct Signature {
 }
 
 impl Signature {
-    /// Constructs an ECDSA Bitcoin signature for [`EcdsaSighashType::All`].
+    /// Constructs an ECDSA bitcoin signature for [`EcdsaSighashType::All`].
     pub fn sighash_all(sig: secp256k1::ecdsa::Signature) -> Signature {
         Signature { sig, hash_ty: EcdsaSighashType::All }
     }
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 6c4d354c..b6321abe 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -12,6 +12,9 @@
 use hashes::{hash160, Hash};
 use hex::FromHex;
 use internals::write_err;
+#[cfg(feature = "rand-std")]
+pub use secp256k1::rand;
+pub use secp256k1::{self, constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey};
 
 use crate::crypto::ecdsa;
 use crate::network::Network;
@@ -19,12 +22,6 @@
 use crate::taproot::{TapNodeHash, TapTweakHash};
 use crate::{base58, io};
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-pub use secp256k1::{self, constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey};
-
-#[cfg(feature = "rand-std")]
-pub use secp256k1::rand;
-
 /// A Bitcoin ECDSA public key
 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct PublicKey {
@@ -71,7 +68,7 @@ pub fn wpubkey_hash(&self) -> Option<WPubkeyHash> {
     }
 
     /// Write the public key into a writer
-    pub fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
+    pub fn write_into<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
         self.with_serialized(|bytes| writer.write_all(bytes))
     }
 
@@ -79,7 +76,7 @@ pub fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io
     ///
     /// This internally reads the first byte before reading the rest, so
     /// use of a `BufReader` is recommended.
-    pub fn read_from<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, io::Error> {
+    pub fn read_from<R: io::Read>(mut reader: R) -> Result<Self, io::Error> {
         let mut bytes = [0; 65];
 
         reader.read_exact(&mut bytes[0..1])?;
@@ -87,7 +84,7 @@ pub fn read_from<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, io::Error
 
         reader.read_exact(&mut bytes[1..])?;
         Self::from_slice(bytes).map_err(|e| {
-            // Need a static string for no-std io
+            // Need a static string for core2
             #[cfg(feature = "std")]
             let reason = e;
             #[cfg(not(feature = "std"))]
@@ -509,9 +506,19 @@ impl fmt::Display for TweakedPublicKey {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
 }
 
+/// Untweaked BIP-340 key pair
+#[deprecated(since = "0.31.0", note = "use UntweakedKeypair instead")]
+#[allow(deprecated)]
+pub type UntweakedKeyPair = UntweakedKeypair;
+
 /// Untweaked BIP-340 key pair
 pub type UntweakedKeypair = Keypair;
 
+/// Tweaked BIP-340 key pair
+#[deprecated(since = "0.31.0", note = "use TweakedKeypair instead")]
+#[allow(deprecated)]
+pub type TweakedKeyPair = TweakedKeypair;
+
 /// Tweaked BIP-340 key pair
 ///
 /// # Examples
@@ -755,6 +762,7 @@ mod tests {
 
     use super::*;
     use crate::address::Address;
+    use crate::io;
     use crate::network::Network::{Bitcoin, Testnet};
 
     #[test]
@@ -916,11 +924,11 @@ fn pubkey_read_write() {
 
         // sanity checks
         assert!(PublicKey::read_from(&mut cursor).is_err());
-        assert!(PublicKey::read_from(&mut io::Cursor::new(&[])).is_err());
-        assert!(PublicKey::read_from(&mut io::Cursor::new(&[0; 33][..])).is_err());
-        assert!(PublicKey::read_from(&mut io::Cursor::new(&[2; 32][..])).is_err());
-        assert!(PublicKey::read_from(&mut io::Cursor::new(&[0; 65][..])).is_err());
-        assert!(PublicKey::read_from(&mut io::Cursor::new(&[4; 64][..])).is_err());
+        assert!(PublicKey::read_from(io::Cursor::new(&[])).is_err());
+        assert!(PublicKey::read_from(io::Cursor::new(&[0; 33][..])).is_err());
+        assert!(PublicKey::read_from(io::Cursor::new(&[2; 32][..])).is_err());
+        assert!(PublicKey::read_from(io::Cursor::new(&[0; 65][..])).is_err());
+        assert!(PublicKey::read_from(io::Cursor::new(&[4; 64][..])).is_err());
     }
 
     #[test]
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index 6fb2ddbe..6dcb6e62 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -15,7 +15,6 @@
 use core::{fmt, str};
 
 use hashes::{hash_newtype, sha256, sha256d, sha256t_hash_newtype, Hash};
-use internals::write_err;
 
 use crate::blockdata::witness::Witness;
 use crate::consensus::{encode, Encodable};
@@ -224,15 +223,15 @@ pub enum Error {
 
     /// There are mismatches in the number of prevouts provided compared to the number of inputs in
     /// the transaction.
-    PrevoutsSize(PrevoutsSizeError),
+    PrevoutsSize,
 
     /// Requested a prevout index which is greater than the number of prevouts provided or a
     /// [`Prevouts::One`] with different index.
-    PrevoutsIndex(PrevoutsIndexError),
+    PrevoutIndex,
 
     /// A single prevout has been provided but all prevouts are needed unless using
     /// `SIGHASH_ANYONECANPAY`.
-    PrevoutsKind(PrevoutsKindError),
+    PrevoutKind,
 
     /// Annex must be at least one byte long and the first bytes must be `0x50`.
     WrongAnnex,
@@ -252,9 +251,9 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             Io(error_kind) => write!(f, "writer errored: {:?}", error_kind),
             IndexOutOfInputsBounds { index, inputs_size } => write!(f, "requested index ({}) is greater or equal than the number of transaction inputs ({})", index, inputs_size),
             SingleWithoutCorrespondingOutput { index, outputs_size } => write!(f, "SIGHASH_SINGLE for input ({}) haven't a corresponding output (#outputs:{})", index, outputs_size),
-            PrevoutsSize(ref e) => write_err!(f, "prevouts size"; e),
-            PrevoutsIndex(ref e) => write_err!(f, "prevouts index"; e),
-            PrevoutsKind(ref e) => write_err!(f, "prevouts kind"; e),
+            PrevoutsSize => write!(f, "number of supplied prevouts differs from the number of inputs in transaction"),
+            PrevoutIndex => write!(f, "the index requested is greater than available prevouts or different from the provided [Provided::Anyone] index"),
+            PrevoutKind => write!(f, "a single prevout has been provided but all prevouts are needed without `ANYONECANPAY`"),
             WrongAnnex => write!(f, "annex must be at least one byte long and the first bytes must be `0x50`"),
             InvalidSighashType(hash_ty) => write!(f, "Invalid taproot signature hash type : {} ", hash_ty),
             NotP2wpkhScript => write!(f, "script is not a script pubkey for a p2wpkh output"),
@@ -268,12 +267,12 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
         use Error::*;
 
         match *self {
-            PrevoutsSize(ref e) => Some(e),
-            PrevoutsIndex(ref e) => Some(e),
-            PrevoutsKind(ref e) => Some(e),
             Io(_)
             | IndexOutOfInputsBounds { .. }
             | SingleWithoutCorrespondingOutput { .. }
+            | PrevoutsSize
+            | PrevoutIndex
+            | PrevoutKind
             | WrongAnnex
             | InvalidSighashType(_)
             | NotP2wpkhScript => None,
@@ -285,114 +284,36 @@ impl From<io::Error> for Error {
     fn from(e: io::Error) -> Self { Error::Io(e.kind()) }
 }
 
-impl From<PrevoutsSizeError> for Error {
-    fn from(e: PrevoutsSizeError) -> Self { Self::PrevoutsSize(e) }
-}
-
-impl From<PrevoutsKindError> for Error {
-    fn from(e: PrevoutsKindError) -> Self { Self::PrevoutsKind(e) }
-}
-
-impl From<PrevoutsIndexError> for Error {
-    fn from(e: PrevoutsIndexError) -> Self { Self::PrevoutsIndex(e) }
-}
-
 impl<'u, T> Prevouts<'u, T>
 where
     T: Borrow<TxOut>,
 {
-    fn check_all(&self, tx: &Transaction) -> Result<(), PrevoutsSizeError> {
+    fn check_all(&self, tx: &Transaction) -> Result<(), Error> {
         if let Prevouts::All(prevouts) = self {
             if prevouts.len() != tx.input.len() {
-                return Err(PrevoutsSizeError);
+                return Err(Error::PrevoutsSize);
             }
         }
         Ok(())
     }
 
-    fn get_all(&self) -> Result<&[T], PrevoutsKindError> {
+    fn get_all(&self) -> Result<&[T], Error> {
         match self {
             Prevouts::All(prevouts) => Ok(*prevouts),
-            _ => Err(PrevoutsKindError),
+            _ => Err(Error::PrevoutKind),
         }
     }
 
-    fn get(&self, input_index: usize) -> Result<&TxOut, PrevoutsIndexError> {
+    fn get(&self, input_index: usize) -> Result<&TxOut, Error> {
         match self {
             Prevouts::One(index, prevout) =>
                 if input_index == *index {
                     Ok(prevout.borrow())
                 } else {
-                    Err(PrevoutsIndexError::InvalidOneIndex)
+                    Err(Error::PrevoutIndex)
                 },
-            Prevouts::All(prevouts) => prevouts
-                .get(input_index)
-                .map(|x| x.borrow())
-                .ok_or(PrevoutsIndexError::InvalidAllIndex),
-        }
-    }
-}
-
-/// The number of supplied prevouts differs from the number of inputs in the transaction.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]
-pub struct PrevoutsSizeError;
-
-impl fmt::Display for PrevoutsSizeError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "number of supplied prevouts differs from the number of inputs in transaction")
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for PrevoutsSizeError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
-}
-
-/// A single prevout was been provided but all prevouts are needed without `ANYONECANPAY`.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]
-pub struct PrevoutsKindError;
-
-impl fmt::Display for PrevoutsKindError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "single prevout provided but all prevouts are needed without `ANYONECANPAY`")
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for PrevoutsKindError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
-}
-
-/// [`Prevouts`] index related errors.
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum PrevoutsIndexError {
-    /// Invalid index when accessing a [`Prevouts::One`] kind.
-    InvalidOneIndex,
-    /// Invalid index when accessing a [`Prevouts::All`] kind.
-    InvalidAllIndex,
-}
-
-impl fmt::Display for PrevoutsIndexError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        use PrevoutsIndexError::*;
-
-        match *self {
-            InvalidOneIndex => write!(f, "invalid index when accessing a Prevouts::One kind"),
-            InvalidAllIndex => write!(f, "invalid index when accessing a Prevouts::All kind"),
-        }
-    }
-}
-
-#[cfg(feature = "std")]
-impl std::error::Error for PrevoutsIndexError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        use PrevoutsIndexError::*;
-
-        match *self {
-            InvalidOneIndex | InvalidAllIndex => None,
+            Prevouts::All(prevouts) =>
+                prevouts.get(input_index).map(|x| x.borrow()).ok_or(Error::PrevoutIndex),
         }
     }
 }
@@ -673,9 +594,9 @@ pub fn into_transaction(self) -> R { self.tx }
 
     /// Encodes the BIP341 signing data for any flag type into a given object implementing a
     /// [`io::Write`] trait.
-    pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut>>(
+    pub fn taproot_encode_signing_data_to<Write: io::Write, T: Borrow<TxOut>>(
         &mut self,
-        writer: &mut Write,
+        mut writer: Write,
         input_index: usize,
         prevouts: &Prevouts<T>,
         annex: Option<Annex>,
@@ -687,18 +608,18 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
         let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
 
         // epoch
-        0u8.consensus_encode(writer)?;
+        0u8.consensus_encode(&mut writer)?;
 
         // * Control:
         // hash_type (1).
-        (sighash_type as u8).consensus_encode(writer)?;
+        (sighash_type as u8).consensus_encode(&mut writer)?;
 
         // * Transaction Data:
         // nVersion (4): the nVersion of the transaction.
-        self.tx.borrow().version.consensus_encode(writer)?;
+        self.tx.borrow().version.consensus_encode(&mut writer)?;
 
         // nLockTime (4): the nLockTime of the transaction.
-        self.tx.borrow().lock_time.consensus_encode(writer)?;
+        self.tx.borrow().lock_time.consensus_encode(&mut writer)?;
 
         // If the hash_type & 0x80 does not equal SIGHASH_ANYONECANPAY:
         //     sha_prevouts (32): the SHA256 of the serialization of all input outpoints.
@@ -706,16 +627,16 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
         //     sha_scriptpubkeys (32): the SHA256 of the serialization of all spent output scriptPubKeys.
         //     sha_sequences (32): the SHA256 of the serialization of all input nSequence.
         if !anyone_can_pay {
-            self.common_cache().prevouts.consensus_encode(writer)?;
-            self.taproot_cache(prevouts.get_all()?).amounts.consensus_encode(writer)?;
-            self.taproot_cache(prevouts.get_all()?).script_pubkeys.consensus_encode(writer)?;
-            self.common_cache().sequences.consensus_encode(writer)?;
+            self.common_cache().prevouts.consensus_encode(&mut writer)?;
+            self.taproot_cache(prevouts.get_all()?).amounts.consensus_encode(&mut writer)?;
+            self.taproot_cache(prevouts.get_all()?).script_pubkeys.consensus_encode(&mut writer)?;
+            self.common_cache().sequences.consensus_encode(&mut writer)?;
         }
 
         // If hash_type & 3 does not equal SIGHASH_NONE or SIGHASH_SINGLE:
         //     sha_outputs (32): the SHA256 of the serialization of all outputs in CTxOut format.
         if sighash != TapSighashType::None && sighash != TapSighashType::Single {
-            self.common_cache().outputs.consensus_encode(writer)?;
+            self.common_cache().outputs.consensus_encode(&mut writer)?;
         }
 
         // * Data about this input:
@@ -728,7 +649,7 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
         if leaf_hash_code_separator.is_some() {
             spend_type |= 2u8;
         }
-        spend_type.consensus_encode(writer)?;
+        spend_type.consensus_encode(&mut writer)?;
 
         // If hash_type & 0x80 equals SIGHASH_ANYONECANPAY:
         //      outpoint (36): the COutPoint of this input (32-byte hash + 4-byte little-endian).
@@ -742,12 +663,12 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
                     inputs_size: self.tx.borrow().input.len(),
                 })?;
             let previous_output = prevouts.get(input_index)?;
-            txin.previous_output.consensus_encode(writer)?;
-            previous_output.value.consensus_encode(writer)?;
-            previous_output.script_pubkey.consensus_encode(writer)?;
-            txin.sequence.consensus_encode(writer)?;
+            txin.previous_output.consensus_encode(&mut writer)?;
+            previous_output.value.consensus_encode(&mut writer)?;
+            previous_output.script_pubkey.consensus_encode(&mut writer)?;
+            txin.sequence.consensus_encode(&mut writer)?;
         } else {
-            (input_index as u32).consensus_encode(writer)?;
+            (input_index as u32).consensus_encode(&mut writer)?;
         }
 
         // If an annex is present (the lowest bit of spend_type is set):
@@ -757,7 +678,7 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
             let mut enc = sha256::Hash::engine();
             annex.consensus_encode(&mut enc)?;
             let hash = sha256::Hash::from_engine(enc);
-            hash.consensus_encode(writer)?;
+            hash.consensus_encode(&mut writer)?;
         }
 
         // * Data about this output:
@@ -775,7 +696,7 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
                 })?
                 .consensus_encode(&mut enc)?;
             let hash = sha256::Hash::from_engine(enc);
-            hash.consensus_encode(writer)?;
+            hash.consensus_encode(&mut writer)?;
         }
 
         //     if (scriptpath):
@@ -783,9 +704,9 @@ pub fn taproot_encode_signing_data_to<Write: io::Write + ?Sized, T: Borrow<TxOut
         //         ss += bytes([0])
         //         ss += struct.pack("<i", codeseparator_pos)
         if let Some((hash, code_separator_pos)) = leaf_hash_code_separator {
-            hash.as_byte_array().consensus_encode(writer)?;
-            KEY_VERSION_0.consensus_encode(writer)?;
-            code_separator_pos.consensus_encode(writer)?;
+            hash.as_byte_array().consensus_encode(&mut writer)?;
+            KEY_VERSION_0.consensus_encode(&mut writer)?;
+            code_separator_pos.consensus_encode(&mut writer)?;
         }
 
         Ok(())
@@ -854,15 +775,29 @@ pub fn taproot_script_spend_signature_hash<S: Into<TapLeafHash>, T: Borrow<TxOut
         Ok(TapSighash::from_engine(enc))
     }
 
+    /// Encodes the BIP143 signing data for any flag type into a given object implementing a
+    /// [`std::io::Write`] trait.
+    #[deprecated(since = "0.31.0", note = "use segwit_v0_encode_signing_data_to instead")]
+    pub fn segwit_encode_signing_data_to<Write: io::Write>(
+        &mut self,
+        writer: Write,
+        input_index: usize,
+        script_code: &Script,
+        value: Amount,
+        sighash_type: EcdsaSighashType,
+    ) -> Result<(), Error> {
+        self.segwit_v0_encode_signing_data_to(writer, input_index, script_code, value, sighash_type)
+    }
+
     /// Encodes the BIP143 signing data for any flag type into a given object implementing a
     /// [`std::io::Write`] trait.
     ///
     /// `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`].)
-    pub fn segwit_v0_encode_signing_data_to<Write: io::Write + ?Sized>(
+    pub fn segwit_v0_encode_signing_data_to<Write: io::Write>(
         &mut self,
-        writer: &mut Write,
+        mut writer: Write,
         input_index: usize,
         script_code: &Script,
         value: Amount,
@@ -872,21 +807,21 @@ pub fn segwit_v0_encode_signing_data_to<Write: io::Write + ?Sized>(
 
         let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
 
-        self.tx.borrow().version.consensus_encode(writer)?;
+        self.tx.borrow().version.consensus_encode(&mut writer)?;
 
         if !anyone_can_pay {
-            self.segwit_cache().prevouts.consensus_encode(writer)?;
+            self.segwit_cache().prevouts.consensus_encode(&mut writer)?;
         } else {
-            zero_hash.consensus_encode(writer)?;
+            zero_hash.consensus_encode(&mut writer)?;
         }
 
         if !anyone_can_pay
             && sighash != EcdsaSighashType::Single
             && sighash != EcdsaSighashType::None
         {
-            self.segwit_cache().sequences.consensus_encode(writer)?;
+            self.segwit_cache().sequences.consensus_encode(&mut writer)?;
         } else {
-            zero_hash.consensus_encode(writer)?;
+            zero_hash.consensus_encode(&mut writer)?;
         }
 
         {
@@ -896,14 +831,14 @@ pub fn segwit_v0_encode_signing_data_to<Write: io::Write + ?Sized>(
                     inputs_size: self.tx.borrow().input.len(),
                 })?;
 
-            txin.previous_output.consensus_encode(writer)?;
-            script_code.consensus_encode(writer)?;
-            value.consensus_encode(writer)?;
-            txin.sequence.consensus_encode(writer)?;
+            txin.previous_output.consensus_encode(&mut writer)?;
+            script_code.consensus_encode(&mut writer)?;
+            value.consensus_encode(&mut writer)?;
+            txin.sequence.consensus_encode(&mut writer)?;
         }
 
         if sighash != EcdsaSighashType::Single && sighash != EcdsaSighashType::None {
-            self.segwit_cache().outputs.consensus_encode(writer)?;
+            self.segwit_cache().outputs.consensus_encode(&mut writer)?;
         } else if sighash == EcdsaSighashType::Single && input_index < self.tx.borrow().output.len()
         {
             let mut single_enc = LegacySighash::engine();
@@ -914,11 +849,31 @@ pub fn segwit_v0_encode_signing_data_to<Write: io::Write + ?Sized>(
             writer.write_all(&zero_hash[..])?;
         }
 
-        self.tx.borrow().lock_time.consensus_encode(writer)?;
-        sighash_type.to_u32().consensus_encode(writer)?;
+        self.tx.borrow().lock_time.consensus_encode(&mut writer)?;
+        sighash_type.to_u32().consensus_encode(&mut writer)?;
         Ok(())
     }
 
+    /// Computes the BIP143 sighash for any flag type.
+    #[deprecated(since = "0.31.0", note = "use (p2wpkh|p2wsh)_signature_hash instead")]
+    pub fn segwit_signature_hash(
+        &mut self,
+        input_index: usize,
+        script_code: &Script,
+        value: Amount,
+        sighash_type: EcdsaSighashType,
+    ) -> Result<SegwitV0Sighash, Error> {
+        let mut enc = SegwitV0Sighash::engine();
+        self.segwit_v0_encode_signing_data_to(
+            &mut enc,
+            input_index,
+            script_code,
+            value,
+            sighash_type,
+        )?;
+        Ok(SegwitV0Sighash::from_engine(enc))
+    }
+
     /// Computes the BIP143 sighash to spend a p2wpkh transaction for any flag type.
     ///
     /// `script_pubkey` is the `scriptPubkey` (native segwit) of the spend transaction
@@ -984,9 +939,9 @@ pub fn p2wsh_signature_hash(
     ///
     /// This function can't handle the SIGHASH_SINGLE bug internally, so it returns [`EncodeSigningDataResult`]
     /// that must be handled by the caller (see [`EncodeSigningDataResult::is_sighash_single_bug`]).
-    pub fn legacy_encode_signing_data_to<Write: io::Write + ?Sized, U: Into<u32>>(
+    pub fn legacy_encode_signing_data_to<Write: io::Write, U: Into<u32>>(
         &self,
-        writer: &mut Write,
+        writer: Write,
         input_index: usize,
         script_pubkey: &Script,
         sighash_type: U,
@@ -1011,9 +966,9 @@ pub fn legacy_encode_signing_data_to<Write: io::Write + ?Sized, U: Into<u32>>(
             return EncodeSigningDataResult::SighashSingleBug;
         }
 
-        fn encode_signing_data_to_inner<Write: io::Write + ?Sized>(
+        fn encode_signing_data_to_inner<Write: io::Write>(
             self_: &Transaction,
-            writer: &mut Write,
+            mut writer: Write,
             input_index: usize,
             script_pubkey: &Script,
             sighash_type: u32,
@@ -1074,8 +1029,8 @@ fn encode_signing_data_to_inner<Write: io::Write + ?Sized>(
                 _ => unreachable!(),
             };
             // hash the result
-            tx.consensus_encode(writer)?;
-            sighash_type.to_le_bytes().consensus_encode(writer)?;
+            tx.consensus_encode(&mut writer)?;
+            sighash_type.to_le_bytes().consensus_encode(&mut writer)?;
             Ok(())
         }
 
@@ -1201,7 +1156,7 @@ impl<R: BorrowMut<Transaction>> SighashCache<R> {
     /// let mut sig_hasher = SighashCache::new(&mut tx_to_sign);
     /// for inp in 0..input_count {
     ///     let prevout_script = Script::new();
-    ///     let _sighash = sig_hasher.p2wpkh_signature_hash(inp, prevout_script, Amount::ONE_SAT, EcdsaSighashType::All);
+    ///     let _sighash = sig_hasher.segwit_signature_hash(inp, prevout_script, Amount::ONE_SAT, EcdsaSighashType::All);
     ///     // ... sign the sighash
     ///     sig_hasher.witness_mut(inp).unwrap().push(&Vec::new());
     /// }
@@ -1529,23 +1484,23 @@ fn test_sighash_errors() {
         let empty_prevouts : Prevouts<TxOut> = Prevouts::All(&empty_vec);
         assert_eq!(
             c.taproot_signature_hash(0, &empty_prevouts, None, None, TapSighashType::All),
-            Err(Error::PrevoutsSize(PrevoutsSizeError))
+            Err(Error::PrevoutsSize)
         );
         let two = vec![TxOut::NULL, TxOut::NULL];
         let too_many_prevouts = Prevouts::All(&two);
         assert_eq!(
             c.taproot_signature_hash(0, &too_many_prevouts, None, None, TapSighashType::All),
-            Err(Error::PrevoutsSize(PrevoutsSizeError))
+            Err(Error::PrevoutsSize)
         );
         let tx_out = TxOut::NULL;
         let prevout = Prevouts::One(1, &tx_out);
         assert_eq!(
             c.taproot_signature_hash(0, &prevout, None, None, TapSighashType::All),
-            Err(Error::PrevoutsKind(PrevoutsKindError))
+            Err(Error::PrevoutKind)
         );
         assert_eq!(
             c.taproot_signature_hash(0, &prevout, None, None, TapSighashType::AllPlusAnyoneCanPay),
-            Err(Error::PrevoutsIndex(PrevoutsIndexError::InvalidOneIndex))
+            Err(Error::PrevoutIndex)
         );
         assert_eq!(
             c.taproot_signature_hash(10, &prevout, None, None, TapSighashType::AllPlusAnyoneCanPay),
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index d7cc880a..f1c18b2f 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -8,6 +8,7 @@
 use core::fmt;
 
 use internals::write_err;
+pub use secp256k1::{self, constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey};
 
 use crate::prelude::*;
 use crate::sighash::{InvalidSighashTypeError, TapSighashType};
diff --git a/bitcoin/src/error.rs b/bitcoin/src/error.rs
index 6adcbbb0..f5ea45c3 100644
--- a/bitcoin/src/error.rs
+++ b/bitcoin/src/error.rs
@@ -2,6 +2,4 @@
 
 //! Contains error types and other error handling tools.
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
 pub use crate::parse::ParseIntError;
diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs
index 3ac56a74..ccbc4faa 100644
--- a/bitcoin/src/hash_types.rs
+++ b/bitcoin/src/hash_types.rs
@@ -47,10 +47,7 @@ fn from(hash: $hashtype) -> Self {
     };
 }
 pub(crate) use impl_asref_push_bytes;
-
 // newtypes module is solely here so we can rustfmt::skip.
-#[rustfmt::skip]
-#[doc(inline)]
 pub use newtypes::*;
 
 #[rustfmt::skip]
diff --git a/bitcoin/src/internal_macros.rs b/bitcoin/src/internal_macros.rs
index db46f944..cf95940f 100644
--- a/bitcoin/src/internal_macros.rs
+++ b/bitcoin/src/internal_macros.rs
@@ -34,9 +34,10 @@ fn consensus_decode_from_finite_reader<R: $crate::io::Read + ?Sized>(
             fn consensus_decode<R: $crate::io::Read + ?Sized>(
                 r: &mut R,
             ) -> Result<$thing, $crate::consensus::encode::Error> {
+                use crate::io::Read as _;
                 let mut r = r.take($crate::consensus::encode::MAX_VEC_SIZE as u64);
                 Ok($thing {
-                    $($field: $crate::consensus::Decodable::consensus_decode(&mut r)?),+
+                    $($field: $crate::consensus::Decodable::consensus_decode(r.by_ref())?),+
                 })
             }
         }
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 6fd04af2..68b7d246 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -23,7 +23,7 @@
 //!                 deserialization.
 //! * `secp-lowmemory` - optimizations for low-memory devices.
 //! * `no-std` - enables additional features required for this crate to be usable
-//!              without std. Does **not** disable `std`.
+//!              without std. Does **not** disable `std`. Depends on `core2`.
 //! * `bitcoinconsensus-std` - enables `std` in `bitcoinconsensus` and communicates it
 //!                            to this crate so it knows how to implement
 //!                            `std::error::Error`. At this time there's a hack to
@@ -68,6 +68,9 @@
 /// Bitcoin's libbitcoinconsensus with Rust binding.
 pub extern crate bitcoinconsensus;
 
+#[cfg(not(feature = "std"))]
+extern crate core2;
+
 /// Rust implementation of cryptographic hash function algorithems.
 pub extern crate hashes;
 
@@ -113,42 +116,53 @@
 pub mod string;
 pub mod taproot;
 
-use bitcoin_io::io;
-
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use crate::{
-    address::{Address, AddressType},
-    amount::{Amount, Denomination, SignedAmount},
-    bip32::XKeyIdentifier,
-    blockdata::block::{self, Block},
-    blockdata::constants,
-    blockdata::fee_rate::FeeRate,
-    blockdata::locktime::{self, absolute, relative},
-    blockdata::opcodes::{self, Opcode},
-    blockdata::script::witness_program::{self, WitnessProgram},
-    blockdata::script::witness_version::{self, WitnessVersion},
-    blockdata::script::{self, Script, ScriptBuf, ScriptHash, WScriptHash},
-    blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut},
-    blockdata::weight::Weight,
-    blockdata::witness::{self, Witness},
-    consensus::encode::VarInt,
-    crypto::ecdsa,
-    crypto::key::{self, PrivateKey, PubkeyHash, PublicKey, WPubkeyHash, XOnlyPublicKey},
-    crypto::sighash::{self, LegacySighash, SegwitV0Sighash, TapSighash, TapSighashTag},
-    hash_types::{BlockHash, FilterHash, FilterHeader, TxMerkleNode, Txid, WitnessCommitment, Wtxid},
-    merkle_tree::MerkleBlock,
-    network::Network,
-    pow::{CompactTarget, Target, Work},
-    psbt::Psbt,
-    sighash::{EcdsaSighashType, TapSighashType},
-    taproot::{TapBranchTag, TapLeafHash, TapLeafTag, TapNodeHash, TapTweakHash, TapTweakTag},
+// May depend on crate features and we don't want to bother with it
+#[allow(unused)]
+#[cfg(feature = "std")]
+use std::error::Error as StdError;
+#[cfg(feature = "std")]
+use std::io;
+
+#[allow(unused)]
+#[cfg(not(feature = "std"))]
+use core2::error::Error as StdError;
+#[cfg(not(feature = "std"))]
+use core2::io;
+
+pub use crate::address::{Address, AddressType};
+pub use crate::amount::{Amount, Denomination, SignedAmount};
+pub use crate::bip32::XKeyIdentifier;
+pub use crate::blockdata::block::{self, Block};
+pub use crate::blockdata::constants;
+pub use crate::blockdata::fee_rate::FeeRate;
+pub use crate::blockdata::locktime::{self, absolute, relative};
+pub use crate::blockdata::opcodes::{self, Opcode};
+pub use crate::blockdata::script::witness_program::{self, WitnessProgram};
+pub use crate::blockdata::script::witness_version::{self, WitnessVersion};
+pub use crate::blockdata::script::{self, Script, ScriptBuf, ScriptHash, WScriptHash};
+pub use crate::blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut};
+pub use crate::blockdata::weight::Weight;
+pub use crate::blockdata::witness::{self, Witness};
+pub use crate::consensus::encode::VarInt;
+pub use crate::crypto::ecdsa;
+pub use crate::crypto::key::{
+    self, PrivateKey, PubkeyHash, PublicKey, WPubkeyHash, XOnlyPublicKey,
+};
+pub use crate::crypto::sighash::{self, LegacySighash, SegwitV0Sighash, TapSighash, TapSighashTag};
+pub use crate::hash_types::{
+    BlockHash, FilterHash, FilterHeader, TxMerkleNode, Txid, WitnessCommitment, Wtxid,
+};
+pub use crate::merkle_tree::MerkleBlock;
+pub use crate::network::Network;
+pub use crate::pow::{CompactTarget, Target, Work};
+pub use crate::psbt::Psbt;
+pub use crate::sighash::{EcdsaSighashType, TapSighashType};
+pub use crate::taproot::{
+    TapBranchTag, TapLeafHash, TapLeafTag, TapNodeHash, TapTweakHash, TapTweakTag,
 };
 
 #[cfg(not(feature = "std"))]
 mod io_extras {
-    use crate::io;
-
     /// A writer which will move data into the void.
     pub struct Sink {
         _priv: (),
@@ -157,12 +171,12 @@ pub struct Sink {
     /// Creates an instance of a writer which will successfully consume all data.
     pub const fn sink() -> Sink { Sink { _priv: () } }
 
-    impl io::Write for Sink {
+    impl core2::io::Write for Sink {
         #[inline]
-        fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) }
+        fn write(&mut self, buf: &[u8]) -> core2::io::Result<usize> { Ok(buf.len()) }
 
         #[inline]
-        fn flush(&mut self) -> io::Result<()> { Ok(()) }
+        fn flush(&mut self) -> core2::io::Result<()> { Ok(()) }
     }
 }
 
@@ -184,10 +198,34 @@ mod prelude {
     pub use std::collections::{BTreeMap, BTreeSet, btree_map, BinaryHeap};
 
     #[cfg(feature = "std")]
-    pub use crate::io::sink;
+    pub use std::io::sink;
 
     #[cfg(not(feature = "std"))]
     pub use crate::io_extras::sink;
 
     pub use hex::DisplayHex;
 }
+
+#[cfg(bench)]
+use bench::EmptyWrite;
+
+#[cfg(bench)]
+mod bench {
+    use core::fmt::Arguments;
+
+    use crate::io::{IoSlice, Result, Write};
+
+    #[derive(Default, Clone, Debug, PartialEq, Eq)]
+    pub struct EmptyWrite;
+
+    impl Write for EmptyWrite {
+        fn write(&mut self, buf: &[u8]) -> Result<usize> { Ok(buf.len()) }
+        fn write_vectored(&mut self, bufs: &[IoSlice]) -> Result<usize> {
+            Ok(bufs.iter().map(|s| s.len()).sum())
+        }
+        fn flush(&mut self) -> Result<()> { Ok(()) }
+
+        fn write_all(&mut self, _: &[u8]) -> Result<()> { Ok(()) }
+        fn write_fmt(&mut self, _: Arguments) -> Result<()> { Ok(()) }
+    }
+}
diff --git a/bitcoin/src/merkle_tree/mod.rs b/bitcoin/src/merkle_tree/mod.rs
index 7d75641f..c120d8fa 100644
--- a/bitcoin/src/merkle_tree/mod.rs
+++ b/bitcoin/src/merkle_tree/mod.rs
@@ -18,16 +18,13 @@
 use core::cmp::min;
 use core::iter;
 
+pub use block::{MerkleBlock, MerkleBlockError, PartialMerkleTree};
 use hashes::Hash;
 
 use crate::consensus::encode::Encodable;
 use crate::io;
 use crate::prelude::*;
 
-#[rustfmt::skip]
-#[doc(inline)]
-pub use self::block::{MerkleBlock, MerkleBlockError, PartialMerkleTree};
-
 /// Calculates the merkle root of a list of *hashes*, inline (in place) in `hashes`.
 ///
 /// In most cases, you'll want to use [`calculate_root`] instead. Please note, calling this function
diff --git a/bitcoin/src/network.rs b/bitcoin/src/network.rs
index c66ab070..cd3d3042 100644
--- a/bitcoin/src/network.rs
+++ b/bitcoin/src/network.rs
@@ -27,7 +27,6 @@
 #[cfg(feature = "serde")]
 use serde::{Deserialize, Serialize};
 
-use crate::consensus::Params;
 use crate::constants::ChainHash;
 use crate::p2p::Magic;
 use crate::prelude::{String, ToOwned};
@@ -145,17 +144,6 @@ pub fn chain_hash(self) -> ChainHash { ChainHash::using_genesis_block(self) }
     pub fn from_chain_hash(chain_hash: ChainHash) -> Option<Network> {
         Network::try_from(chain_hash).ok()
     }
-
-    /// Returns the associated network parameters.
-    pub const fn params(self) -> &'static Params {
-        const PARAMS: [Params; 4] = [
-            Params::new(Network::Bitcoin),
-            Params::new(Network::Testnet),
-            Params::new(Network::Signet),
-            Params::new(Network::Regtest),
-        ];
-        &PARAMS[self as usize]
-    }
 }
 
 #[cfg(feature = "serde")]
diff --git a/bitcoin/src/p2p/address.rs b/bitcoin/src/p2p/address.rs
index 5e470551..2ae9bba1 100644
--- a/bitcoin/src/p2p/address.rs
+++ b/bitcoin/src/p2p/address.rs
@@ -116,7 +116,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 
 impl ToSocketAddrs for Address {
     type Iter = iter::Once<SocketAddr>;
-    fn to_socket_addrs(&self) -> Result<Self::Iter, std::io::Error> {
+    fn to_socket_addrs(&self) -> Result<Self::Iter, io::Error> {
         Ok(iter::once(self.socket_addr()?))
     }
 }
@@ -296,7 +296,7 @@ fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Err
 
 impl ToSocketAddrs for AddrV2Message {
     type Iter = iter::Once<SocketAddr>;
-    fn to_socket_addrs(&self) -> Result<Self::Iter, std::io::Error> {
+    fn to_socket_addrs(&self) -> Result<Self::Iter, io::Error> {
         Ok(iter::once(self.socket_addr()?))
     }
 }
diff --git a/bitcoin/src/p2p/message.rs b/bitcoin/src/p2p/message.rs
index 448e8a1c..0b92e089 100644
--- a/bitcoin/src/p2p/message.rs
+++ b/bitcoin/src/p2p/message.rs
@@ -10,6 +10,7 @@
 use core::{fmt, iter};
 
 use hashes::{sha256d, Hash};
+use io::Read as _;
 
 use crate::blockdata::{block, transaction};
 use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};
@@ -428,7 +429,7 @@ fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
 
     #[inline]
     fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
-        Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE as u64))
+        Self::consensus_decode_from_finite_reader(r.take(MAX_MSG_SIZE as u64).by_ref())
     }
 }
 
@@ -533,7 +534,7 @@ fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(
 
     #[inline]
     fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
-        Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE as u64))
+        Self::consensus_decode_from_finite_reader(r.take(MAX_MSG_SIZE as u64).by_ref())
     }
 }
 
diff --git a/bitcoin/src/p2p/message_bloom.rs b/bitcoin/src/p2p/message_bloom.rs
index 812ce2f5..e3dcbe18 100644
--- a/bitcoin/src/p2p/message_bloom.rs
+++ b/bitcoin/src/p2p/message_bloom.rs
@@ -5,9 +5,10 @@
 //! This module describes BIP37 Connection Bloom filtering network messages.
 //!
 
+use std::io;
+
 use crate::consensus::{encode, Decodable, Encodable, ReadExt};
 use crate::internal_macros::impl_consensus_encoding;
-use crate::io;
 
 /// `filterload` message sets the current bloom filter
 #[derive(Clone, PartialEq, Eq, Debug)]
diff --git a/bitcoin/src/p2p/mod.rs b/bitcoin/src/p2p/mod.rs
index 3132c025..c7f67570 100644
--- a/bitcoin/src/p2p/mod.rs
+++ b/bitcoin/src/p2p/mod.rs
@@ -8,6 +8,8 @@
 #[cfg(feature = "std")]
 pub mod address;
 #[cfg(feature = "std")]
+pub use self::address::Address;
+#[cfg(feature = "std")]
 pub mod message;
 #[cfg(feature = "std")]
 pub mod message_blockdata;
@@ -31,11 +33,6 @@
 use crate::prelude::{Borrow, BorrowMut, String, ToOwned};
 use crate::{io, Network};
 
-#[rustfmt::skip]
-#[doc(inline)]
-#[cfg(feature = "std")]
-pub use self::address::Address;
-
 /// Version of the protocol as appearing in network message headers.
 ///
 /// This constant is used to signal to other peers which features you support. Increasing it implies
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index cc25654a..f81b9700 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -19,7 +19,6 @@
 use crate::io::{self, Read, Write};
 use crate::prelude::String;
 use crate::string::FromHexStr;
-use crate::Network;
 
 /// Implement traits and methods shared by `Target` and `Work`.
 macro_rules! do_impl {
@@ -138,6 +137,12 @@ impl Target {
     // https://github.com/bitcoin/bitcoin/blob/8105bce5b384c72cf08b25b7c5343622754e7337/src/kernel/chainparams.cpp#L348
     pub const MAX_ATTAINABLE_SIGNET: Self = Target(U256(0x0377_ae00 << 80, 0));
 
+    /// The maximum possible target (see [`Target::MAX`]).
+    ///
+    /// This is provided for consistency with Rust 1.41.1, newer code should use [`Target::MAX`].
+    #[deprecated(since = "0.31.0", note = "Use Self::MAX instead")]
+    pub const fn max_value() -> Self { Target::MAX }
+
     /// Computes the [`Target`] value from a compact representation.
     ///
     /// ref: <https://developer.bitcoin.org/reference/block_chain.html#target-nbits>
@@ -224,14 +229,8 @@ pub fn to_work(self) -> Work { Work(self.0.inverse()) }
     /// [max]: Target::max
     /// [target]: crate::blockdata::block::Header::target
     #[cfg_attr(all(test, mutate), mutate)]
-    pub fn difficulty(&self, network: Network) -> u128 {
-        let max = match network {
-            Network::Bitcoin => Target::MAX_ATTAINABLE_MAINNET,
-            Network::Testnet => Target::MAX_ATTAINABLE_TESTNET,
-            Network::Signet => Target::MAX_ATTAINABLE_SIGNET,
-            Network::Regtest => Target::MAX_ATTAINABLE_REGTEST,
-        };
-        let d = max.0 / self.0;
+    pub fn difficulty(&self) -> u128 {
+        let d = Target::MAX.0 / self.0;
         d.saturating_to_u128()
     }
 
diff --git a/bitcoin/src/psbt/map/mod.rs b/bitcoin/src/psbt/map/mod.rs
index f416e5db..91d455e0 100644
--- a/bitcoin/src/psbt/map/mod.rs
+++ b/bitcoin/src/psbt/map/mod.rs
@@ -1,19 +1,15 @@
 // SPDX-License-Identifier: CC0-1.0
 
+use crate::prelude::*;
+use crate::psbt::raw;
+
 mod global;
 mod input;
 mod output;
 
-use crate::prelude::*;
-use crate::psbt::raw;
-use crate::psbt::serialize::Serialize;
-
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
-    input::{Input, PsbtSighashType},
-    output::Output,
-};
+pub use self::input::{Input, PsbtSighashType};
+pub use self::output::Output;
+use super::serialize::Serialize;
 
 /// A trait that describes a PSBT key-value map.
 pub(super) trait Map {
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index 925ea413..a44daa6f 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -7,13 +7,6 @@
 //! except we define PSBTs containing non-standard sighash types as invalid.
 //!
 
-#[macro_use]
-mod macros;
-mod error;
-mod map;
-pub mod raw;
-pub mod serialize;
-
 use core::{cmp, fmt};
 #[cfg(feature = "std")]
 use std::collections::{HashMap, HashSet};
@@ -30,12 +23,16 @@
 use crate::sighash::{self, EcdsaSighashType, SighashCache};
 use crate::{Amount, FeeRate};
 
-#[rustfmt::skip]                // Keep public re-exports separate.
-#[doc(inline)]
-pub use self::{
-    map::{Input, Output, PsbtSighashType},
-    error::Error,
-};
+#[macro_use]
+mod macros;
+pub mod raw;
+pub mod serialize;
+
+mod error;
+pub use self::error::Error;
+
+mod map;
+pub use self::map::{Input, Output, PsbtSighashType};
 
 /// A Partially Signed Transaction.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/bitcoin/src/psbt/raw.rs b/bitcoin/src/psbt/raw.rs
index 85275835..effd805b 100644
--- a/bitcoin/src/psbt/raw.rs
+++ b/bitcoin/src/psbt/raw.rs
@@ -196,7 +196,7 @@ fn try_from(key: Key) -> Result<Self, Self::Error> {
 }
 
 // core2 doesn't have read_to_end
-pub(crate) fn read_to_end<D: io::Read + ?Sized>(d: &mut D) -> Result<Vec<u8>, io::Error> {
+pub(crate) fn read_to_end<D: io::Read>(mut d: D) -> Result<Vec<u8>, io::Error> {
     let mut result = vec![];
     let mut buf = [0u8; 64];
     loop {
diff --git a/bitcoin/src/sign_message.rs b/bitcoin/src/sign_message.rs
index d846946a..17a00ec2 100644
--- a/bitcoin/src/sign_message.rs
+++ b/bitcoin/src/sign_message.rs
@@ -8,12 +8,9 @@
 
 use hashes::{sha256d, Hash, HashEngine};
 
-use crate::consensus::{encode, Encodable};
-
-#[rustfmt::skip]
-#[doc(inline)]
 #[cfg(feature = "secp-recovery")]
 pub use self::message_signing::{MessageSignature, MessageSignatureError};
+use crate::consensus::{encode, Encodable};
 
 /// The prefix for signed messages using Bitcoin's message signing protocol.
 pub const BITCOIN_SIGNED_MSG_PREFIX: &[u8] = b"\x18Bitcoin Signed Message:\n";
diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs
index 8e4dab06..9c5ccc9f 100644
--- a/bitcoin/src/taproot.rs
+++ b/bitcoin/src/taproot.rs
@@ -16,13 +16,10 @@
 
 use crate::consensus::Encodable;
 use crate::crypto::key::{TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey};
-use crate::prelude::*;
-use crate::{io, Script, ScriptBuf};
-
 // Re-export these so downstream only has to use one `taproot` module.
-#[rustfmt::skip]
-#[doc(inline)]
 pub use crate::crypto::taproot::{SigFromSliceError, Signature};
+use crate::prelude::*;
+use crate::{io, Script, ScriptBuf};
 
 // Taproot test vectors from BIP-341 state the hashes without any reversing
 sha256t_hash_newtype! {
@@ -294,6 +291,10 @@ pub fn from_node_info<C: secp256k1::Verification>(
         info
     }
 
+    /// Returns the internal script map.
+    #[deprecated(since = "0.31.0", note = "use Self::script_map instead")]
+    pub fn as_script_map(&self) -> &ScriptMerkleProofMap { self.script_map() }
+
     /// Constructs a [`ControlBlock`] for particular script with the given version.
     ///
     /// # Returns
@@ -1112,7 +1113,7 @@ fn from_collection<T: AsRef<[TapNodeHash]> + Into<Vec<TapNodeHash>>>(
     /// # Returns
     ///
     /// The number of bytes written to the writer.
-    pub fn encode<Write: io::Write + ?Sized>(&self, writer: &mut Write) -> io::Result<usize> {
+    pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
         for hash in self.0.iter() {
             writer.write_all(hash.as_ref())?;
         }
@@ -1240,12 +1241,12 @@ pub fn size(&self) -> usize {
     /// # Returns
     ///
     /// The number of bytes written to the writer.
-    pub fn encode<Write: io::Write + ?Sized>(&self, writer: &mut Write) -> io::Result<usize> {
+    pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
         let first_byte: u8 =
             i32::from(self.output_key_parity) as u8 | self.leaf_version.to_consensus();
         writer.write_all(&[first_byte])?;
         writer.write_all(&self.internal_key.serialize())?;
-        self.merkle_branch.encode(writer)?;
+        self.merkle_branch.encode(&mut writer)?;
         Ok(self.size())
     }
 
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index c40270cc..55f29cac 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -10,7 +10,7 @@ cargo-fuzz = true
 
 [dependencies]
 honggfuzz = { version = "0.5.55", default-features = false }
-bitcoin = { version = "0.31.0", features = [ "serde" ] }
+bitcoin = { version = "0.31.0-rc2", features = [ "serde" ] }
 
 serde = { version = "1.0.103", features = [ "derive" ] }
 serde_json = "1.0"
diff --git a/fuzz/generate-files.sh b/fuzz/generate-files.sh
index 25af8989..89746d75 100755
--- a/fuzz/generate-files.sh
+++ b/fuzz/generate-files.sh
@@ -21,7 +21,7 @@ cargo-fuzz = true
 
 [dependencies]
 honggfuzz = { version = "0.5.55", default-features = false }
-bitcoin = { version = "0.31.0", features = [ "serde" ] }
+bitcoin = { version = "0.31.0-rc2", features = [ "serde" ] }
 
 serde = { version = "1.0.103", features = [ "derive" ] }
 serde_json = "1.0"
diff --git a/hashes/Cargo.toml b/hashes/Cargo.toml
index 7c298aa3..6f91a020 100644
--- a/hashes/Cargo.toml
+++ b/hashes/Cargo.toml
@@ -14,11 +14,11 @@ exclude = ["tests", "contrib"]
 
 [features]
 default = ["std"]
-std = ["alloc", "hex/std", "bitcoin-io/std"]
+std = ["alloc", "hex/std"]
 alloc = ["hex/alloc"]
 serde-std = ["serde/std"]
-# If you want I/O you must enable either "std" or "io".
-io = ["bitcoin-io"]
+# If you want I/O you must enable either "std" or "core2".
+core2 = ["actual-core2", "hex/core2"]
 # Smaller (but slower) implementation of sha256, sha512 and ripemd160
 small-hash = []
 
@@ -29,12 +29,13 @@ rustdoc-args = ["--cfg", "docsrs"]
 [dependencies]
 hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
 
-bitcoin-io = { version = "0.1", default-features = false, optional = true }
-
 schemars = { version = "0.8.3", optional = true }
 # Only enable this if you explicitly do not want to use "std", otherwise enable "serde-std".
 serde = { version = "1.0", default-features = false, optional = true }
 
+# Do NOT use this feature! Use the "core2" feature instead.
+actual-core2 = { package = "core2", version = "0.3.2", default-features = false, optional = true }
+
 [dev-dependencies]
 serde_test = "1.0"
 serde_json = "1.0"
diff --git a/hashes/embedded/Cargo.toml b/hashes/embedded/Cargo.toml
index 5d9644c4..e44e0344 100644
--- a/hashes/embedded/Cargo.toml
+++ b/hashes/embedded/Cargo.toml
@@ -18,8 +18,8 @@ cortex-m-rt = "0.6.10"
 cortex-m-semihosting = "0.3.3"
 panic-halt = "0.2.0"
 alloc-cortex-m = { version = "0.4.1", optional = true }
-bitcoin_hashes = { path="../", default-features = false, features = ["io"] }
-bitcoin-io = { path = "../../io", default_features = false }
+bitcoin_hashes = { path="../", default-features = false, features = ["core2"] }
+core2 = { version = "0.3.0", default_features = false }
 
 [[bin]]
 name = "embedded"
@@ -33,6 +33,3 @@ lto = true # better optimizations
 
 [patch.crates-io.bitcoin-internals]
 path = "../../internals"
-
-[patch.crates-io.bitcoin-io]
-path = "../../io"
diff --git a/hashes/embedded/src/main.rs b/hashes/embedded/src/main.rs
index bba22ff8..a43e7106 100644
--- a/hashes/embedded/src/main.rs
+++ b/hashes/embedded/src/main.rs
@@ -10,7 +10,7 @@
 #[cfg(feature = "alloc")] use alloc::string::ToString;
 
 use bitcoin_hashes::{sha256, Hash, HashEngine};
-use bitcoin_io::io::Write;
+use core2::io::Write;
 use core::str::FromStr;
 use cortex_m_rt::entry;
 use cortex_m_semihosting::{debug, hprintln};
diff --git a/hashes/src/impls.rs b/hashes/src/impls.rs
index 0f7cc5a0..088a7f1f 100644
--- a/hashes/src/impls.rs
+++ b/hashes/src/impls.rs
@@ -1,73 +1,69 @@
 // SPDX-License-Identifier: CC0-1.0
 
-//! `std` / `io` Impls.
+//! `std` / `core2` Impls.
 //!
-//! Implementations of traits defined in `std` / `io` and not in `core`.
+//! Implementations of traits defined in `std` / `core2` and not in `core`.
 //!
 
-use bitcoin_io::impl_write;
+use crate::{hmac, io, ripemd160, sha1, sha256, sha512, siphash24, HashEngine};
 
-use crate::{hmac, ripemd160, sha1, sha256, sha512, siphash24, HashEngine};
+impl io::Write for sha1::HashEngine {
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
 
-impl_write!(
-    sha1::HashEngine,
-    |us: &mut sha1::HashEngine, buf| {
-        us.input(buf);
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.input(buf);
         Ok(buf.len())
-    },
-    |_us| { Ok(()) }
-);
-
-impl_write!(
-    sha256::HashEngine,
-    |us: &mut sha256::HashEngine, buf| {
-        us.input(buf);
+    }
+}
+
+impl io::Write for sha256::HashEngine {
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.input(buf);
         Ok(buf.len())
-    },
-    |_us| { Ok(()) }
-);
-
-impl_write!(
-    sha512::HashEngine,
-    |us: &mut sha512::HashEngine, buf| {
-        us.input(buf);
+    }
+}
+
+impl io::Write for sha512::HashEngine {
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.input(buf);
         Ok(buf.len())
-    },
-    |_us| { Ok(()) }
-);
-
-impl_write!(
-    ripemd160::HashEngine,
-    |us: &mut ripemd160::HashEngine, buf| {
-        us.input(buf);
+    }
+}
+
+impl io::Write for ripemd160::HashEngine {
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.input(buf);
         Ok(buf.len())
-    },
-    |_us| { Ok(()) }
-);
-
-impl_write!(
-    siphash24::HashEngine,
-    |us: &mut siphash24::HashEngine, buf| {
-        us.input(buf);
+    }
+}
+
+impl io::Write for siphash24::HashEngine {
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.input(buf);
         Ok(buf.len())
-    },
-    |_us| { Ok(()) }
-);
-
-impl_write!(
-    hmac::HmacEngine<T>,
-    |us: &mut hmac::HmacEngine<T>, buf| {
-        us.input(buf);
+    }
+}
+
+impl<T: crate::Hash> io::Write for hmac::HmacEngine<T> {
+    fn flush(&mut self) -> io::Result<()> { Ok(()) }
+
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        self.input(buf);
         Ok(buf.len())
-    },
-    |_us| { Ok(()) },
-    T: crate::Hash
-);
+    }
+}
 
 #[cfg(test)]
 mod tests {
-    use bitcoin_io::io::Write;
-
+    use super::io::Write;
     use crate::{hash160, hmac, ripemd160, sha1, sha256, sha256d, sha512, siphash24, Hash};
 
     macro_rules! write_test {
diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs
index 10c47d75..aa1c6ffc 100644
--- a/hashes/src/lib.rs
+++ b/hashes/src/lib.rs
@@ -81,6 +81,8 @@
 // Exclude clippy lints we don't think are valuable
 #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
 
+#[cfg(all(not(test), not(feature = "std"), feature = "core2"))]
+extern crate actual_core2 as core2;
 #[cfg(all(feature = "alloc", not(feature = "std")))]
 extern crate alloc;
 #[cfg(any(test, feature = "std"))]
@@ -117,7 +119,7 @@ pub mod _core {
 pub mod cmp;
 pub mod hash160;
 pub mod hmac;
-#[cfg(feature = "bitcoin-io")]
+#[cfg(any(test, feature = "std", feature = "core2"))]
 mod impls;
 pub mod ripemd160;
 pub mod sha1;
@@ -129,7 +131,12 @@ pub mod _core {
 pub mod siphash24;
 
 use core::{borrow, fmt, hash, ops};
+// You get I/O if you enable "std" or "core2" (as well as during testing).
+#[cfg(any(test, feature = "std"))]
+use std::io;
 
+#[cfg(all(not(test), not(feature = "std"), feature = "core2"))]
+use core2::io;
 pub use hmac::{Hmac, HmacEngine};
 
 /// A hashing engine which bytes can be serialized into.
diff --git a/hashes/src/sha512.rs b/hashes/src/sha512.rs
index f7fff543..85a8aa4c 100644
--- a/hashes/src/sha512.rs
+++ b/hashes/src/sha512.rs
@@ -10,40 +10,7 @@
 
 use crate::{FromSliceError, HashEngine as _};
 
-crate::internal_macros::hash_type! {
-    512,
-    false,
-    "Output of the SHA512 hash function.",
-    "crate::util::json_hex_string::len_64"
-}
-
-#[cfg(not(hashes_fuzz))]
-pub(crate) fn from_engine(mut e: HashEngine) -> Hash {
-    // pad buffer with a single 1-bit then all 0s, until there are exactly 16 bytes remaining
-    let data_len = e.length as u64;
-
-    let zeroes = [0; BLOCK_SIZE - 16];
-    e.input(&[0x80]);
-    if e.length % BLOCK_SIZE > zeroes.len() {
-        e.input(&zeroes);
-    }
-    let pad_length = zeroes.len() - (e.length % BLOCK_SIZE);
-    e.input(&zeroes[..pad_length]);
-    debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len());
-
-    e.input(&[0; 8]);
-    e.input(&(8 * data_len).to_be_bytes());
-    debug_assert_eq!(e.length % BLOCK_SIZE, 0);
-
-    Hash(e.midstate())
-}
-
-#[cfg(hashes_fuzz)]
-pub(crate) fn from_engine(e: HashEngine) -> Hash {
-    let mut hash = e.midstate();
-    hash[0] ^= 0xff; // Make this distinct from SHA-256
-    Hash(hash)
-}
+crate::internal_macros::hash_trait_impls!(512, false);
 
 pub(crate) const BLOCK_SIZE: usize = 128;
 
@@ -110,6 +77,52 @@ fn n_bytes_hashed(&self) -> usize { self.length }
     engine_input_impl!();
 }
 
+/// Output of the SHA512 hash function.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
+#[repr(transparent)]
+pub struct Hash(
+    #[cfg_attr(
+        feature = "schemars",
+        schemars(schema_with = "crate::util::json_hex_string::len_64")
+    )]
+    [u8; 64],
+);
+
+impl Hash {
+    fn internal_new(arr: [u8; 64]) -> Self { Hash(arr) }
+
+    fn internal_engine() -> HashEngine { Default::default() }
+}
+
+#[cfg(not(hashes_fuzz))]
+pub(crate) fn from_engine(mut e: HashEngine) -> Hash {
+    // pad buffer with a single 1-bit then all 0s, until there are exactly 16 bytes remaining
+    let data_len = e.length as u64;
+
+    let zeroes = [0; BLOCK_SIZE - 16];
+    e.input(&[0x80]);
+    if e.length % BLOCK_SIZE > zeroes.len() {
+        e.input(&zeroes);
+    }
+    let pad_length = zeroes.len() - (e.length % BLOCK_SIZE);
+    e.input(&zeroes[..pad_length]);
+    debug_assert_eq!(e.length % BLOCK_SIZE, zeroes.len());
+
+    e.input(&[0; 8]);
+    e.input(&(8 * data_len).to_be_bytes());
+    debug_assert_eq!(e.length % BLOCK_SIZE, 0);
+
+    Hash(e.midstate())
+}
+
+#[cfg(hashes_fuzz)]
+pub(crate) fn from_engine(e: HashEngine) -> Hash {
+    let mut hash = e.midstate();
+    hash[0] ^= 0xff; // Make this distinct from SHA-256
+    Hash(hash)
+}
+
 #[allow(non_snake_case)]
 fn Ch(x: u64, y: u64, z: u64) -> u64 { z ^ (x & (y ^ z)) }
 #[allow(non_snake_case)]
diff --git a/hashes/src/sha512_256.rs b/hashes/src/sha512_256.rs
index 61ce8fe3..27d90d68 100644
--- a/hashes/src/sha512_256.rs
+++ b/hashes/src/sha512_256.rs
@@ -13,19 +13,6 @@
 
 use crate::{sha512, FromSliceError};
 
-crate::internal_macros::hash_type! {
-    256,
-    false,
-    "Output of the SHA512/256 hash function.\n\nSHA512/256 is a hash function that uses the sha512 alogrithm but it truncates the output to 256 bits. It has different initial constants than sha512 so it produces an entirely different hash compared to sha512. More information at <https://eprint.iacr.org/2010/548.pdf>. ",
-    "crate::util::json_hex_string::len_32"
-}
-
-fn from_engine(e: HashEngine) -> Hash {
-    let mut ret = [0; 32];
-    ret.copy_from_slice(&sha512::from_engine(e.0)[..32]);
-    Hash(ret)
-}
-
 /// Engine to compute SHA512/256 hash function.
 ///
 /// SHA512/256 is a hash function that uses the sha512 alogrithm but it truncates
@@ -54,6 +41,19 @@ fn n_bytes_hashed(&self) -> usize { self.0.n_bytes_hashed() }
     fn input(&mut self, inp: &[u8]) { self.0.input(inp); }
 }
 
+crate::internal_macros::hash_type! {
+    256,
+    false,
+    "Output of the SHA512/256 hash function.\n\nSHA512/256 is a hash function that uses the sha512 alogrithm but it truncates the output to 256 bits. It has different initial constants than sha512 so it produces an entirely different hash compared to sha512. More information at <https://eprint.iacr.org/2010/548.pdf>. ",
+    "crate::util::json_hex_string::len_32"
+}
+
+fn from_engine(e: HashEngine) -> Hash {
+    let mut ret = [0; 32];
+    ret.copy_from_slice(&sha512::from_engine(e.0)[..32]);
+    Hash(ret)
+}
+
 #[cfg(test)]
 mod tests {
     #[test]
diff --git a/internals/build.rs b/internals/build.rs
index b5e2f5f6..ab1f0411 100644
--- a/internals/build.rs
+++ b/internals/build.rs
@@ -1,5 +1,3 @@
-const MSRV_MINOR: u64 = 48;
-
 fn main() {
     let rustc = std::env::var_os("RUSTC");
     let rustc = rustc.as_ref().map(std::path::Path::new).unwrap_or_else(|| "rustc".as_ref());
@@ -27,7 +25,11 @@ fn main() {
         .expect("invalid Rust minor version");
 
     // print cfg for all interesting versions less than or equal to minor
-    for version in MSRV_MINOR..=minor {
-        println!("cargo:rustc-cfg=rust_v_1_{}", version);
+    // 46 adds `track_caller`
+    // 55 adds `kind()` to `ParseIntError`
+    for version in &[46, 55] {
+        if *version <= minor {
+            println!("cargo:rustc-cfg=rust_v_1_{}", version);
+        }
     }
 }
diff --git a/io/Cargo.toml b/io/Cargo.toml
deleted file mode 100644
index 87101120..00000000
--- a/io/Cargo.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-[package]
-name = "bitcoin-io"
-version = "0.1.0"
-authors = ["Matt Corallo <birchneutea@mattcorallo.com>"]
-license = "CC0-1.0"
-repository = "https://github.com/rust-bitcoin/rust-bitcoin"
-documentation = "https://docs.rs/bitcoin-io/"
-description = "Simple I/O traits for no-std (and std) environments"
-categories = ["no-std"]
-keywords = [ "io", "no-std" ]
-readme = "README.md"
-edition = "2018"
-exclude = ["tests", "contrib"]
-
-[features]
-default = ["std"]
-std = []
-alloc = []
-
-[package.metadata.docs.rs]
-all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
diff --git a/io/src/lib.rs b/io/src/lib.rs
deleted file mode 100644
index b21acc8a..00000000
--- a/io/src/lib.rs
+++ /dev/null
@@ -1,425 +0,0 @@
-//! Rust-Bitcoin IO Library
-//!
-//! Because the core `std::io` module is not yet exposed in `no-std` Rust, building `no-std`
-//! applications which require reading and writing objects via standard traits is not generally
-//! possible. While there is ongoing work to improve this situation, this module is not likely to
-//! be available for applications with broad rustc version support for some time.
-//!
-//! Thus, this library exists to export a minmal version of `std::io`'s traits which `no-std`
-//! applications may need. With the `std` feature, these traits are also implemented for the
-//! `std::io` traits, allowing standard objects to be used wherever the traits from this crate are
-//! required.
-//!
-//! This traits are not one-for-one drop-ins, but are as close as possible while still implementing
-//! `std::io`'s traits without unnecessary complexity.
-
-// Experimental features we need.
-#![cfg_attr(docsrs, feature(doc_auto_cfg))]
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-#[cfg(any(feature = "alloc", feature = "std"))]
-extern crate alloc;
-
-/// Standard I/O stream definitions which are API-equivalent to `std`'s `io` module. See
-/// [`std::io`] for more info.
-pub mod io {
-    use core::convert::TryInto;
-    use core::fmt::{Debug, Display, Formatter};
-    #[cfg(any(feature = "alloc", feature = "std"))]
-    use alloc::boxed::Box;
-
-    #[cfg(all(feature = "alloc", not(feature = "std")))]
-    mod sealed {
-        use alloc::boxed::Box;
-        use alloc::string::String;
-        use core::fmt::Debug;
-        pub trait IntoBoxDynDebug {
-            fn into(self) -> Box<dyn Debug + Send + Sync + 'static>;
-        }
-        impl IntoBoxDynDebug for &str {
-            fn into(self) -> Box<dyn Debug + Send + Sync + 'static> {
-                Box::new(String::from(self))
-            }
-        }
-        impl IntoBoxDynDebug for String {
-            fn into(self) -> Box<dyn Debug + Send + Sync + 'static> {
-                Box::new(self)
-            }
-        }
-    }
-
-    #[derive(Debug)]
-    pub struct Error {
-        kind: ErrorKind,
-
-        #[cfg(feature = "std")]
-        error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
-        #[cfg(all(feature = "alloc", not(feature = "std")))]
-        error: Option<Box<dyn Debug + Send + Sync + 'static>>,
-    }
-    impl Error {
-        #[cfg(feature = "std")]
-        pub fn new<E>(kind: ErrorKind, error: E) -> Error
-        where
-            E: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
-        {
-            Self { kind, error: Some(error.into()) }
-        }
-        #[cfg(all(feature = "alloc", not(feature = "std")))]
-        pub fn new<E: sealed::IntoBoxDynDebug>(kind: ErrorKind, error: E) -> Error {
-            Self { kind, error: Some(error.into()) }
-        }
-
-        pub fn kind(&self) -> ErrorKind {
-            self.kind
-        }
-    }
-
-    impl From<ErrorKind> for Error {
-        fn from(kind: ErrorKind) -> Error {
-            Self {
-                kind,
-                #[cfg(any(feature = "std", feature = "alloc"))]
-                error: None,
-            }
-        }
-    }
-
-    impl Display for Error {
-        fn fmt(&self, fmt: &mut Formatter) -> core::result::Result<(), core::fmt::Error> {
-            fmt.write_fmt(format_args!("I/O Error: {}", self.kind.description()))?;
-            #[cfg(any(feature = "alloc", feature = "std"))]
-            if let Some(e) = &self.error {
-                fmt.write_fmt(format_args!(". {:?}", e))?;
-            }
-            Ok(())
-        }
-    }
-
-    #[cfg(feature = "std")]
-    impl std::error::Error for Error {
-        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-            self.error.as_ref().and_then(|e| e.as_ref().source())
-        }
-        #[allow(deprecated)]
-        fn description(&self) -> &str {
-            match self.error.as_ref() {
-                Some(e) => e.description(),
-                None => self.kind.description(),
-            }
-        }
-        #[allow(deprecated)]
-        fn cause(&self) -> Option<&dyn std::error::Error> {
-            self.error.as_ref().and_then(|e| e.as_ref().cause())
-        }
-    }
-
-    impl Error {
-        #[cfg(feature = "std")]
-        pub fn get_ref(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
-            self.error.as_deref()
-        }
-        #[cfg(all(feature = "alloc", not(feature = "std")))]
-        pub fn get_ref(&self) -> Option<&(dyn Debug + Send + Sync + 'static)> {
-            self.error.as_deref()
-        }
-    }
-
-    #[cfg(feature = "std")]
-    impl From<std::io::Error> for Error {
-        fn from(o: std::io::Error) -> Error {
-            Self { kind: ErrorKind::from_std(o.kind()), error: o.into_inner() }
-        }
-    }
-
-    #[cfg(feature = "std")]
-    impl From<Error> for std::io::Error {
-        fn from(o: Error) -> std::io::Error {
-            if let Some(err) = o.error {
-                std::io::Error::new(o.kind.to_std(), err)
-            } else {
-                o.kind.to_std().into()
-            }
-        }
-    }
-
-    macro_rules! define_errorkind {
-        ($($kind: ident),*) => {
-            #[non_exhaustive]
-            #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
-            /// A minimal subset of [`std::io::ErrorKind`] which is used for [`Error`]. Note that, as with
-            /// [`std::io`], only [`Self::Interrupted`] has defined semantics in this crate, all other
-            /// variants are provided here only to provide higher-fidelity conversions to and from
-            /// [`std::io::Error`].
-            pub enum ErrorKind {
-                $($kind),*
-            }
-
-            impl ErrorKind {
-                fn description(&self) -> &'static str {
-                    match self {
-                        $(Self::$kind => stringify!($kind)),*
-                    }
-                }
-                #[cfg(feature = "std")]
-                fn to_std(self) -> std::io::ErrorKind {
-                    match self {
-                        $(Self::$kind => std::io::ErrorKind::$kind),*
-                    }
-                }
-                #[cfg(feature = "std")]
-                fn from_std(o: std::io::ErrorKind) -> ErrorKind {
-                    match o {
-                        $(std::io::ErrorKind::$kind => ErrorKind::$kind),*,
-                        _ => ErrorKind::Other
-                    }
-                }
-            }
-        }
-    }
-
-    define_errorkind!(
-        NotFound,
-        PermissionDenied,
-        ConnectionRefused,
-        ConnectionReset,
-        ConnectionAborted,
-        NotConnected,
-        AddrInUse,
-        AddrNotAvailable,
-        BrokenPipe,
-        AlreadyExists,
-        WouldBlock,
-        InvalidInput,
-        InvalidData,
-        TimedOut,
-        WriteZero,
-        Interrupted,
-        UnexpectedEof,
-        // Note: Any time we bump the MSRV any new error kinds should be added here!
-        Other
-    );
-
-    pub type Result<T> = core::result::Result<T, Error>;
-
-    /// A generic trait describing an input stream. See [`std::io::Read`] for more info.
-    pub trait Read {
-        fn read(&mut self, buf: &mut [u8]) -> Result<usize>;
-        #[inline]
-        fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
-            while !buf.is_empty() {
-                match self.read(buf) {
-                    Ok(0) => return Err(ErrorKind::UnexpectedEof.into()),
-                    Ok(len) => buf = &mut buf[len..],
-                    Err(e) if e.kind() == ErrorKind::Interrupted => {}
-                    Err(e) => return Err(e),
-                }
-            }
-            Ok(())
-        }
-        #[inline]
-        fn take(&mut self, limit: u64) -> Take<Self> {
-            Take { reader: self, remaining: limit }
-        }
-    }
-
-    pub struct Take<'a, R: Read + ?Sized> {
-        reader: &'a mut R,
-        remaining: u64,
-    }
-    impl<'a, R: Read + ?Sized> Read for Take<'a, R> {
-        #[inline]
-        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
-            let len = core::cmp::min(buf.len(), self.remaining.try_into().unwrap_or(buf.len()));
-            let read = self.reader.read(&mut buf[..len])?;
-            self.remaining -= read.try_into().unwrap_or(self.remaining);
-            Ok(read)
-        }
-    }
-
-    #[cfg(feature = "std")]
-    impl<R: std::io::Read> Read for R {
-        #[inline]
-        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
-            Ok(<R as std::io::Read>::read(self, buf)?)
-        }
-    }
-
-    #[cfg(not(feature = "std"))]
-    impl Read for &[u8] {
-        #[inline]
-        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
-            let cnt = core::cmp::min(self.len(), buf.len());
-            buf[..cnt].copy_from_slice(&self[..cnt]);
-            *self = &self[cnt..];
-            Ok(cnt)
-        }
-    }
-
-    pub struct Cursor<T> {
-        inner: T,
-        pos: u64,
-    }
-    impl<T: AsRef<[u8]>> Cursor<T> {
-        #[inline]
-        pub fn new(inner: T) -> Self {
-            Cursor { inner, pos: 0 }
-        }
-        #[inline]
-        pub fn position(&self) -> u64 {
-            self.pos
-        }
-        #[inline]
-        pub fn into_inner(self) -> T {
-            self.inner
-        }
-    }
-    impl<T: AsRef<[u8]>> Read for Cursor<T> {
-        #[inline]
-        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
-            let inner: &[u8] = self.inner.as_ref();
-            let start_pos = self.pos.try_into().unwrap_or(inner.len());
-            let read = core::cmp::min(inner.len().saturating_sub(start_pos), buf.len());
-            buf[..read].copy_from_slice(&inner[start_pos..start_pos + read]);
-            self.pos = self.pos.saturating_add(read.try_into().unwrap_or(u64::max_value() /* unreachable */));
-            Ok(read)
-        }
-    }
-
-    /// A generic trait describing an output stream. See [`std::io::Write`] for more info.
-    pub trait Write {
-        fn write(&mut self, buf: &[u8]) -> Result<usize>;
-        fn flush(&mut self) -> Result<()>;
-
-        #[inline]
-        fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
-            while !buf.is_empty() {
-                match self.write(buf) {
-                    Ok(0) => return Err(ErrorKind::UnexpectedEof.into()),
-                    Ok(len) => buf = &buf[len..],
-                    Err(e) if e.kind() == ErrorKind::Interrupted => {}
-                    Err(e) => return Err(e),
-                }
-            }
-            Ok(())
-        }
-    }
-
-    #[cfg(feature = "std")]
-    impl<W: std::io::Write> Write for W {
-        #[inline]
-        fn write(&mut self, buf: &[u8]) -> Result<usize> {
-            Ok(<W as std::io::Write>::write(self, buf)?)
-        }
-        #[inline]
-        fn flush(&mut self) -> Result<()> {
-            Ok(<W as std::io::Write>::flush(self)?)
-        }
-    }
-
-    #[cfg(all(feature = "alloc", not(feature = "std")))]
-    impl Write for alloc::vec::Vec<u8> {
-        #[inline]
-        fn write(&mut self, buf: &[u8]) -> Result<usize> {
-            self.extend_from_slice(buf);
-            Ok(buf.len())
-        }
-        #[inline]
-        fn flush(&mut self) -> Result<()> { Ok(()) }
-    }
-
-    #[cfg(not(feature = "std"))]
-    impl<'a> Write for &'a mut [u8] {
-        #[inline]
-        fn write(&mut self, buf: &[u8]) -> Result<usize> {
-            let cnt = core::cmp::min(self.len(), buf.len());
-            self[..cnt].copy_from_slice(&buf[..cnt]);
-            *self = &mut core::mem::take(self)[cnt..];
-            Ok(cnt)
-        }
-        #[inline]
-        fn flush(&mut self) -> Result<()> { Ok(()) }
-    }
-
-    /// A sink to which all writes succeed. See [`std::io::Sink`] for more info.
-    pub struct Sink;
-    #[cfg(not(feature = "std"))]
-    impl Write for Sink {
-        #[inline]
-        fn write(&mut self, buf: &[u8]) -> Result<usize> {
-            Ok(buf.len())
-        }
-        #[inline]
-        fn write_all(&mut self, _: &[u8]) -> Result<()> { Ok(()) }
-        #[inline]
-        fn flush(&mut self) -> Result<()> { Ok(()) }
-    }
-    #[cfg(feature = "std")]
-    impl std::io::Write for Sink {
-        #[inline]
-        fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
-            Ok(buf.len())
-        }
-        #[inline]
-        fn write_all(&mut self, _: &[u8]) -> std::io::Result<()> { Ok(()) }
-        #[inline]
-        fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
-    }
-    /// Returns a sink to which all writes succeed. See [`std::io::sink`] for more info.
-    pub fn sink() -> Sink { Sink }
-}
-
-#[doc(hidden)]
-#[cfg(feature = "std")]
-/// Re-export std for the below macro
-pub use std as _std;
-
-#[macro_export]
-/// Because we cannot provide a blanket implementation of [`std::io::Write`] for all implementers
-/// of this crate's `io::Write` trait, we provide this macro instead.
-///
-/// This macro will implement `Write` given a `write` and `flush` fn, either by implementing the
-/// crate's native `io::Write` trait directly, or a more generic trait from `std` for users using
-/// that feature. In any case, this crate's `io::Write` feature will be implemented for the given
-/// type, even if indirectly.
-#[cfg(not(feature = "std"))]
-macro_rules! impl_write {
-    ($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
-        impl<$($bounded_ty: $bounds),*> $crate::io::Write for $ty {
-            #[inline]
-            fn write(&mut self, buf: &[u8]) -> $crate::io::Result<usize> {
-                $write_fn(self, buf)
-            }
-            #[inline]
-            fn flush(&mut self) -> $crate::io::Result<()> {
-                $flush_fn(self)
-            }
-        }
-    }
-}
-
-
-#[macro_export]
-/// Because we cannot provide a blanket implementation of [`std::io::Write`] for all implementers
-/// of this crate's `io::Write` trait, we provide this macro instead.
-///
-/// This macro will implement `Write` given a `write` and `flush` fn, either by implementing the
-/// crate's native `io::Write` trait directly, or a more generic trait from `std` for users using
-/// that feature. In any case, this crate's `io::Write` feature will be implemented for the given
-/// type, even if indirectly.
-#[cfg(feature = "std")]
-macro_rules! impl_write {
-    ($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
-        impl<$($bounded_ty: $bounds),*> $crate::_std::io::Write for $ty {
-            #[inline]
-            fn write(&mut self, buf: &[u8]) -> $crate::_std::io::Result<usize> {
-                $write_fn(self, buf)
-            }
-            #[inline]
-            fn flush(&mut self) -> $crate::_std::io::Result<()> {
-                $flush_fn(self)
-            }
-        }
-    }
-}


Test patch:
diff --git a/contrib/test.sh b/contrib/test.sh
index 96ea2d70..986f48f4 100755
--- a/contrib/test.sh
+++ b/contrib/test.sh
@@ -17,6 +17,8 @@ if cargo --version | grep ${MSRV}; then
     cargo update -p schemars --precise 0.8.12
     # schemars_derive 0.8.13 uses edition 2021
     cargo update -p schemars_derive --precise 0.8.12
+    # memchr 2.6.0 uses edition 2021
+    cargo update -p memchr --precise 2.5.0
     # byteorder 1.5.0 uses edition 2021
     cargo update -p byteorder --precise 1.4.3
 
diff --git a/hashes/contrib/test.sh b/hashes/contrib/test.sh
index a0dc5e34..37160d0e 100755
--- a/hashes/contrib/test.sh
+++ b/hashes/contrib/test.sh
@@ -2,7 +2,7 @@
 
 set -ex
 
-FEATURES="serde serde-std std io alloc"
+FEATURES="serde serde-std std core2 alloc"
 
 cargo --version
 rustc --version
diff --git a/hashes/extended_tests/schemars/Cargo.toml b/hashes/extended_tests/schemars/Cargo.toml
index 8a8943d2..7c523c0f 100644
--- a/hashes/extended_tests/schemars/Cargo.toml
+++ b/hashes/extended_tests/schemars/Cargo.toml
@@ -21,6 +21,3 @@ serde_json = "1.0"
 
 [patch.crates-io.bitcoin-internals]
 path = "../../../internals"
-
-[patch.crates-io.bitcoin-io]
-path = "../../../io"