Repository: rust-bitcoin/rust-bitcoin
Pull request #2240 (2023-11-29 00:32:39)

Require `BufRead` instead of `Read`
Require `BufRead` instead of `Read` for consensus decode trait.


tcharding: > Rebasing, undraftin, and eventually merging will unblock my work on push_decode.

Lets go!
apoelstra: Whew, this trait has weirdly-defined methods. I had to check the std docs to be sure that we were supposed to expose the internal buffer size like that with methods like "return the remaning contents of the internal buffer, unless there are none, in which case first fill it as much as we can".

utACK.
Kixunil: Also note that this is not an optimization but it prevents people from accidentally writing horribly unoptimal code.
apoelstra: > Holy shit, this change is horrible because of the blanket impl on `std` traits.

Without the blanket impl it's differently horrible (though maybe less horrible, on net). It means that we have a `BufRead` trait that isn't actually implemented on all the things that users expect `BufRead` to be implemented on. Yes, we can implement it specifically for `&[u8]` but then what happens when a user wants to use a file handle or something?

On second thought, in this case the blanket impl won't help them ... if they do not have `std` they won't get the blanket impl. So no matter what, their filehandle type needs to be aware of our `io` crate and its `BufRead` type. Ok, yeah, never mind. We should drop the blanket impl.
apoelstra: Since we already have `Cursor` I'd like to keep it for this PR. It is a very simple type and generally useful. Though I agree we don't need it on `&[u8]`.

I'm ambivalent about switching its internal position to `u64`. It looks like `std` uses `u64` (and to implement `BufRead` they need to cast the `usize` argument to a `u64` internally, which I suppose hypothetically could be a bug if it was used on a 128-bit arch and somebody was trying to consume enough bytes..). I believe you're correct that the motivation for this is the `Seek` trait, which maybe we will want in the future.
Kixunil: @apoelstra `&[u8]` already implements `Read` sanely (using `#[cfg]` trick), it really should implement `BufRead`. I personally never need `Cursor` in practice because of the impl and would hate to need it because someone didn't write a few lines of code.

When I wrote that comment I thought blanket impls are the problem and only later I discovered that it's solvable using `#[cfg]` and the solution is already being used. I no longer believe we should drop them.
tcharding: Just to be clear, this PR was originally @Kixunil's work in https://github.com/rust-bitcoin/rust-bitcoin/pull/2179
tcharding: Draft until I resolve @Kixunil's review comments.
tcharding: > Aside from the bug, you previously set the PR to draft until you address my feedback but you now un-drafted it without removing `remaining` from `Cursor`...

Ha, that is a comical fail for the start of the year. I literally wondered yesterday about `remaining` after it appeared to come back (I must have swapped machines and not pulled), I convinced myself it was ok - face palm.
tcharding: Force push removes `remaining` ... again. Also casts `usize` to `u64` instead of the other way around. 
sanket1729: > Also note that this is not an optimization but it prevents people from accidentally writing horribly unoptimal code.

Is it that bad? 

tcharding: Thanks for the review @sanket1729, and yes please wait for @Kixunil before merging.
tcharding: Changes in force push:
- Used `take` .
- Updated the commit log in the first patch.
Kixunil: @sanket1729 

> Is it that bad?

Yes. Storytime!

Back when I was a kid, I've implemented my own encryption program (yes, the horror; please ignore obvious cryptographic issues; to my defense though I wasn't defending against state-level attackers...) It took a file to encrypt and a key file which was just random noise it xorred each byte of the input file with a corresponding byte of the output file - reading and writing one-by-one.

What would one expect to take under a second took minutes (IIRC, anyway, it was clearly crazy slow) for even few hundred kB files. And as a kid I didn't know about cost of context switch and how buffering avoids it.

We don't parse byte-by-byte but there's a lot of places with similarly short reads and the only thing that actually does bulk reads are scripts and witness elements. So yes, I expect it'd be horrible to make that mistake and could be hard for newbies. Notably I still see questions on /r/rust "why is reading from file slow in Rust?" from time to time.

Patch:
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
index 865f7c92..afdd271d 100644
--- a/.github/workflows/fuzz.yml
+++ b/.github/workflows/fuzz.yml
@@ -69,7 +69,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: actions/download-artifact@v3
+      - uses: actions/download-artifact@v4
       - name: Display structure of downloaded files
         run: ls -R
       - run: find executed_* -type f -exec cat {} + | sort > executed
diff --git a/.github/workflows/kani.yml b/.github/workflows/kani.yml
index dff1e54b..520ad176 100644
--- a/.github/workflows/kani.yml
+++ b/.github/workflows/kani.yml
@@ -11,4 +11,4 @@ jobs:
         uses: actions/checkout@v4
 
       - name: 'Run Kani on your code.'
-        uses: model-checking/kani-github-action@v1.1
+        uses: model-checking/kani-github-action@v1.0
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index fb048b8c..1cae13ca 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -45,7 +45,6 @@ dependencies = [
  "hex-conservative",
  "hex_lit",
  "mutagen",
- "ordered",
  "secp256k1",
  "serde",
  "serde_json",
@@ -236,12 +235,6 @@ dependencies = [
  "proc-macro2",
 ]
 
-[[package]]
-name = "ordered"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0642533dea0bb58bd5cae31bafc1872429f0f12ac8c61fe2b4ba44f80b959b"
-
 [[package]]
 name = "ppv-lite86"
 version = "0.2.8"
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index ceda6598..90a84ae2 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -44,7 +44,6 @@ dependencies = [
  "hex-conservative",
  "hex_lit",
  "mutagen",
- "ordered",
  "secp256k1",
  "serde",
  "serde_json",
@@ -235,12 +234,6 @@ dependencies = [
  "proc-macro2",
 ]
 
-[[package]]
-name = "ordered"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f0642533dea0bb58bd5cae31bafc1872429f0f12ac8c61fe2b4ba44f80b959b"
-
 [[package]]
 name = "ppv-lite86"
 version = "0.2.17"
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 8c899897..f06cc572 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -38,7 +38,6 @@ secp256k1 = { version = "0.28.0", default-features = false, features = ["hashes"
 units = { package = "bitcoin-units", version = "0.1.0", default-features = false, features = ["alloc"] }
 
 base64 = { version = "0.21.3", optional = true }
-ordered = { version = "0.2.0", optional = true }
 # Only use this feature for no-std builds, otherwise use bitcoinconsensus-std.
 bitcoinconsensus = { version = "0.20.2-0.5.0", default-features = false, optional = true }
 
diff --git a/bitcoin/examples/sighash.rs b/bitcoin/examples/sighash.rs
index 04b0fb3f..bdddaa7e 100644
--- a/bitcoin/examples/sighash.rs
+++ b/bitcoin/examples/sighash.rs
@@ -44,7 +44,7 @@ fn compute_sighash_p2wpkh(raw_tx: &[u8], inp_idx: usize, value: u64) {
 
     let mut cache = sighash::SighashCache::new(&tx);
     let sighash = cache
-        .p2wpkh_signature_hash(inp_idx, &spk, Amount::from_sat(value), sig.sighash_type)
+        .p2wpkh_signature_hash(inp_idx, &spk, Amount::from_sat(value), sig.hash_ty)
         .expect("failed to compute sighash");
     println!("Segwit p2wpkh sighash: {:x}", sighash);
     let msg = secp256k1::Message::from_digest(sighash.to_byte_array());
@@ -93,9 +93,9 @@ fn compute_sighash_legacy(raw_tx: &[u8], inp_idx: usize, script_pubkey_bytes_opt
         let sig = ecdsa::Signature::from_slice(instr.unwrap().push_bytes().unwrap().as_bytes())
             .expect("failed to parse sig");
         let sighash = cache
-            .legacy_signature_hash(inp_idx, script_code, sig.sighash_type.to_u32())
+            .legacy_signature_hash(inp_idx, script_code, sig.hash_ty.to_u32())
             .expect("failed to compute sighash");
-        println!("Legacy sighash: {:x} (sighash flag {})", sighash, sig.sighash_type);
+        println!("Legacy sighash: {:x} (sighash flag {})", sighash, sig.hash_ty);
     }
 }
 
@@ -126,14 +126,9 @@ fn compute_sighash_p2wsh(raw_tx: &[u8], inp_idx: usize, value: u64) {
         assert!((70..=72).contains(&sig_len), "signature length {} out of bounds", sig_len);
         //here we assume that all sighash_flags are the same. Can they be different?
         let sighash = cache
-            .p2wsh_signature_hash(
-                inp_idx,
-                witness_script,
-                Amount::from_sat(value),
-                sig.sighash_type,
-            )
+            .p2wsh_signature_hash(inp_idx, witness_script, Amount::from_sat(value), sig.hash_ty)
             .expect("failed to compute sighash");
-        println!("Segwit p2wsh sighash: {:x} ({})", sighash, sig.sighash_type);
+        println!("Segwit p2wsh sighash: {:x} ({})", sighash, sig.hash_ty);
     }
 }
 
diff --git a/bitcoin/examples/sign-tx-segwit-v0.rs b/bitcoin/examples/sign-tx-segwit-v0.rs
index 6a8879d3..ef7f969c 100644
--- a/bitcoin/examples/sign-tx-segwit-v0.rs
+++ b/bitcoin/examples/sign-tx-segwit-v0.rs
@@ -66,10 +66,10 @@ fn main() {
 
     // Sign the sighash using the secp256k1 library (exported by rust-bitcoin).
     let msg = Message::from(sighash);
-    let signature = secp.sign_ecdsa(&msg, &sk);
+    let sig = secp.sign_ecdsa(&msg, &sk);
 
     // Update the witness stack.
-    let signature = bitcoin::ecdsa::Signature { signature, sighash_type };
+    let signature = bitcoin::ecdsa::Signature { sig, hash_ty: EcdsaSighashType::All };
     let pk = sk.public_key(&secp);
     *sighasher.witness_mut(input_index).unwrap() = Witness::p2wpkh(&signature, &pk);
 
diff --git a/bitcoin/examples/sign-tx-taproot.rs b/bitcoin/examples/sign-tx-taproot.rs
index f8fe3f02..c9025df2 100644
--- a/bitcoin/examples/sign-tx-taproot.rs
+++ b/bitcoin/examples/sign-tx-taproot.rs
@@ -72,10 +72,10 @@ fn main() {
     // Sign the sighash using the secp256k1 library (exported by rust-bitcoin).
     let tweaked: TweakedKeypair = keypair.tap_tweak(&secp, None);
     let msg = Message::from_digest(sighash.to_byte_array());
-    let signature = secp.sign_schnorr(&msg, &tweaked.to_inner());
+    let sig = secp.sign_schnorr(&msg, &tweaked.to_inner());
 
     // Update the witness stack.
-    let signature = bitcoin::taproot::Signature { signature, sighash_type };
+    let signature = bitcoin::taproot::Signature { sig, hash_ty: sighash_type };
     sighasher.witness_mut(input_index).unwrap().push(&signature.to_vec());
 
     // Get the signed transaction.
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 66f6b796..1b4d4a81 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -282,14 +282,14 @@ fn generate_bip86_key_spend_tx(
     let unsigned_tx = psbt.unsigned_tx.clone();
     psbt.inputs.iter_mut().enumerate().try_for_each::<_, Result<(), Box<dyn std::error::Error>>>(
         |(vout, input)| {
-            let sighash_type = input
+            let hash_ty = input
                 .sighash_type
                 .and_then(|psbt_sighash_type| psbt_sighash_type.taproot_hash_ty().ok())
                 .unwrap_or(TapSighashType::All);
             let hash = SighashCache::new(&unsigned_tx).taproot_key_spend_signature_hash(
                 vout,
                 &sighash::Prevouts::All(input_txouts.as_slice()),
-                sighash_type,
+                hash_ty,
             )?;
 
             let (_, (_, derivation_path)) = input
@@ -304,7 +304,7 @@ fn generate_bip86_key_spend_tx(
                 None,
                 input,
                 hash,
-                sighash_type,
+                hash_ty,
                 secp,
             );
 
@@ -512,7 +512,7 @@ fn refresh_tx(
             psbt.outputs = vec![Output::default()];
             psbt.unsigned_tx.lock_time = absolute::LockTime::ZERO;
 
-            let sighash_type = input
+            let hash_ty = input
                 .sighash_type
                 .and_then(|psbt_sighash_type| psbt_sighash_type.taproot_hash_ty().ok())
                 .unwrap_or(TapSighashType::All);
@@ -522,7 +522,7 @@ fn refresh_tx(
                     value: input_value,
                     script_pubkey: prevout_script_pubkey,
                 }]),
-                sighash_type,
+                hash_ty,
             )?;
 
             {
@@ -538,7 +538,7 @@ fn refresh_tx(
                     None,
                     input,
                     hash,
-                    sighash_type,
+                    hash_ty,
                     &self.secp,
                 );
             }
@@ -654,7 +654,7 @@ fn spend_inheritance(
             let secret_key =
                 self.master_xpriv.derive_priv(&self.secp, &derivation_path)?.to_priv().inner;
             for lh in leaf_hashes {
-                let sighash_type = TapSighashType::All;
+                let hash_ty = TapSighashType::All;
                 let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(
                     0,
                     &sighash::Prevouts::All(&[TxOut {
@@ -662,7 +662,7 @@ fn spend_inheritance(
                         script_pubkey: input_script_pubkey.clone(),
                     }]),
                     *lh,
-                    sighash_type,
+                    hash_ty,
                 )?;
                 sign_psbt_taproot(
                     &secret_key,
@@ -670,7 +670,7 @@ fn spend_inheritance(
                     Some(*lh),
                     &mut psbt.inputs[0],
                     hash,
-                    sighash_type,
+                    hash_ty,
                     &self.secp,
                 );
             }
@@ -730,7 +730,7 @@ fn sign_psbt_taproot(
     leaf_hash: Option<TapLeafHash>,
     psbt_input: &mut psbt::Input,
     hash: TapSighash,
-    sighash_type: TapSighashType,
+    hash_ty: TapSighashType,
     secp: &Secp256k1<secp256k1::All>,
 ) {
     let keypair = secp256k1::Keypair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
@@ -740,9 +740,9 @@ fn sign_psbt_taproot(
     };
 
     let msg = secp256k1::Message::from_digest(hash.to_byte_array());
-    let signature = secp.sign_schnorr(&msg, &keypair);
+    let sig = secp.sign_schnorr(&msg, &keypair);
 
-    let final_signature = taproot::Signature { signature, sighash_type };
+    let final_signature = taproot::Signature { sig, hash_ty };
 
     if let Some(lh) = leaf_hash {
         psbt_input.tap_script_sigs.insert((pubkey, lh), final_signature);
diff --git a/bitcoin/src/address/mod.rs b/bitcoin/src/address/mod.rs
index d1d8175e..79ae4c80 100644
--- a/bitcoin/src/address/mod.rs
+++ b/bitcoin/src/address/mod.rs
@@ -44,7 +44,7 @@
 };
 use crate::blockdata::script::witness_program::WitnessProgram;
 use crate::blockdata::script::witness_version::WitnessVersion;
-use crate::blockdata::script::{self, Script, ScriptBuf, ScriptHash};
+use crate::blockdata::script::{self, PushBytesBuf, Script, ScriptBuf, ScriptHash};
 use crate::crypto::key::{
     CompressedPublicKey, PubkeyHash, PublicKey, TweakedPublicKey, UntweakedPublicKey,
 };
@@ -132,8 +132,9 @@ impl NetworkValidation for NetworkUnchecked {
 
 /// The inner representation of an address, without the network validation tag.
 ///
-/// This struct represents the inner representation of an address without the network validation
-/// tag, which is used to ensure that addresses are used only on the appropriate network.
+/// An `Address` is composed of a payload and a network. This struct represents the inner
+/// representation of an address without the network validation tag, which is used to ensure that
+/// addresses are used only on the appropriate network.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 enum AddressInner {
     P2pkh { hash: PubkeyHash, network: NetworkKind },
@@ -318,8 +319,11 @@ fn from(n: Network) -> Self { Self::from_network(n) }
 /// * [BIP341 - Taproot: SegWit version 1 spending rules](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)
 /// * [BIP350 - Bech32m format for v1+ witness addresses](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-// The `#[repr(transparent)]` attribute is used to guarantee the layout of the `Address` struct. It
-// is an implementation detail and users should not rely on it in their code.
+///
+/// The `#[repr(transparent)]` attribute is used to guarantee that the layout of the
+/// `Address` struct is the same as the layout of the `AddressInner` struct. This attribute is
+/// an implementation detail and users should not rely on it in their code.
+///
 #[repr(transparent)]
 pub struct Address<V = NetworkChecked>(AddressInner, PhantomData<V>)
 where
@@ -349,7 +353,8 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 /// Methods on [`Address`] that can be called on both `Address<NetworkChecked>` and
 /// `Address<NetworkUnchecked>`.
 impl<V: NetworkValidation> Address<V> {
-    /// Returns a reference to the address as if it was unchecked.
+    /// Returns a reference to the unchecked address, which is dangerous to use if the address
+    /// is invalid in the context of `NetworkUnchecked`.
     pub fn as_unchecked(&self) -> &Address<NetworkUnchecked> {
         unsafe { &*(self as *const Address<V> as *const Address<NetworkUnchecked>) }
     }
@@ -515,8 +520,10 @@ pub fn from_script(script: &Script, network: Network) -> Result<Address, Error>
         } else if script.is_witness_program() {
             let opcode = script.first_opcode().expect("is_witness_program guarantees len > 4");
 
+            let buf = PushBytesBuf::try_from(script.as_bytes()[2..].to_vec())
+                .expect("is_witness_program guarantees len <= 42 bytes");
             let version = WitnessVersion::try_from(opcode)?;
-            let program = WitnessProgram::new(version, &script.as_bytes()[2..])?;
+            let program = WitnessProgram::new(version, buf)?;
             Ok(Address::from_witness_program(program, network))
         } else {
             Err(Error::UnrecognizedScript)
@@ -625,12 +632,10 @@ fn payload_as_bytes(&self) -> &[u8] {
 /// Methods that can be called only on `Address<NetworkUnchecked>`.
 impl Address<NetworkUnchecked> {
     /// Returns a reference to the checked address.
-    ///
     /// This function is dangerous in case the address is not a valid checked address.
     pub fn assume_checked_ref(&self) -> &Address {
         unsafe { &*(self as *const Address<NetworkUnchecked> as *const Address) }
     }
-
     /// Parsed addresses do not always have *one* network. The problem is that legacy testnet,
     /// regtest and signet addresse use the same prefix instead of multiple different ones. When
     /// parsing, such addresses are always assumed to be testnet addresses (the same is true for
@@ -722,7 +727,8 @@ impl FromStr for Address<NetworkUnchecked> {
     fn from_str(s: &str) -> Result<Address<NetworkUnchecked>, ParseError> {
         if let Ok((hrp, witness_version, data)) = bech32::segwit::decode(s) {
             let version = WitnessVersion::try_from(witness_version)?;
-            let program = WitnessProgram::new(version, &data)
+            let buf = PushBytesBuf::try_from(data).expect("bech32 guarantees valid program length");
+            let program = WitnessProgram::new(version, buf)
                 .expect("bech32 guarantees valid program length for witness");
 
             let hrp = KnownHrp::from_hrp(hrp)?;
@@ -918,10 +924,10 @@ fn test_p2shwsh() {
     #[test]
     fn test_non_existent_segwit_version() {
         // 40-byte program
-        let program = hex!(
+        let program = PushBytesBuf::from(hex!(
             "654f6ea368e0acdfd92976b7c2103a1b26313f430654f6ea368e0acdfd92976b7c2103a1b26313f4"
-        );
-        let program = WitnessProgram::new(WitnessVersion::V13, &program).expect("valid program");
+        ));
+        let program = WitnessProgram::new(WitnessVersion::V13, program).expect("valid program");
 
         let addr = Address::from_witness_program(program, Bitcoin);
         roundtrips(&addr, Bitcoin);
diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs
index b9da6710..61752610 100644
--- a/bitcoin/src/bip152.rs
+++ b/bitcoin/src/bip152.rs
@@ -12,7 +12,7 @@
 
 use hashes::{sha256, siphash24, Hash};
 use internals::impl_array_newtype;
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::consensus::encode::{self, Decodable, Encodable, VarInt};
 use crate::internal_macros::{impl_bytes_newtype, impl_consensus_encoding};
@@ -81,7 +81,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for PrefilledTransaction {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let idx = VarInt::consensus_decode(r)?.0;
         let idx = u16::try_from(idx)
             .map_err(|_| encode::Error::ParseFailed("BIP152 prefilled tx index out of bounds"))?;
@@ -137,7 +137,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for ShortId {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<ShortId, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<ShortId, encode::Error> {
         Ok(ShortId(Decodable::consensus_decode(r)?))
     }
 }
@@ -273,7 +273,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for BlockTransactionsRequest {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(BlockTransactionsRequest {
             block_hash: BlockHash::consensus_decode(r)?,
             indexes: {
diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs
index 7ada3c3f..98cbc99e 100644
--- a/bitcoin/src/bip158.rs
+++ b/bitcoin/src/bip158.rs
@@ -43,7 +43,7 @@
 
 use hashes::{sha256d, siphash24, Hash};
 use internals::write_err;
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::blockdata::block::{Block, BlockHash};
 use crate::blockdata::script::Script;
@@ -245,7 +245,7 @@ pub fn match_any<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
     where
         I: Iterator,
         I::Item: Borrow<[u8]>,
-        R: Read + ?Sized,
+        R: BufRead + ?Sized,
     {
         self.reader.match_any(reader, query)
     }
@@ -255,7 +255,7 @@ pub fn match_all<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
     where
         I: Iterator,
         I::Item: Borrow<[u8]>,
-        R: Read + ?Sized,
+        R: BufRead + ?Sized,
     {
         self.reader.match_all(reader, query)
     }
@@ -278,7 +278,7 @@ pub fn match_any<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
     where
         I: Iterator,
         I::Item: Borrow<[u8]>,
-        R: Read + ?Sized,
+        R: BufRead + ?Sized,
     {
         let n_elements: VarInt = Decodable::consensus_decode(reader).unwrap_or(VarInt(0));
         // map hashes to [0, n_elements << grp]
@@ -321,7 +321,7 @@ pub fn match_all<I, R>(&self, reader: &mut R, query: I) -> Result<bool, Error>
     where
         I: Iterator,
         I::Item: Borrow<[u8]>,
-        R: Read + ?Sized,
+        R: BufRead + ?Sized,
     {
         let n_elements: VarInt = Decodable::consensus_decode(reader).unwrap_or(VarInt(0));
         // map hashes to [0, n_elements << grp]
@@ -447,7 +447,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: Read + ?Sized,
+        R: BufRead + ?Sized,
     {
         let mut q = 0u64;
         while reader.read(1)? == 1 {
@@ -470,7 +470,7 @@ pub struct BitStreamReader<'a, R: ?Sized> {
     reader: &'a mut R,
 }
 
-impl<'a, R: Read + ?Sized> BitStreamReader<'a, R> {
+impl<'a, R: BufRead + ?Sized> 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 }
@@ -668,14 +668,14 @@ fn test_filter() {
         let bytes = out;
 
         {
-            let query = [hex!("abcdef"), hex!("eeeeee")];
+            let query = vec![hex!("abcdef"), hex!("eeeeee")];
             let reader = GcsFilterReader::new(0, 0, M, P);
             assert!(reader
                 .match_any(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice()))
                 .unwrap());
         }
         {
-            let query = [hex!("abcdef"), hex!("123456")];
+            let query = vec![hex!("abcdef"), hex!("123456")];
             let reader = GcsFilterReader::new(0, 0, M, P);
             assert!(!reader
                 .match_any(&mut bytes.as_slice(), &mut query.iter().map(|v| v.as_slice()))
diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs
index e66d5f22..b8e7f5a8 100644
--- a/bitcoin/src/blockdata/block.rs
+++ b/bitcoin/src/blockdata/block.rs
@@ -11,7 +11,7 @@
 use core::fmt;
 
 use hashes::{sha256d, Hash, HashEngine};
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use super::Weight;
 use crate::blockdata::script;
@@ -207,7 +207,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for Version {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Decodable::consensus_decode(r).map(Version)
     }
 }
diff --git a/bitcoin/src/blockdata/locktime/absolute.rs b/bitcoin/src/blockdata/locktime/absolute.rs
index 6138aa4f..efeaa428 100644
--- a/bitcoin/src/blockdata/locktime/absolute.rs
+++ b/bitcoin/src/blockdata/locktime/absolute.rs
@@ -10,7 +10,7 @@
 use core::{fmt, mem};
 
 use internals::write_err;
-use io::{Read, Write};
+use io::{BufRead, Write};
 #[cfg(all(test, mutate))]
 use mutagen::mutate;
 
@@ -345,7 +345,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for LockTime {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         u32::consensus_decode(r).map(LockTime::from_consensus)
     }
 }
@@ -389,20 +389,6 @@ fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<u32, E> {
     }
 }
 
-#[cfg(feature = "ordered")]
-impl ordered::ArbitraryOrd for LockTime {
-    fn arbitrary_cmp(&self, other: &Self) -> Ordering {
-        use LockTime::*;
-
-        match (self, other) {
-            (Blocks(_), Seconds(_)) => Ordering::Less,
-            (Seconds(_), Blocks(_)) => Ordering::Greater,
-            (Blocks(this), Blocks(that)) => this.cmp(that),
-            (Seconds(this), Seconds(that)) => this.cmp(that),
-        }
-    }
-}
-
 /// An absolute block height, guaranteed to always contain a valid height value.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index b7724b69..be47c91c 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -353,14 +353,7 @@ pub fn is_op_return(&self) -> bool {
         }
     }
 
-    /// Checks whether a script is trivially known to have no satisfying input.
-    ///
-    /// This method has potentially confusing semantics and an unclear purpose, so it's going to be
-    /// removed. Use `is_op_return` if you want `OP_RETURN` semantics.
-    #[deprecated(
-        since = "TBD",
-        note = "The method is not very useful, you might want `is_op_return`"
-    )]
+    /// Checks whether a script can be proven to have no satisfying input.
     #[inline]
     pub fn is_provably_unspendable(&self) -> bool {
         use crate::blockdata::opcodes::Class::{IllegalOp, ReturnOp};
@@ -596,12 +589,22 @@ pub(in crate::blockdata::script) fn last_opcode(&self) -> Option<Opcode> {
     /// Iterates the script to find the last pushdata.
     ///
     /// Returns `None` if the instruction is an opcode or if the script is empty.
-    pub(crate) fn last_pushdata(&self) -> Option<&PushBytes> {
+    pub(crate) fn last_pushdata(&self) -> Option<Push> {
         match self.instructions().last() {
             // Handles op codes up to (but excluding) OP_PUSHNUM_NEG.
-            Some(Ok(Instruction::PushBytes(bytes))) => Some(bytes),
+            Some(Ok(Instruction::PushBytes(bytes))) => Some(Push::Data(bytes)),
             // OP_16 (0x60) and lower are considered "pushes" by Bitcoin Core (excl. OP_RESERVED).
-            // However we are only interested in the pushdata so we can ignore them.
+            // By here we know that op is between OP_PUSHNUM_NEG AND OP_PUSHNUM_16 inclusive.
+            Some(Ok(Instruction::Op(op))) if op.to_u8() <= 0x60 => {
+                if op == OP_PUSHNUM_NEG1 {
+                    Some(Push::Num(-1))
+                } else if op == OP_RESERVED {
+                    Some(Push::Reserved)
+                } else {
+                    let num = (op.to_u8() - 0x50) as i8; // cast ok, num is [1, 16].
+                    Some(Push::Num(num))
+                }
+            }
             _ => None,
         }
     }
@@ -619,6 +622,19 @@ pub fn into_script_buf(self: Box<Self>) -> ScriptBuf {
     }
 }
 
+/// Data pushed by "push" opcodes.
+///
+/// "push" opcodes are defined by Bitcoin Core as OP_PUSHBYTES_, OP_PUSHDATA, OP_PUSHNUM_, and
+/// OP_RESERVED i.e., everything less than OP_PUSHNUM_16 (0x60) . (TODO: Add link to core code).
+pub(crate) enum Push<'a> {
+    /// All the OP_PUSHBYTES_ and OP_PUSHDATA_ opcodes.
+    Data(&'a PushBytes),
+    /// All the OP_PUSHNUM_ opcodes (-1, 1, 2, .., 16)
+    Num(i8),
+    /// OP_RESERVED
+    Reserved,
+}
+
 /// Iterator over bytes of a script
 pub struct Bytes<'a>(core::iter::Copied<core::slice::Iter<'a, u8>>);
 
diff --git a/bitcoin/src/blockdata/script/mod.rs b/bitcoin/src/blockdata/script/mod.rs
index c48e9a57..d79a58fd 100644
--- a/bitcoin/src/blockdata/script/mod.rs
+++ b/bitcoin/src/blockdata/script/mod.rs
@@ -66,7 +66,7 @@
 use core::ops::{Deref, DerefMut};
 
 use hashes::{hash160, sha256};
-use io::{Read, Write};
+use io::{BufRead, Write};
 #[cfg(feature = "serde")]
 use serde;
 
@@ -594,7 +594,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for ScriptBuf {
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         r: &mut R,
     ) -> Result<Self, encode::Error> {
         Ok(ScriptBuf(Decodable::consensus_decode_from_finite_reader(r)?))
diff --git a/bitcoin/src/blockdata/script/owned.rs b/bitcoin/src/blockdata/script/owned.rs
index 39cd3c8a..976caa51 100644
--- a/bitcoin/src/blockdata/script/owned.rs
+++ b/bitcoin/src/blockdata/script/owned.rs
@@ -213,7 +213,7 @@ fn push_slice_no_opt(&mut self, data: &PushBytes) {
         self.0.extend_from_slice(data.as_bytes());
     }
 
-    /// Computes the sum of `len` and the length of an appropriate push opcode.
+    /// Computes the sum of `len` and the lenght of an appropriate push opcode.
     pub(in crate::blockdata::script) fn reserved_len_for_slice(len: usize) -> usize {
         len + match len {
             0..=0x4b => 1,
@@ -277,7 +277,7 @@ pub(in crate::blockdata::script) fn push_verify(&mut self, last_opcode: Option<O
 
     /// Converts this `ScriptBuf` into a [boxed](Box) [`Script`].
     ///
-    /// This method reallocates if the capacity is greater than length of the script but should not
+    /// This method reallocates if the capacity is greater than lenght of the script but should not
     /// when they are equal. If you know beforehand that you need to create a script of exact size
     /// use [`reserve_exact`](Self::reserve_exact) before adding data to the script so that the
     /// reallocation can be avoided.
diff --git a/bitcoin/src/blockdata/script/witness_program.rs b/bitcoin/src/blockdata/script/witness_program.rs
index 7572c515..d2b67024 100644
--- a/bitcoin/src/blockdata/script/witness_program.rs
+++ b/bitcoin/src/blockdata/script/witness_program.rs
@@ -10,65 +10,60 @@
 use core::fmt;
 
 use hashes::Hash as _;
-use internals::array_vec::ArrayVec;
 use secp256k1::{Secp256k1, Verification};
 
 use crate::blockdata::script::witness_version::WitnessVersion;
-use crate::blockdata::script::{PushBytes, Script};
+use crate::blockdata::script::{PushBytes, PushBytesBuf, PushBytesErrorReport, Script};
 use crate::crypto::key::{CompressedPublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey};
 use crate::taproot::TapNodeHash;
 
-/// The minimum byte size of a segregated witness program.
-pub const MIN_SIZE: usize = 2;
-
-/// The maximum byte size of a segregated witness program.
-pub const MAX_SIZE: usize = 40;
-
 /// The segregated witness program.
 ///
 /// The segregated witness program is technically only the program bytes _excluding_ the witness
 /// version, however we maintain length invariants on the `program` that are governed by the version
 /// number, therefore we carry the version number around along with the program bytes.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct WitnessProgram {
     /// The segwit version associated with this witness program.
     version: WitnessVersion,
     /// The witness program (between 2 and 40 bytes).
-    program: ArrayVec<u8, MAX_SIZE>,
+    program: PushBytesBuf,
 }
 
 impl WitnessProgram {
-    /// Creates a new witness program, copying the content from the given byte slice.
-    pub fn new(version: WitnessVersion, bytes: &[u8]) -> Result<Self, Error> {
+    /// Creates a new witness program.
+    pub fn new<P>(version: WitnessVersion, program: P) -> Result<Self, Error>
+    where
+        P: TryInto<PushBytesBuf>,
+        <P as TryInto<PushBytesBuf>>::Error: PushBytesErrorReport,
+    {
         use Error::*;
 
-        let program_len = bytes.len();
-        if program_len < MIN_SIZE || program_len > MAX_SIZE {
-            return Err(InvalidLength(program_len));
+        let program = program.try_into().map_err(|error| InvalidLength(error.input_len()))?;
+        if program.len() < 2 || program.len() > 40 {
+            return Err(InvalidLength(program.len()));
         }
 
         // Specific segwit v0 check. These addresses can never spend funds sent to them.
-        if version == WitnessVersion::V0 && (program_len != 20 && program_len != 32) {
-            return Err(InvalidSegwitV0Length(program_len));
+        if version == WitnessVersion::V0 && (program.len() != 20 && program.len() != 32) {
+            return Err(InvalidSegwitV0Length(program.len()));
         }
-
-        let program = ArrayVec::from_slice(bytes);
         Ok(WitnessProgram { version, program })
     }
 
     /// Creates a [`WitnessProgram`] from a 20 byte pubkey hash.
     fn new_p2wpkh(program: [u8; 20]) -> Self {
-        WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
+        WitnessProgram { version: WitnessVersion::V0, program: program.into() }
     }
 
     /// Creates a [`WitnessProgram`] from a 32 byte script hash.
     fn new_p2wsh(program: [u8; 32]) -> Self {
-        WitnessProgram { version: WitnessVersion::V0, program: ArrayVec::from_slice(&program) }
+        WitnessProgram { version: WitnessVersion::V0, program: program.into() }
     }
 
     /// Creates a [`WitnessProgram`] from a 32 byte serialize taproot xonly pubkey.
     fn new_p2tr(program: [u8; 32]) -> Self {
-        WitnessProgram { version: WitnessVersion::V1, program: ArrayVec::from_slice(&program) }
+        WitnessProgram { version: WitnessVersion::V1, program: program.into() }
     }
 
     /// Creates a [`WitnessProgram`] from `pk` for a P2WPKH output.
@@ -104,12 +99,7 @@ pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Self {
     pub fn version(&self) -> WitnessVersion { self.version }
 
     /// Returns the witness program.
-    pub fn program(&self) -> &PushBytes {
-        self.program
-            .as_slice()
-            .try_into()
-            .expect("witness programs are always smaller than max size of PushBytes")
-    }
+    pub fn program(&self) -> &PushBytes { &self.program }
 
     /// Returns true if this witness program is for a P2WPKH output.
     pub fn is_p2wpkh(&self) -> bool {
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index caf4274d..d956108b 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -16,7 +16,7 @@
 
 use hashes::{self, sha256d, Hash};
 use internals::write_err;
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use super::Weight;
 use crate::blockdata::fee_rate::FeeRate;
@@ -28,6 +28,7 @@
 use crate::internal_macros::{impl_consensus_encoding, impl_hashencode};
 use crate::parse::impl_parse_str_from_int_infallible;
 use crate::prelude::*;
+use crate::script::Push;
 #[cfg(doc)]
 use crate::sighash::{EcdsaSighashType, TapSighashType};
 use crate::string::FromHexStr;
@@ -908,7 +909,7 @@ fn count_p2sh_sigops<S>(&self, spent: &mut S) -> usize
         fn count_sigops(prevout: &TxOut, input: &TxIn) -> usize {
             let mut count: usize = 0;
             if prevout.script_pubkey.is_p2sh() {
-                if let Some(redeem) = input.script_sig.last_pushdata() {
+                if let Some(Push::Data(redeem)) = input.script_sig.last_pushdata() {
                     count =
                         count.saturating_add(Script::from_bytes(redeem.as_bytes()).count_sigops());
                 }
@@ -954,7 +955,7 @@ fn count_sigops(prevout: TxOut, input: &TxIn) -> usize {
             } else if prevout.script_pubkey.is_p2sh() && script_sig.is_push_only() {
                 // If prevout is P2SH and scriptSig is push only
                 // then we wrap the last push (redeemScript) in a Script
-                if let Some(push_bytes) = script_sig.last_pushdata() {
+                if let Some(Push::Data(push_bytes)) = script_sig.last_pushdata() {
                     Script::from_bytes(push_bytes.as_bytes())
                 } else {
                     return 0;
@@ -1024,15 +1025,11 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for Version {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Decodable::consensus_decode(r).map(Version)
     }
 }
 
-impl fmt::Display for Version {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
-}
-
 impl_consensus_encoding!(TxOut, value, script_pubkey);
 
 impl Encodable for OutPoint {
@@ -1042,7 +1039,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
     }
 }
 impl Decodable for OutPoint {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(OutPoint {
             txid: Decodable::consensus_decode(r)?,
             vout: Decodable::consensus_decode(r)?,
@@ -1061,7 +1058,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 impl Decodable for TxIn {
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         r: &mut R,
     ) -> Result<Self, encode::Error> {
         Ok(TxIn {
@@ -1080,7 +1077,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for Sequence {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Decodable::consensus_decode(r).map(Sequence)
     }
 }
@@ -1110,7 +1107,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for Transaction {
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         r: &mut R,
     ) -> Result<Self, encode::Error> {
         let version = Version::consensus_decode_from_finite_reader(r)?;
@@ -1181,7 +1178,7 @@ fn from(tx: &Transaction) -> Wtxid { tx.wtxid() }
 ///   of the to-be-constructed transaction.
 ///
 /// Note that lengths of the scripts and witness elements must be non-serialized, IOW *without* the
-/// preceding compact size. The length of preceding compact size is computed and added inside the
+/// preceding compact size. The lenght of preceding compact size is computed and added inside the
 /// function for convenience.
 ///
 /// If you  have the transaction already constructed (except for signatures) with a dummy value for
@@ -1932,8 +1929,7 @@ fn transaction_verify() {
         let mut witness: Vec<_> = spending.input[1].witness.to_vec();
         witness[0][10] = 42;
         spending.input[1].witness = Witness::from_slice(&witness);
-
-        let error = spending
+        match spending
             .verify(|point: &OutPoint| {
                 if let Some(tx) = spent3.remove(&point.txid) {
                     return tx.output.get(point.vout as usize).cloned();
@@ -1941,9 +1937,8 @@ fn transaction_verify() {
                 None
             })
             .err()
-            .unwrap();
-
-        match error {
+            .unwrap()
+        {
             TxVerifyError::ScriptVerification(_) => {}
             _ => panic!("Wrong error type"),
         }
diff --git a/bitcoin/src/blockdata/weight.rs b/bitcoin/src/blockdata/weight.rs
index 3fad1a1c..b33360ff 100644
--- a/bitcoin/src/blockdata/weight.rs
+++ b/bitcoin/src/blockdata/weight.rs
@@ -67,8 +67,6 @@ pub const fn from_vb_unwrap(vb: u64) -> Weight {
                 #[allow(unconditional_panic)]
                 // disabling this lint until panic!() can be used.
                 #[allow(clippy::let_unit_value)]
-                // disabling this lint until panic!() can be used.
-                #[allow(clippy::out_of_bounds_indexing)]
                 let _int_overflow_scaling_weight = [(); 0][1];
                 Weight(0)
             }
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index daf744a7..777452c9 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -8,7 +8,7 @@
 use core::fmt;
 use core::ops::Index;
 
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::consensus::encode::{Error, MAX_VEC_SIZE};
 use crate::consensus::{Decodable, Encodable, WriteExt};
@@ -124,7 +124,7 @@ pub struct Iter<'a> {
 }
 
 impl Decodable for Witness {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let witness_elements = VarInt::consensus_decode(r)?.0 as usize;
         // Minimum size of witness element is 1 byte, so if the count is
         // greater than MAX_VEC_SIZE we must return an error.
@@ -637,9 +637,9 @@ fn test_push_ecdsa_sig() {
         // The very first signature in block 734,958
         let sig_bytes =
             hex!("304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b541");
-        let signature = secp256k1::ecdsa::Signature::from_der(&sig_bytes).unwrap();
+        let sig = secp256k1::ecdsa::Signature::from_der(&sig_bytes).unwrap();
         let mut witness = Witness::default();
-        let signature = crate::ecdsa::Signature { signature, sighash_type: EcdsaSighashType::All };
+        let signature = crate::ecdsa::Signature { sig, hash_ty: EcdsaSighashType::All };
         witness.push_ecdsa_signature(&signature);
         let expected_witness = vec![hex!(
             "304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b54101")
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index 0e3e43d8..6017300c 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -20,7 +20,7 @@
 
 use hashes::{sha256, sha256d, Hash};
 use internals::write_err;
-use io::{Cursor, Read, Write};
+use io::{Cursor, BufRead, Read, Write};
 
 use crate::bip152::{PrefilledTransaction, ShortId};
 use crate::bip158::{FilterHash, FilterHeader};
@@ -301,7 +301,7 @@ pub trait Decodable: Sized {
     ///   avoid creating redundant `Take` wrappers. Failure to do so might result only in a tiny
     ///   performance hit.
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         reader: &mut R,
     ) -> Result<Self, Error> {
         // This method is always strictly less general than, `consensus_decode`, so it's safe and
@@ -319,7 +319,7 @@ fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
     /// for types that override [`Self::consensus_decode_from_finite_reader`]
     /// instead.
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(reader: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(reader: &mut R) -> Result<Self, Error> {
         Self::consensus_decode_from_finite_reader(&mut reader.take(MAX_VEC_SIZE as u64))
     }
 }
@@ -357,7 +357,7 @@ macro_rules! impl_int_encodable {
     ($ty:ident, $meth_dec:ident, $meth_enc:ident) => {
         impl Decodable for $ty {
             #[inline]
-            fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+            fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
                 ReadExt::$meth_dec(r)
             }
         }
@@ -440,7 +440,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for VarInt {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let n = ReadExt::read_u8(r)?;
         match n {
             0xFF => {
@@ -482,7 +482,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for bool {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<bool, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<bool, Error> {
         ReadExt::read_bool(r)
     }
 }
@@ -499,7 +499,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for String {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<String, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<String, Error> {
         String::from_utf8(Decodable::consensus_decode(r)?)
             .map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
     }
@@ -517,7 +517,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for Cow<'static, str> {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Cow<'static, str>, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Cow<'static, str>, Error> {
         String::from_utf8(Decodable::consensus_decode(r)?)
             .map_err(|_| self::Error::ParseFailed("String was not valid UTF8"))
             .map(Cow::Owned)
@@ -539,7 +539,7 @@ fn consensus_encode<W: WriteExt + ?Sized>(
 
         impl Decodable for [u8; $size] {
             #[inline]
-            fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+            fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
                 let mut ret = [0; $size];
                 r.read_slice(&mut ret)?;
                 Ok(ret)
@@ -560,7 +560,7 @@ fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
 
 impl Decodable for [u16; 8] {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let mut res = [0; 8];
         for item in &mut res {
             *item = Decodable::consensus_decode(r)?;
@@ -595,7 +595,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
         impl Decodable for Vec<$type> {
             #[inline]
-            fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+            fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
                 r: &mut R,
             ) -> Result<Self, Error> {
                 let len = VarInt::consensus_decode_from_finite_reader(r)?.0;
@@ -685,7 +685,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for Vec<u8> {
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let len = VarInt::consensus_decode(r)?.0 as usize;
         // most real-world vec of bytes data, wouldn't be larger than 128KiB
         let opts = ReadBytesFromFiniteReaderOpts { len, chunk_size: 128 * 1024 };
@@ -702,7 +702,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for Box<[u8]> {
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         <Vec<u8>>::consensus_decode_from_finite_reader(r).map(From::from)
     }
 }
@@ -727,7 +727,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for CheckedData {
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let len = u32::consensus_decode_from_finite_reader(r)? as usize;
 
         let checksum = <[u8; 4]>::consensus_decode_from_finite_reader(r)?;
@@ -787,7 +787,7 @@ fn consensus_encode<W: Write + ?Sized>(
         impl<$($x: Decodable),*> Decodable for ($($x),*) {
             #[inline]
             #[allow(non_snake_case)]
-            fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+            fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
                 Ok(($({let $x = Decodable::consensus_decode(r)?; $x }),*))
             }
         }
@@ -809,7 +809,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for sha256d::Hash {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
     }
 }
@@ -821,7 +821,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for sha256::Hash {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
     }
 }
@@ -833,7 +833,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for TapLeafHash {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         Ok(Self::from_byte_array(<<Self as Hash>::Bytes>::consensus_decode(r)?))
     }
 }
diff --git a/bitcoin/src/consensus/serde.rs b/bitcoin/src/consensus/serde.rs
index 7f94341b..9a8d66cc 100644
--- a/bitcoin/src/consensus/serde.rs
+++ b/bitcoin/src/consensus/serde.rs
@@ -12,7 +12,7 @@
 use core::fmt;
 use core::marker::PhantomData;
 
-use io::{Read, Write};
+use io::{BufRead, Read, Write};
 use serde::de::{SeqAccess, Unexpected, Visitor};
 use serde::ser::SerializeSeq;
 use serde::{Deserializer, Serializer};
@@ -420,11 +420,12 @@ fn into_de_error<DE: serde::de::Error>(self) -> DE {
 
 struct IterReader<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> {
     iterator: core::iter::Fuse<I>,
+    buf: Option<u8>,
     error: Option<E>,
 }
 
 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 new(iterator: I) -> Self { IterReader { iterator: iterator.fuse(), buf: None, error: None } }
 
     fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
         let result = T::consensus_decode(&mut self);
@@ -443,8 +444,17 @@ fn decode<T: Decodable>(mut self) -> Result<T, DecodeError<E>> {
 }
 
 impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> Read for IterReader<E, I> {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+    fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
         let mut count = 0;
+        if buf.is_empty() {
+            return Ok(0);
+        }
+
+        if let Some(first) = self.buf.take() {
+            buf[0] = first;
+            buf = &mut buf[1..];
+            count += 1;
+        }
         for (dst, src) in buf.iter_mut().zip(&mut self.iterator) {
             match src {
                 Ok(byte) => *dst = byte,
@@ -460,6 +470,34 @@ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
     }
 }
 
+impl<E: fmt::Debug, I: Iterator<Item = Result<u8, E>>> BufRead for IterReader<E, I> {
+    fn fill_buf(&mut self) -> Result<&[u8], io::Error> {
+        // matching on reference rather than using `ref` confuses borrow checker
+        if let Some(ref byte) = self.buf {
+            Ok(core::slice::from_ref(byte))
+        } else {
+            match self.iterator.next() {
+                Some(Ok(byte)) => {
+                    self.buf = Some(byte);
+                    Ok(core::slice::from_ref(self.buf.as_ref().expect("we've just filled it")))
+                },
+                Some(Err(error)) => {
+                    self.error = Some(error);
+                    Err(io::ErrorKind::Other.into())
+                },
+                None => Ok(&[]),
+            }
+        }
+    }
+
+    fn consume(&mut self, len: usize) {
+        debug_assert!(len <= 1);
+        if len > 0 {
+            self.buf = None;
+        }
+    }
+}
+
 /// Helper for `#[serde(with = "")]`.
 ///
 /// To (de)serialize a field using consensus encoding you can write e.g.:
diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs
index 2ce06002..14251fac 100644
--- a/bitcoin/src/crypto/ecdsa.rs
+++ b/bitcoin/src/crypto/ecdsa.rs
@@ -22,24 +22,24 @@
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
 pub struct Signature {
-    /// The underlying ECDSA Signature.
-    pub signature: secp256k1::ecdsa::Signature,
-    /// The corresponding hash type.
-    pub sighash_type: EcdsaSighashType,
+    /// The underlying ECDSA Signature
+    pub sig: secp256k1::ecdsa::Signature,
+    /// The corresponding hash type
+    pub hash_ty: EcdsaSighashType,
 }
 
 impl Signature {
     /// Constructs an ECDSA Bitcoin signature for [`EcdsaSighashType::All`].
-    pub fn sighash_all(signature: secp256k1::ecdsa::Signature) -> Signature {
-        Signature { signature, sighash_type: EcdsaSighashType::All }
+    pub fn sighash_all(sig: secp256k1::ecdsa::Signature) -> Signature {
+        Signature { sig, hash_ty: EcdsaSighashType::All }
     }
 
     /// Deserializes from slice following the standardness rules for [`EcdsaSighashType`].
     pub fn from_slice(sl: &[u8]) -> Result<Self, Error> {
-        let (sighash_type, sig) = sl.split_last().ok_or(Error::EmptySignature)?;
-        let sighash_type = EcdsaSighashType::from_standard(*sighash_type as u32)?;
-        let signature = secp256k1::ecdsa::Signature::from_der(sig).map_err(Error::Secp256k1)?;
-        Ok(Signature { signature, sighash_type })
+        let (hash_ty, sig) = sl.split_last().ok_or(Error::EmptySignature)?;
+        let hash_ty = EcdsaSighashType::from_standard(*hash_ty as u32)?;
+        let sig = secp256k1::ecdsa::Signature::from_der(sig).map_err(Error::Secp256k1)?;
+        Ok(Signature { sig, hash_ty })
     }
 
     /// Serializes an ECDSA signature (inner secp256k1 signature in DER format).
@@ -47,9 +47,9 @@ pub fn from_slice(sl: &[u8]) -> Result<Self, Error> {
     /// This does **not** perform extra heap allocation.
     pub fn serialize(&self) -> SerializedSignature {
         let mut buf = [0u8; MAX_SIG_LEN];
-        let signature = self.signature.serialize_der();
+        let signature = self.sig.serialize_der();
         buf[..signature.len()].copy_from_slice(&signature);
-        buf[signature.len()] = self.sighash_type as u8;
+        buf[signature.len()] = self.hash_ty as u8;
         SerializedSignature { data: buf, len: signature.len() + 1 }
     }
 
@@ -59,19 +59,14 @@ pub fn serialize(&self) -> SerializedSignature {
     /// [`serialize`](Self::serialize) method instead.
     pub fn to_vec(self) -> Vec<u8> {
         // TODO: add support to serialize to a writer to SerializedSig
-        self.signature
-            .serialize_der()
-            .iter()
-            .copied()
-            .chain(iter::once(self.sighash_type as u8))
-            .collect()
+        self.sig.serialize_der().iter().copied().chain(iter::once(self.hash_ty as u8)).collect()
     }
 }
 
 impl fmt::Display for Signature {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::LowerHex::fmt(&self.signature.serialize_der().as_hex(), f)?;
-        fmt::LowerHex::fmt(&[self.sighash_type as u8].as_hex(), f)
+        fmt::LowerHex::fmt(&self.sig.serialize_der().as_hex(), f)?;
+        fmt::LowerHex::fmt(&[self.hash_ty as u8].as_hex(), f)
     }
 }
 
@@ -82,8 +77,8 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
         let bytes = Vec::from_hex(s)?;
         let (sighash_byte, signature) = bytes.split_last().ok_or(Error::EmptySignature)?;
         Ok(Signature {
-            signature: secp256k1::ecdsa::Signature::from_der(signature)?,
-            sighash_type: EcdsaSighashType::from_standard(*sighash_byte as u32)?,
+            sig: secp256k1::ecdsa::Signature::from_der(signature)?,
+            hash_ty: EcdsaSighashType::from_standard(*sighash_byte as u32)?,
         })
     }
 }
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index f5ac68d7..9ee810cc 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -202,7 +202,7 @@ pub fn verify<C: secp256k1::Verification>(
         msg: &secp256k1::Message,
         sig: &ecdsa::Signature,
     ) -> Result<(), Error> {
-        Ok(secp.verify_ecdsa(msg, &sig.signature, &self.inner)?)
+        Ok(secp.verify_ecdsa(msg, &sig.sig, &self.inner)?)
     }
 }
 
@@ -321,7 +321,7 @@ pub fn verify<C: secp256k1::Verification>(
         msg: &secp256k1::Message,
         sig: &ecdsa::Signature,
     ) -> Result<(), Error> {
-        Ok(secp.verify_ecdsa(msg, &sig.signature, &self.0)?)
+        Ok(secp.verify_ecdsa(msg, &sig.sig, &self.0)?)
     }
 }
 
diff --git a/bitcoin/src/crypto/sighash.rs b/bitcoin/src/crypto/sighash.rs
index 4ed1cf53..4b805281 100644
--- a/bitcoin/src/crypto/sighash.rs
+++ b/bitcoin/src/crypto/sighash.rs
@@ -586,10 +586,10 @@ pub(crate) fn split_anyonecanpay_flag(self) -> (TapSighashType, bool) {
     }
 
     /// Constructs a [`TapSighashType`] from a raw `u8`.
-    pub fn from_consensus_u8(sighash_type: u8) -> Result<Self, InvalidSighashTypeError> {
+    pub fn from_consensus_u8(hash_ty: u8) -> Result<Self, InvalidSighashTypeError> {
         use TapSighashType::*;
 
-        Ok(match sighash_type {
+        Ok(match hash_ty {
             0x00 => Default,
             0x01 => All,
             0x02 => None,
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index c0a1a2d7..ca771c8c 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -18,10 +18,10 @@
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
 pub struct Signature {
-    /// The underlying schnorr signature.
-    pub signature: secp256k1::schnorr::Signature,
-    /// The corresponding hash type.
-    pub sighash_type: TapSighashType,
+    /// The underlying schnorr signature
+    pub sig: secp256k1::schnorr::Signature,
+    /// The corresponding hash type
+    pub hash_ty: TapSighashType,
 }
 
 impl Signature {
@@ -30,14 +30,14 @@ pub fn from_slice(sl: &[u8]) -> Result<Self, SigFromSliceError> {
         match sl.len() {
             64 => {
                 // default type
-                let signature = secp256k1::schnorr::Signature::from_slice(sl)?;
-                Ok(Signature { signature, sighash_type: TapSighashType::Default })
+                let sig = secp256k1::schnorr::Signature::from_slice(sl)?;
+                Ok(Signature { sig, hash_ty: TapSighashType::Default })
             }
             65 => {
-                let (sighash_type, signature) = sl.split_last().expect("Slice len checked == 65");
-                let sighash_type = TapSighashType::from_consensus_u8(*sighash_type)?;
-                let signature = secp256k1::schnorr::Signature::from_slice(signature)?;
-                Ok(Signature { signature, sighash_type })
+                let (hash_ty, sig) = sl.split_last().expect("Slice len checked == 65");
+                let hash_ty = TapSighashType::from_consensus_u8(*hash_ty)?;
+                let sig = secp256k1::schnorr::Signature::from_slice(sig)?;
+                Ok(Signature { sig, hash_ty })
             }
             len => Err(SigFromSliceError::InvalidSignatureSize(len)),
         }
@@ -48,11 +48,11 @@ pub fn from_slice(sl: &[u8]) -> Result<Self, SigFromSliceError> {
     /// Note: this allocates on the heap, prefer [`serialize`](Self::serialize) if vec is not needed.
     pub fn to_vec(self) -> Vec<u8> {
         // TODO: add support to serialize to a writer to SerializedSig
-        let mut ser_sig = self.signature.as_ref().to_vec();
-        if self.sighash_type == TapSighashType::Default {
+        let mut ser_sig = self.sig.as_ref().to_vec();
+        if self.hash_ty == TapSighashType::Default {
             // default sighash type, don't add extra sighash byte
         } else {
-            ser_sig.push(self.sighash_type as u8);
+            ser_sig.push(self.hash_ty as u8);
         }
         ser_sig
     }
@@ -63,13 +63,13 @@ pub fn to_vec(self) -> Vec<u8> {
     /// You can get a slice from it using deref coercions or turn it into an iterator.
     pub fn serialize(self) -> SerializedSignature {
         let mut buf = [0; serialized_signature::MAX_LEN];
-        let ser_sig = self.signature.serialize();
+        let ser_sig = self.sig.serialize();
         buf[..64].copy_from_slice(&ser_sig);
-        let len = if self.sighash_type == TapSighashType::Default {
+        let len = if self.hash_ty == TapSighashType::Default {
             // default sighash type, don't add extra sighash byte
             64
         } else {
-            buf[64] = self.sighash_type as u8;
+            buf[64] = self.hash_ty as u8;
             65
         };
         SerializedSignature::from_raw_parts(buf, len)
diff --git a/bitcoin/src/internal_macros.rs b/bitcoin/src/internal_macros.rs
index 669c3ff6..62b67aa3 100644
--- a/bitcoin/src/internal_macros.rs
+++ b/bitcoin/src/internal_macros.rs
@@ -22,7 +22,7 @@ fn consensus_encode<R: $crate::io::Write + ?Sized>(
         impl $crate::consensus::Decodable for $thing {
 
             #[inline]
-            fn consensus_decode_from_finite_reader<R: $crate::io::Read + ?Sized>(
+            fn consensus_decode_from_finite_reader<R: $crate::io::BufRead + ?Sized>(
                 r: &mut R,
             ) -> Result<$thing, $crate::consensus::encode::Error> {
                 Ok($thing {
@@ -31,7 +31,7 @@ fn consensus_decode_from_finite_reader<R: $crate::io::Read + ?Sized>(
             }
 
             #[inline]
-            fn consensus_decode<R: $crate::io::Read + ?Sized>(
+            fn consensus_decode<R: $crate::io::BufRead + ?Sized>(
                 r: &mut R,
             ) -> Result<$thing, $crate::consensus::encode::Error> {
                 let mut r = r.take($crate::consensus::encode::MAX_VEC_SIZE as u64);
@@ -202,7 +202,7 @@ fn consensus_encode<W: $crate::io::Write + ?Sized>(&self, w: &mut W) -> Result<u
         }
 
         impl $crate::consensus::Decodable for $hashtype {
-            fn consensus_decode<R: $crate::io::Read + ?Sized>(r: &mut R) -> Result<Self, $crate::consensus::encode::Error> {
+            fn consensus_decode<R: $crate::io::BufRead + ?Sized>(r: &mut R) -> Result<Self, $crate::consensus::encode::Error> {
                 use $crate::hashes::Hash;
                 Ok(Self::from_byte_array(<<$hashtype as $crate::hashes::Hash>::Bytes>::consensus_decode(r)?))
             }
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index 25265c8d..7411bc53 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -38,9 +38,6 @@
 #![cfg_attr(fuzzing, allow(dead_code, unused_imports))]
 // Exclude clippy lints we don't think are valuable
 #![allow(clippy::needless_question_mark)] // https://github.com/rust-bitcoin/rust-bitcoin/pull/2134
-#![allow(clippy::uninhabited_references)] // falsely claims that 100% safe code is UB
-#![allow(clippy::manual_range_contains)] // more readable than clippy's format
-#![allow(clippy::unconditional_recursion)] // broken; see https://github.com/rust-lang/rust-clippy/issues/12133
 
 // Disable 16-bit support at least for now as we can't guarantee it yet.
 #[cfg(target_pointer_width = "16")]
@@ -62,7 +59,7 @@
 /// Encodes and decodes the Bech32 forrmat.
 pub extern crate bech32;
 
-/// Rust implementation of cryptographic hash function algorithms.
+/// Rust implementation of cryptographic hash function algorithems.
 pub extern crate hashes;
 
 /// Re-export the `hex-conservative` crate.
@@ -71,10 +68,6 @@
 /// Re-export the `bitcoin-io` crate.
 pub extern crate io;
 
-/// Re-export the `ordered` crate.
-#[cfg(feature = "ordered")]
-pub extern crate ordered;
-
 /// Rust wrapper library for Pieter Wuille's libsecp256k1.  Implements ECDSA and BIP 340 signatures
 /// for the SECG elliptic curve group secp256k1 and related utilities.
 pub extern crate secp256k1;
@@ -172,7 +165,7 @@ pub mod amount {
     //! We refer to the documentation on the types for more information.
 
     use crate::consensus::{encode, Decodable, Encodable};
-    use crate::io;
+    use crate::io::{BufRead, Write};
 
     #[rustfmt::skip]            // Keep public re-exports separate.
     #[doc(inline)]
@@ -184,14 +177,14 @@ pub mod amount {
 
     impl Decodable for Amount {
         #[inline]
-        fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+        fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
             Ok(Amount::from_sat(Decodable::consensus_decode(r)?))
         }
     }
 
     impl Encodable for Amount {
         #[inline]
-        fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
+        fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
             self.to_sat().consensus_encode(w)
         }
     }
diff --git a/bitcoin/src/merkle_tree/block.rs b/bitcoin/src/merkle_tree/block.rs
index 3194454a..402ffaab 100644
--- a/bitcoin/src/merkle_tree/block.rs
+++ b/bitcoin/src/merkle_tree/block.rs
@@ -41,7 +41,7 @@
 use core::fmt;
 
 use hashes::Hash;
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use self::MerkleBlockError::*;
 use crate::blockdata::block::{self, Block, TxMerkleNode};
@@ -151,7 +151,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for MerkleBlock {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(MerkleBlock {
             header: Decodable::consensus_decode(r)?,
             txn: Decodable::consensus_decode(r)?,
@@ -452,7 +452,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for PartialMerkleTree {
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         r: &mut R,
     ) -> Result<Self, encode::Error> {
         let num_transactions: u32 = Decodable::consensus_decode(r)?;
diff --git a/bitcoin/src/p2p/address.rs b/bitcoin/src/p2p/address.rs
index 014cadd4..e1a69d61 100644
--- a/bitcoin/src/p2p/address.rs
+++ b/bitcoin/src/p2p/address.rs
@@ -9,7 +9,7 @@
 use core::{fmt, iter};
 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
 
-use io::{Read, Write};
+use io::{BufRead, Read, Write};
 
 use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, VarInt, WriteExt};
 use crate::p2p::ServiceFlags;
@@ -75,7 +75,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for Address {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(Address {
             services: Decodable::consensus_decode(r)?,
             address: read_be_address(r)?,
@@ -167,7 +167,7 @@ fn encode_addr<W: Write + ?Sized>(
 }
 
 impl Decodable for AddrV2 {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let network_id = u8::consensus_decode(r)?;
         let len = VarInt::consensus_decode(r)?.0;
         if len > 512 {
@@ -285,7 +285,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for AddrV2Message {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(AddrV2Message {
             time: Decodable::consensus_decode(r)?,
             services: ServiceFlags::from(VarInt::consensus_decode(r)?.0),
diff --git a/bitcoin/src/p2p/message.rs b/bitcoin/src/p2p/message.rs
index 2c8dc699..a4343d1f 100644
--- a/bitcoin/src/p2p/message.rs
+++ b/bitcoin/src/p2p/message.rs
@@ -9,7 +9,7 @@
 use core::{fmt, iter};
 
 use hashes::{sha256d, Hash};
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::blockdata::{block, transaction};
 use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};
@@ -110,7 +110,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for CommandString {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let rawbytes: [u8; 12] = Decodable::consensus_decode(r)?;
         let rv = iter::FromIterator::from_iter(rawbytes.iter().filter_map(|&u| {
             if u > 0 {
@@ -407,7 +407,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for HeaderDeserializationWrapper {
     #[inline]
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         r: &mut R,
     ) -> Result<Self, encode::Error> {
         let len = VarInt::consensus_decode(r)?.0;
@@ -426,13 +426,13 @@ fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
     }
 
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE as u64))
     }
 }
 
 impl Decodable for RawNetworkMessage {
-    fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
+    fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
         r: &mut R,
     ) -> Result<Self, encode::Error> {
         let magic = Decodable::consensus_decode_from_finite_reader(r)?;
@@ -442,7 +442,7 @@ fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
         let raw_payload = checked_data.into_data();
         let payload_len = raw_payload.len() as u32;
 
-        let mut mem_d = io::Cursor::new(raw_payload);
+        let mut mem_d = raw_payload.as_slice();
         let payload = match &cmd.0[..] {
             "version" =>
                 NetworkMessage::Version(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
@@ -525,13 +525,13 @@ fn consensus_decode_from_finite_reader<R: Read + ?Sized>(
             "addrv2" =>
                 NetworkMessage::AddrV2(Decodable::consensus_decode_from_finite_reader(&mut mem_d)?),
             "sendaddrv2" => NetworkMessage::SendAddrV2,
-            _ => NetworkMessage::Unknown { command: cmd, payload: mem_d.into_inner() },
+            _ => NetworkMessage::Unknown { command: cmd, payload: raw_payload },
         };
         Ok(RawNetworkMessage { magic, payload, payload_len, checksum })
     }
 
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE as u64))
     }
 }
diff --git a/bitcoin/src/p2p/message_blockdata.rs b/bitcoin/src/p2p/message_blockdata.rs
index 550029d1..f1a142d5 100644
--- a/bitcoin/src/p2p/message_blockdata.rs
+++ b/bitcoin/src/p2p/message_blockdata.rs
@@ -7,7 +7,7 @@
 //!
 
 use hashes::{sha256d, Hash as _};
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::blockdata::block::BlockHash;
 use crate::blockdata::transaction::{Txid, Wtxid};
@@ -83,7 +83,7 @@ macro_rules! encode_inv {
 
 impl Decodable for Inventory {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let inv_type: u32 = Decodable::consensus_decode(r)?;
         Ok(match inv_type {
             0 => Inventory::Error,
diff --git a/bitcoin/src/p2p/message_bloom.rs b/bitcoin/src/p2p/message_bloom.rs
index 3e89c933..cb04fca7 100644
--- a/bitcoin/src/p2p/message_bloom.rs
+++ b/bitcoin/src/p2p/message_bloom.rs
@@ -5,7 +5,7 @@
 //! This module describes BIP37 Connection Bloom filtering network messages.
 //!
 
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::consensus::{encode, Decodable, Encodable, ReadExt};
 use crate::internal_macros::impl_consensus_encoding;
@@ -48,7 +48,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for BloomFlags {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(match r.read_u8()? {
             0 => BloomFlags::None,
             1 => BloomFlags::All,
diff --git a/bitcoin/src/p2p/message_network.rs b/bitcoin/src/p2p/message_network.rs
index 29b42fe6..6ad59b5b 100644
--- a/bitcoin/src/p2p/message_network.rs
+++ b/bitcoin/src/p2p/message_network.rs
@@ -7,7 +7,7 @@
 //!
 
 use hashes::sha256d;
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::consensus::{encode, Decodable, Encodable, ReadExt};
 use crate::internal_macros::impl_consensus_encoding;
@@ -32,13 +32,6 @@ pub struct VersionMessage {
     /// The network address of the peer sending the message
     pub sender: Address,
     /// A random nonce used to detect loops in the network
-    ///
-    /// The nonce can be used to detect situations when a node accidentally
-    /// connects to itself. Set it to a random value and, in case of incoming
-    /// connections, compare the value - same values mean self-connection.
-    ///
-    /// If your application uses P2P to only fetch the data and doesn't listen
-    /// you may just set it to 0.
     pub nonce: u64,
     /// A string describing the peer's software
     pub user_agent: String,
@@ -117,7 +110,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 }
 
 impl Decodable for RejectReason {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(match r.read_u8()? {
             0x01 => RejectReason::Malformed,
             0x10 => RejectReason::Invalid,
diff --git a/bitcoin/src/p2p/mod.rs b/bitcoin/src/p2p/mod.rs
index 0c0381fe..1a450375 100644
--- a/bitcoin/src/p2p/mod.rs
+++ b/bitcoin/src/p2p/mod.rs
@@ -25,7 +25,7 @@
 
 use hex::FromHex;
 use internals::{debug_from_display, write_err};
-use io::{Read, Write};
+use io::{BufRead, Write};
 
 use crate::consensus::encode::{self, Decodable, Encodable};
 use crate::prelude::{Borrow, BorrowMut, String, ToOwned};
@@ -198,7 +198,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for ServiceFlags {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(ServiceFlags(Decodable::consensus_decode(r)?))
     }
 }
@@ -290,7 +290,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, writer: &mut W) -> Result<usize, i
 }
 
 impl Decodable for Magic {
-    fn consensus_decode<R: Read + ?Sized>(reader: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(reader: &mut R) -> Result<Self, encode::Error> {
         Ok(Magic(Decodable::consensus_decode(reader)?))
     }
 }
diff --git a/bitcoin/src/pow.rs b/bitcoin/src/pow.rs
index 68afaab4..20bab1db 100644
--- a/bitcoin/src/pow.rs
+++ b/bitcoin/src/pow.rs
@@ -9,7 +9,7 @@
 use core::fmt::{self, LowerHex, UpperHex};
 use core::ops::{Add, Div, Mul, Not, Rem, Shl, Shr, Sub};
 
-use io::{Read, Write};
+use io::{BufRead, Write};
 #[cfg(all(test, mutate))]
 use mutagen::mutate;
 
@@ -301,7 +301,7 @@ fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Er
 
 impl Decodable for CompactTarget {
     #[inline]
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         u32::consensus_decode(r).map(CompactTarget)
     }
 }
@@ -1676,13 +1676,13 @@ fn u256_max_min_inverse_roundtrip() {
     }
 
     #[test]
-    fn u256_wrapping_add_wraps_at_boundary() {
+    fn u256_wrapping_add_wraps_at_boundry() {
         assert_eq!(U256::MAX.wrapping_add(U256::ONE), U256::ZERO);
         assert_eq!(U256::MAX.wrapping_add(U256::from(2_u8)), U256::ONE);
     }
 
     #[test]
-    fn u256_wrapping_sub_wraps_at_boundary() {
+    fn u256_wrapping_sub_wraps_at_boundry() {
         assert_eq!(U256::ZERO.wrapping_sub(U256::ONE), U256::MAX);
         assert_eq!(U256::ONE.wrapping_sub(U256::from(2_u8)), U256::MAX);
     }
diff --git a/bitcoin/src/psbt/macros.rs b/bitcoin/src/psbt/macros.rs
index f07e5bbc..33deb634 100644
--- a/bitcoin/src/psbt/macros.rs
+++ b/bitcoin/src/psbt/macros.rs
@@ -56,7 +56,7 @@ fn deserialize(bytes: &[u8]) -> Result<Self, $crate::psbt::Error> {
 macro_rules! impl_psbtmap_decoding {
     ($thing:ty) => {
         impl $thing {
-            pub(crate) fn decode<R: $crate::io::Read + ?Sized>(
+            pub(crate) fn decode<R: $crate::io::BufRead + ?Sized>(
                 r: &mut R,
             ) -> Result<Self, $crate::psbt::Error> {
                 let mut rv: Self = core::default::Default::default();
diff --git a/bitcoin/src/psbt/map/global.rs b/bitcoin/src/psbt/map/global.rs
index 7803df2b..8de2ea4b 100644
--- a/bitcoin/src/psbt/map/global.rs
+++ b/bitcoin/src/psbt/map/global.rs
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: CC0-1.0
 
-use io::{Cursor, Read};
+use io::{Cursor, BufRead, Read};
 
 use crate::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpub};
 use crate::blockdata::transaction::Transaction;
@@ -70,7 +70,7 @@ fn get_pairs(&self) -> Vec<raw::Pair> {
 }
 
 impl Psbt {
-    pub(crate) fn decode_global<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    pub(crate) fn decode_global<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let mut r = r.take(MAX_VEC_SIZE as u64);
         let mut tx: Option<Transaction> = None;
         let mut version: Option<u32> = None;
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index 13943728..4581e770 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -371,10 +371,8 @@ fn bip32_sign_ecdsa<C, K, T>(
                 Ok((msg, sighash_ty)) => (msg, sighash_ty),
             };
 
-            let sig = ecdsa::Signature {
-                signature: secp.sign_ecdsa(&msg, &sk.inner),
-                sighash_type: sighash_ty,
-            };
+            let sig =
+                ecdsa::Signature { sig: secp.sign_ecdsa(&msg, &sk.inner), hash_ty: sighash_ty };
 
             let pk = sk.public_key(secp);
 
@@ -2089,7 +2087,7 @@ fn sign_psbt() {
         psbt.inputs[0].bip32_derivation = map;
 
         // Second input is unspendable by us e.g., from another wallet that supports future upgrades.
-        let unknown_prog = WitnessProgram::new(WitnessVersion::V4, &[0xaa; 34]).unwrap();
+        let unknown_prog = WitnessProgram::new(WitnessVersion::V4, vec![0xaa; 34]).unwrap();
         let txout_unknown_future = TxOut {
             value: Amount::from_sat(10),
             script_pubkey: ScriptBuf::new_witness_program(&unknown_prog),
diff --git a/bitcoin/src/psbt/raw.rs b/bitcoin/src/psbt/raw.rs
index 5f753495..eec5cf5a 100644
--- a/bitcoin/src/psbt/raw.rs
+++ b/bitcoin/src/psbt/raw.rs
@@ -8,7 +8,7 @@
 
 use core::fmt;
 
-use io::{Read, Write};
+use io::{BufRead, Read, Write};
 
 use super::serialize::{Deserialize, Serialize};
 use crate::consensus::encode::{
@@ -74,7 +74,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 }
 
 impl Key {
-    pub(crate) fn decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    pub(crate) fn decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         let VarInt(byte_size): VarInt = Decodable::consensus_decode(r)?;
 
         if byte_size == 0 {
@@ -137,7 +137,7 @@ fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
 }
 
 impl Pair {
-    pub(crate) fn decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
+    pub(crate) fn decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
         Ok(Pair { key: Key::decode(r)?, value: Decodable::consensus_decode(r)? })
     }
 }
@@ -159,7 +159,7 @@ impl<Subtype> Decodable for ProprietaryKey<Subtype>
 where
     Subtype: Copy + From<u8> + Into<u8>,
 {
-    fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let prefix = Vec::<u8>::consensus_decode(r)?;
         let subtype = Subtype::from(r.read_u8()?);
         let key = read_to_end(r)?;
@@ -195,6 +195,7 @@ fn try_from(key: Key) -> Result<Self, Self::Error> {
     }
 }
 
+// core2 doesn't have read_to_end
 pub(crate) fn read_to_end<D: Read + ?Sized>(d: &mut D) -> Result<Vec<u8>, io::Error> {
     let mut result = vec![];
     let mut buf = [0u8; 64];
diff --git a/fuzz/cycle.sh b/fuzz/cycle.sh
index 7de15c82..0b59827a 100755
--- a/fuzz/cycle.sh
+++ b/fuzz/cycle.sh
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-# Continuously cycle over fuzz targets running each for 1 hour.
+# Continuosly cycle over fuzz targets running each for 1 hour.
 # It uses chrt SCHED_IDLE so that other process takes priority.
 #
 # For hfuzz options see https://github.com/google/honggfuzz/blob/master/docs/USAGE.md
diff --git a/hashes/README.md b/hashes/README.md
index 4ce91fa9..433a9593 100644
--- a/hashes/README.md
+++ b/hashes/README.md
@@ -2,7 +2,7 @@
 
 This is a simple, no-dependency library which implements the hash functions
 needed by Bitcoin. These are SHA1, SHA256, SHA256d, SHA512, and RIPEMD160. As an
-ancillary thing, it exposes hexadecimal serialization and deserialization,
+ancilliary thing, it exposes hexadecimal serialization and deserialization,
 since these are needed to display hashes anway.
 
 [Documentation](https://docs.rs/bitcoin_hashes/)
@@ -11,6 +11,18 @@ since these are needed to display hashes anway.
 
 This library should always compile with any combination of features on **Rust 1.56.1**.
 
+
+To build with the MSRV you will need to pin `serde` (if you have either the `serde` or the
+`schemars` feature enabled)
+
+```
+# serde 1.0.157 uses syn 2.0 which requires edition 2021
+cargo update -p serde --precise 1.0.156
+```
+
+before building. (And if your code is a library, your downstream users will need to run these
+commands, and so on.)
+
 ## Contributions
 
 Contributions are welcome, including additional hash function implementations.
diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs
index 1a613cd3..774dfdc8 100644
--- a/hashes/src/sha256t.rs
+++ b/hashes/src/sha256t.rs
@@ -101,7 +101,7 @@ fn from_engine<T: Tag>(e: sha256::HashEngine) -> Hash<T> {
 /// says the midstate should be generated by hashing the supplied string in a way described in
 /// BIP-341. Alternatively, you can supply `hash_bytes` to hash raw bytes. If you have the midstate
 /// already pre-computed and prefer **compiler** performance to readability you may use
-/// `raw(MIDSTATE_BYTES, HASHED_BYTES_LENGTH)` instead.
+/// `raw(MIDSTATE_BYTES, HASHED_BYTES_LENGHT)` instead.
 ///
 /// Both visibility modifiers and attributes are optional and passed to inner structs (excluding
 /// `#[hash_newtype(...)]`). The attributes suffer same compiler performance limitations as in
diff --git a/hashes/src/sha512_256.rs b/hashes/src/sha512_256.rs
index e5c36624..c0d4529b 100644
--- a/hashes/src/sha512_256.rs
+++ b/hashes/src/sha512_256.rs
@@ -2,7 +2,7 @@
 
 //! SHA512_256 implementation.
 //!
-//! SHA512/256 is a hash function that uses the sha512 algorithm but it truncates
+//! SHA512/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>.
@@ -16,7 +16,7 @@
 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 algorithm 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>."
+    "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>."
 }
 
 fn from_engine(e: HashEngine) -> Hash {
@@ -27,7 +27,7 @@ fn from_engine(e: HashEngine) -> Hash {
 
 /// Engine to compute SHA512/256 hash function.
 ///
-/// SHA512/256 is a hash function that uses the sha512 algorithm but it truncates
+/// SHA512/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>.
diff --git a/io/README.md b/io/README.md
deleted file mode 100644
index e38e2d06..00000000
--- a/io/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Rust-Bitcoin IO Library
-=======================
-
-The `std::io` module is not exposed in `no-std` Rust so building `no-std` applications which require
-reading and writing objects via standard traits is not generally possible. Thus, this library exists
-to export a minmal version of `std::io`'s traits which we use in `rust-bitcoin` so that we can
-support `no-std` applications.
-
-These traits are not one-for-one drop-ins, but are as close as possible while still implementing
-`std::io`'s traits without unnecessary complexity.
diff --git a/io/src/lib.rs b/io/src/lib.rs
index f820140e..12ecad4e 100644
--- a/io/src/lib.rs
+++ b/io/src/lib.rs
@@ -1,11 +1,16 @@
 //! Rust-Bitcoin IO Library
 //!
-//! The `std::io` module is not exposed in `no-std` Rust so building `no-std` applications which
-//! require reading and writing objects via standard traits is not generally possible. Thus, this
-//! library exists to export a minmal version of `std::io`'s traits which we use in `rust-bitcoin`
-//! so that we can support `no-std` applications.
+//! 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.
 //!
-//! These traits are not one-for-one drop-ins, but are as close as possible while still implementing
+//! 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.
 
 #![cfg_attr(not(feature = "std"), no_std)]
@@ -47,6 +52,19 @@ fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> {
     fn take(&mut self, limit: u64) -> Take<Self> { Take { reader: self, remaining: limit } }
 }
 
+/// A trait describing an input stream that uses an internal buffer when reading.
+pub trait BufRead: Read {
+    /// Returns data read from this reader, filling the internal buffer if needed.
+    fn fill_buf(&mut self) -> Result<&[u8]>;
+
+    /// Marks the buffered data up to amount as consumed.
+    ///
+    /// # Panics
+    ///
+    /// May panic if `amount` is greater than amount of data read by `fill_buf`.
+    fn consume(&mut self, amount: usize);
+}
+
 pub struct Take<'a, R: Read + ?Sized> {
     reader: &'a mut R,
     remaining: u64,
@@ -62,6 +80,30 @@ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
     }
 }
 
+// Impl copied from Rust stdlib.
+impl<'a, R: BufRead + ?Sized> BufRead for Take<'a, R> {
+    #[inline]
+    fn fill_buf(&mut self) -> Result<&[u8]> {
+        // Don't call into inner reader at all at EOF because it may still block
+        if self.remaining == 0 {
+            return Ok(&[]);
+        }
+
+        let buf = self.reader.fill_buf()?;
+        // Cast length to a u64 instead of casting `remaining` to a `usize`
+        // (in case `remaining > u32::MAX` and we are on a 32 bit machine).
+        let cap = cmp::min(buf.len() as u64, self.remaining) as usize;
+        Ok(&buf[..cap])
+    }
+
+    #[inline]
+    fn consume(&mut self, amount: usize) {
+        assert!(amount as u64 <= self.remaining);
+        self.remaining -= amount as u64;
+        self.reader.consume(amount);
+    }
+}
+
 #[cfg(feature = "std")]
 impl<R: std::io::Read> Read for R {
     #[inline]
@@ -70,6 +112,15 @@ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
     }
 }
 
+#[cfg(feature = "std")]
+impl<R: std::io::BufRead + Read + ?Sized> BufRead for R {
+    #[inline]
+    fn fill_buf(&mut self) -> Result<&[u8]> { Ok(std::io::BufRead::fill_buf(self)?) }
+
+    #[inline]
+    fn consume(&mut self, amount: usize) { std::io::BufRead::consume(self, amount) }
+}
+
 #[cfg(not(feature = "std"))]
 impl Read for &[u8] {
     #[inline]
@@ -81,6 +132,16 @@ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
     }
 }
 
+#[cfg(not(feature = "std"))]
+impl BufRead for &[u8] {
+    #[inline]
+    fn fill_buf(&mut self) -> Result<&[u8]> { Ok(self) }
+
+    // This panics if amount is out of bounds, same as the std version.
+    #[inline]
+    fn consume(&mut self, amount: usize) { *self = &self[amount..] }
+}
+
 pub struct Cursor<T> {
     inner: T,
     pos: u64,
@@ -110,6 +171,20 @@ fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
     }
 }
 
+impl<T: AsRef<[u8]>> BufRead for Cursor<T> {
+    #[inline]
+    fn fill_buf(&mut self) -> Result<&[u8]> {
+        let inner: &[u8] = self.inner.as_ref();
+        Ok(&inner[self.pos as usize..])
+    }
+
+    #[inline]
+    fn consume(&mut self, amount: usize) {
+        assert!(amount <= self.inner.as_ref().len());
+        self.pos += amount as u64;
+    }
+}
+
 /// 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>;
@@ -197,3 +272,30 @@ fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
 /// Returns a sink to which all writes succeed. See [`std::io::sink`] for more info.
 #[inline]
 pub fn sink() -> Sink { Sink }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn buf_read_fill_and_consume_slice() {
+        let data = [0_u8, 1, 2];
+
+        let mut slice = &data[..];
+
+        let fill = BufRead::fill_buf(&mut slice).unwrap();
+        assert_eq!(fill.len(), 3);
+        assert_eq!(fill, &[0_u8, 1, 2]);
+        slice.consume(2);
+
+        let fill = BufRead::fill_buf(&mut slice).unwrap();
+        assert_eq!(fill.len(), 1);
+        assert_eq!(fill, &[2_u8]);
+        slice.consume(1);
+
+        // checks we can attempt to read from a now-empty reader.
+        let fill = BufRead::fill_buf(&mut slice).unwrap();
+        assert_eq!(fill.len(), 0);
+        assert_eq!(fill, &[]);
+    }
+}
diff --git a/units/src/amount.rs b/units/src/amount.rs
index 9feba679..4f304a91 100644
--- a/units/src/amount.rs
+++ b/units/src/amount.rs
@@ -594,8 +594,6 @@ pub const fn from_int_btc(btc: u64) -> Amount {
                 #[allow(unconditional_panic)]
                 // disabling this lint until panic!() can be used.
                 #[allow(clippy::let_unit_value)]
-                // disabling this lint until panic!() can be used.
-                #[allow(clippy::out_of_bounds_indexing)]
                 let _int_overflow_converting_btc_to_sats = [(); 0][1];
                 Amount(0)
             }


Test patch:
diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs
index d2a970a0..88c641d4 100644
--- a/bitcoin/src/blockdata/script/tests.rs
+++ b/bitcoin/src/blockdata/script/tests.rs
@@ -176,7 +176,7 @@ fn p2pk_public_key_compressed_key_returns_some() {
 
 #[test]
 fn script_x_only_key() {
-    // Notice the "20" which prepends the keystr. That 20 is hexadecimal for "32". The Builder automatically adds the 32 opcode
+    // Notice the "20" which prepends the keystr. That 20 is hexidecimal for "32". The Builder automatically adds the 32 opcode
     // to our script in order to give a heads up to the script compiler that it should add the next 32 bytes to the stack.
     // From: https://github.com/bitcoin-core/btcdeb/blob/e8c2750c4a4702768c52d15640ed03bf744d2601/doc/tapscript-example.md?plain=1#L43
     const KEYSTR: &str = "209997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be";
diff --git a/bitcoin/tests/serde.rs b/bitcoin/tests/serde.rs
index 6112ce6d..04686a7c 100644
--- a/bitcoin/tests/serde.rs
+++ b/bitcoin/tests/serde.rs
@@ -174,8 +174,8 @@ fn serde_regression_extended_pub_key() {
 fn serde_regression_ecdsa_sig() {
     let s = include_str!("data/serde/ecdsa_sig_hex");
     let sig = ecdsa::Signature {
-        signature: secp256k1::ecdsa::Signature::from_str(s.trim()).unwrap(),
-        sighash_type: EcdsaSighashType::All,
+        sig: secp256k1::ecdsa::Signature::from_str(s.trim()).unwrap(),
+        hash_ty: EcdsaSighashType::All,
     };
 
     let got = serialize(&sig).unwrap();
@@ -348,8 +348,8 @@ fn serde_regression_proprietary_key() {
 fn serde_regression_taproot_sig() {
     let s = include_str!("data/serde/taproot_sig_hex");
     let sig = taproot::Signature {
-        signature: secp256k1::schnorr::Signature::from_str(s.trim()).unwrap(),
-        sighash_type: TapSighashType::All,
+        sig: secp256k1::schnorr::Signature::from_str(s.trim()).unwrap(),
+        hash_ty: TapSighashType::All,
     };
 
     let got = serialize(&sig).unwrap();