Repository: rust-bitcoin/rust-bitcoin
Pull request #1870 (2023-05-24 09:39:44)

Add from_int_btc method to Amount
Followup PR from https://github.com/rust-bitcoin/rust-bitcoin/pull/1811

Added a `const` associated function `from_int_btc()` for Amount.  `panic()` in const context is only available after 1.57+ so a work around is provided. 

yancyribbens: hmm this compiles fine on my local machine but I didn't notice the warning "invalid feature rust_v_1_57 .." which causes CI to fail.  Looks like this needs more work.
tcharding: Should be using conditional configuration option not a feature (that means `#[cfg(rust_v_X_Y`). There is another instance of using it as a feature in the code currently which is wrong.
apoelstra: > or panic in debug mode happen

Well, we can't panic under any circumstances because we're trying to make a `constfn`.

We could overflow in both release and debug mode, I suppose.
yancyribbens: I'm afk right now so I can't test, however I was thinking const fn will still panic in debug mode if for example `u64::max * 2`  is calculated, otherwise in release mode it silently overflows.
yancyribbens: I tested on the rust playground and I guess since it's const fn the compiler won't allow overflows to compile.  Although I'm not sure how the sementics work with const fn if user args are provided.  Maybe that's not possible.
tcharding: How about this, although it leaks the internal representation in the docs, is that acceptable?
```rust
    /// Creates an [`Amount`] from a value representing a whole number of bitcoin.
    ///
    /// Internally computes the number of satoshis (ie, `btc * 100_000_000`)
    /// saturating at the numeric bounds of `u64`.
    pub const fn saturating_from_int_btc(btc: u64) -> Amount {
        Amount(btc.saturating_mul(100_000_000))
    }
```
and a unit test
```rust
    #[test]
    fn const_saturating_from_int_btc_overflow() {
        let got = Amount::saturating_from_int_btc(u64::MAX - 10);
        let want = Amount(u64::MAX);
        assert_eq!(got, want);
    }
```
tcharding: OT: Why don't we have a const `SATS_PER_BITCOIN` instead of all the `100_000_000` in the code base?
junderw: > OT: Why don't we have a const SATS_PER_BITCOIN instead of all the 100_000_000 in the code base?

`Amount::ONE_BTC` exists... maybe use that everywhere?
junderw: > How about this

If we make a saturating one, I'd expect it to saturate to a whole BTC... maybe even 21 mil (MAX_BTC)

u64::MAX is kind of weird, since it's not a whole bitcoin.
tcharding: > > How about this
> 
> If we make a saturating one, I'd expect it to saturate to a whole BTC... maybe even 21 mil (MAX_BTC)
> 
> u64::MAX is kind of weird, since it's not a whole bitcoin.

I was under the impression that everyone knows that bitcoin (in general not just in rust-bitcoin) uses ints not floats so this behaviour is obvious but that's coming from the perspective of a low level C guy with a history of disliking floats - am I wrong?
junderw: I just think it's weird that

`184467440737` will result in `Amount(18446744073700000000)`
but
`184467440738` and above will result in `Amount(18446744073709551615)`

Since the whole point of the function is to turn `10` into `10 BTC worth of an Amount`...

But again, I think this is just personal preference, and if it's documented anything is fine.
tcharding: Cool, thanks for getting the bounds. So if I understand you clearly this leads to something like (excluding the exact name):
```rust
pub const: u64 MAX_EXPRESSIBLE_WHOLE_BITCOIN_IN_SATS = 18446744073700000000;
```

and we saturate to that. I agree with the sentiment, the reason I'd like to avoid this is because it provides an answer to the "what is the meaning of max bitcoin" question. Currently we implicitly define max bitcoin as `u64::MAX` sats and so far we have been unable to get any improvement on that, at least that is how I understood the debate.

What if, in this PR, we saturate to `Amount::MAX`, state as such in the docs, then leave the whole "what is Amount::MAX, and what does it even mean" discussion for later work on the `amount` module (which is getting a bunch of work at the moment). Would that be a good compromise?
junderw: Sounds perfect.

BTC_MAX (21 mil BTC) and MAX seems confusing though. 
apoelstra: concept ACK from me on saturating as long as it's documented. The warning should probably be in bold, and we should briefly explain that we'd like to just panic but can't in a constfn at our current MSRV.

I also think that we should explore whether we can just use `*` rather than `saturating_mul`, and if that will give us the desired "panic in debug mode, overflow in release mode, refuse to compile in a compile-time-evaluated constfn" behavior.
tcharding: You continue prodding finally tweaked my interest, it behaves as we want - see: #1883 
Kixunil: I'm not convinced saturating is that useful, we should just use the array out of bounds trick to panic in const.
yancyribbens: I also don't see why we should bother with saurating_mul.  As I understand, const context evaluates when compiled, and if there is an overflow it's better to have a compiler error and not compile instead of saturating.
yancyribbens: > I'm not convinced saturating is that useful, we should just use the array out of bounds trick to panic in const.

It looks like there is a lint that prevents the array oob trick :facepalm: 
yancyribbens: If the OOB trick isn't possible, maybe feature gate this so it's only available in 1.57+?
apoelstra: You could either disable the lint or just remove the `let` binding.
Kixunil: Just disable the lint with a comment. Removing `let` wouldn't work and would trigger another lint.
yancyribbens: Sorry for the delay, was afk for a bit
yancyribbens: > git grep _000_000

Oh nice, git grep is dope.  Or better yet, `git grep -n _000_000` so one can find the line num.
tcharding: I use a shell alias to `git grep -IPn --color=always`
yancyribbens: > There are a bunch more places this new function could be used if you get super motivated (`git grep _000_000`).

This is the `const` version, so it's really only useful for other `const` functions.
tcharding: Woops :)
yancyribbens: > A bunch of unrelated changes have snuck into the PR, can you remove them or explain them please? (The PR describtion is stale now too.)
> 
> Thanks man

Sorry about that.  It looks like I pulled in some old state from master on accident.

Patch:
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index a5f10ce0..afba03d5 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -64,9 +64,6 @@ dependencies = [
 [[package]]
 name = "bitcoin-internals"
 version = "0.1.0"
-dependencies = [
- "serde",
-]
 
 [[package]]
 name = "bitcoin_hashes"
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index 5e0c682e..ca9c366a 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -63,9 +63,6 @@ dependencies = [
 [[package]]
 name = "bitcoin-internals"
 version = "0.1.0"
-dependencies = [
- "serde",
-]
 
 [[package]]
 name = "bitcoin_hashes"
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 24c0088b..b7aaa3c5 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -16,7 +16,7 @@ exclude = ["tests", "contrib"]
 default = [ "std", "secp-recovery" ]
 rand-std = ["secp256k1/rand-std"]
 rand = ["secp256k1/rand"]
-serde = ["actual-serde", "hashes/serde", "secp256k1/serde", "internals/serde"]
+serde = ["actual-serde", "hashes/serde", "secp256k1/serde"]
 secp-lowmemory = ["secp256k1/lowmemory"]
 secp-recovery = ["secp256k1/recovery"]
 bitcoinconsensus-std = ["bitcoinconsensus/std", "std"]
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 0ad84de0..3794688d 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -49,7 +49,7 @@
     script_pubkey: UTXO_SCRIPT_PUBKEY,
     pubkey: UTXO_PUBKEY,
     master_fingerprint: UTXO_MASTER_FINGERPRINT,
-    amount_in_sats: Amount::from_sat(50 * 100_000_000), // 50 BTC
+    amount_in_sats: Amount::from_int_btc(50),
     derivation_path: BIP86_DERIVATION_PATH,
 };
 
@@ -60,7 +60,7 @@
     script_pubkey: UTXO_SCRIPT_PUBKEY,
     pubkey: UTXO_PUBKEY,
     master_fingerprint: UTXO_MASTER_FINGERPRINT,
-    amount_in_sats: Amount::from_sat(50 * 100_000_000), // 50 BTC
+    amount_in_sats: Amount::from_int_btc(50),
     derivation_path: BIP86_DERIVATION_PATH,
 };
 
@@ -71,7 +71,7 @@
     script_pubkey: UTXO_SCRIPT_PUBKEY,
     pubkey: UTXO_PUBKEY,
     master_fingerprint: UTXO_MASTER_FINGERPRINT,
-    amount_in_sats: Amount::from_sat(50 * 100_000_000), // 50 BTC
+    amount_in_sats: Amount::from_int_btc(50),
     derivation_path: BIP86_DERIVATION_PATH,
 };
 
diff --git a/bitcoin/src/address.rs b/bitcoin/src/address.rs
index 5e66a077..50b51f6f 100644
--- a/bitcoin/src/address.rs
+++ b/bitcoin/src/address.rs
@@ -148,10 +148,12 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     }
 }
 
+#[doc(hidden)]
 impl From<base58::Error> for Error {
     fn from(e: base58::Error) -> Error { Error::Base58(e) }
 }
 
+#[doc(hidden)]
 impl From<bech32::Error> for Error {
     fn from(e: bech32::Error) -> Error { Error::Bech32(e) }
 }
diff --git a/bitcoin/src/amount.rs b/bitcoin/src/amount.rs
index a8895d59..aa008b44 100644
--- a/bitcoin/src/amount.rs
+++ b/bitcoin/src/amount.rs
@@ -502,9 +502,9 @@ impl Amount {
     /// Exactly one satoshi.
     pub const ONE_SAT: Amount = Amount(1);
     /// Exactly one bitcoin.
-    pub const ONE_BTC: Amount = Amount(100_000_000);
+    pub const ONE_BTC: Amount = Self::from_int_btc(1);
     /// The maximum value allowed as an amount. Useful for sanity checking.
-    pub const MAX_MONEY: Amount = Amount(21_000_000 * 100_000_000);
+    pub const MAX_MONEY: Amount = Self::from_int_btc(21_000_000);
     /// The minimum value of an amount.
     pub const MIN: Amount = Amount::ZERO;
     /// The maximum value of an amount.
@@ -529,6 +529,28 @@ pub fn from_btc(btc: f64) -> Result<Amount, ParseAmountError> {
         Amount::from_float_in(btc, Denomination::Bitcoin)
     }
 
+    /// Convert from a value expressing integer values of bitcoins to an [Amount]
+    /// in const context.
+    ///
+    /// ## Panics
+    ///
+    /// The function panics if the argument multiplied by the number of sats
+    /// per bitcoin overflows a u64 type.
+    pub const fn from_int_btc(btc: u64) -> Amount {
+        // TODO replace whith unwrap() when available in const context.
+        match btc.checked_mul(100_000_000) {
+            Some(amount) => Amount::from_sat(amount),
+            None => {
+                // TODO replace with panic!() when MSRV = 1.57+
+                #[allow(unconditional_panic)]
+                // disabling this lint until panic!() can be used.
+                #[allow(clippy::let_unit_value)]
+                let _int_overflow_converting_btc_to_sats = [(); 0][1];
+                Amount(0)
+            }
+        }
+    }
+
     /// Parse a decimal string as a value in the given denomination.
     ///
     /// Note: This only parses the value string.  If you want to parse a value
@@ -1589,6 +1611,16 @@ fn from_str_zero() {
         }
     }
 
+    #[test]
+    fn from_int_btc() {
+        let amt = Amount::from_int_btc(2);
+        assert_eq!(Amount::from_sat(200_000_000), amt);
+    }
+
+    #[should_panic]
+    #[test]
+    fn from_int_btc_panic() { Amount::from_int_btc(u64::MAX); }
+
     #[test]
     fn mul_div() {
         let sat = Amount::from_sat;
diff --git a/bitcoin/src/blockdata/opcodes.rs b/bitcoin/src/blockdata/opcodes.rs
index c35a2aee..76ddbce8 100644
--- a/bitcoin/src/blockdata/opcodes.rs
+++ b/bitcoin/src/blockdata/opcodes.rs
@@ -153,7 +153,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     OP_PUSHNUM_NEG1 => 0x4f, "Push the array `0x81` onto the stack.";
     OP_RESERVED => 0x50, "Synonym for OP_RETURN.";
     OP_PUSHNUM_1 => 0x51, "Push the array `0x01` onto the stack.";
-    OP_PUSHNUM_2 => 0x52, "Push the array `0x02` onto the stack.";
+    OP_PUSHNUM_2 => 0x52, "the array `0x02` onto the stack.";
     OP_PUSHNUM_3 => 0x53, "Push the array `0x03` onto the stack.";
     OP_PUSHNUM_4 => 0x54, "Push the array `0x04` onto the stack.";
     OP_PUSHNUM_5 => 0x55, "Push the array `0x05` onto the stack.";
@@ -411,23 +411,6 @@ pub fn classify(self, ctx: ClassifyContext) -> Class {
     /// Encodes [`All`] as a byte.
     #[inline]
     pub const fn to_u8(self) -> u8 { self.code }
-
-    /// Encodes PUSHNUM [`All`] as a `u8` representing its number (1-16).
-    ///
-    /// Does not convert `OP_FALSE` to 0. Only `1` to `OP_PUSHNUM_16` are covered.
-    ///
-    /// # Returns
-    ///
-    /// Returns `None` if `self` is not a PUSHNUM.
-    #[inline]
-    pub(crate) const fn decode_pushnum(self) -> Option<u8> {
-        const START: u8 = OP_PUSHNUM_1.code;
-        const END: u8 = OP_PUSHNUM_16.code;
-        match self.code {
-            START..=END => Some(self.code - START + 1),
-            _ => None,
-        }
-    }
 }
 
 impl From<u8> for All {
@@ -570,44 +553,6 @@ fn formatting_works() {
         assert_eq!(s, "    OP_NOP");
     }
 
-    #[test]
-    fn decode_pushnum() {
-        // Test all possible opcodes
-        // - Sanity check
-        assert_eq!(OP_PUSHNUM_1.code, 0x51_u8);
-        assert_eq!(OP_PUSHNUM_16.code, 0x60_u8);
-        for i in 0x00..=0xff_u8 {
-            let expected = match i {
-                // OP_PUSHNUM_1 ..= OP_PUSHNUM_16
-                0x51..=0x60 => Some(i - 0x50),
-                _ => None,
-            };
-            assert_eq!(All::from(i).decode_pushnum(), expected);
-        }
-
-        // Test the named opcode constants
-        // - This is the OP right before PUSHNUMs start
-        assert!(OP_RESERVED.decode_pushnum().is_none());
-        assert_eq!(OP_PUSHNUM_1.decode_pushnum().expect("pushnum"), 1);
-        assert_eq!(OP_PUSHNUM_2.decode_pushnum().expect("pushnum"), 2);
-        assert_eq!(OP_PUSHNUM_3.decode_pushnum().expect("pushnum"), 3);
-        assert_eq!(OP_PUSHNUM_4.decode_pushnum().expect("pushnum"), 4);
-        assert_eq!(OP_PUSHNUM_5.decode_pushnum().expect("pushnum"), 5);
-        assert_eq!(OP_PUSHNUM_6.decode_pushnum().expect("pushnum"), 6);
-        assert_eq!(OP_PUSHNUM_7.decode_pushnum().expect("pushnum"), 7);
-        assert_eq!(OP_PUSHNUM_8.decode_pushnum().expect("pushnum"), 8);
-        assert_eq!(OP_PUSHNUM_9.decode_pushnum().expect("pushnum"), 9);
-        assert_eq!(OP_PUSHNUM_10.decode_pushnum().expect("pushnum"), 10);
-        assert_eq!(OP_PUSHNUM_11.decode_pushnum().expect("pushnum"), 11);
-        assert_eq!(OP_PUSHNUM_12.decode_pushnum().expect("pushnum"), 12);
-        assert_eq!(OP_PUSHNUM_13.decode_pushnum().expect("pushnum"), 13);
-        assert_eq!(OP_PUSHNUM_14.decode_pushnum().expect("pushnum"), 14);
-        assert_eq!(OP_PUSHNUM_15.decode_pushnum().expect("pushnum"), 15);
-        assert_eq!(OP_PUSHNUM_16.decode_pushnum().expect("pushnum"), 16);
-        // - This is the OP right after PUSHNUMs end
-        assert!(OP_NOP.decode_pushnum().is_none());
-    }
-
     #[test]
     fn classify_test() {
         let op174 = OP_CHECKMULTISIG;
diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs
index 5f1c747e..aad2f557 100644
--- a/bitcoin/src/blockdata/script/borrowed.rs
+++ b/bitcoin/src/blockdata/script/borrowed.rs
@@ -318,83 +318,6 @@ pub fn dust_value(&self) -> crate::Amount {
         crate::Amount::from_sat(sats)
     }
 
-    /// Counts the sigops for this Script using accurate counting.
-    ///
-    /// In Bitcoin Core, there are two ways to count sigops, "accurate" and "legacy".
-    /// This method uses "accurate" counting. This means that OP_CHECKMULTISIG and its
-    /// verify variant count for N sigops where N is the number of pubkeys used in the
-    /// multisig. However, it will count for 20 sigops if CHECKMULTISIG is not preceeded by an
-    /// OP_PUSHNUM from 1 - 16 (this would be an invalid script)
-    ///
-    /// Bitcoin Core uses accurate counting for sigops contained within redeemScripts (P2SH)
-    /// and witnessScripts (P2WSH) only. It uses legacy for sigops in scriptSigs and scriptPubkeys.
-    ///
-    /// (Note: taproot scripts don't count toward the sigop count of the block,
-    /// nor do they have CHECKMULTISIG operations. This function does not count OP_CHECKSIGADD,
-    /// so do not use this to try and estimate if a taproot script goes over the sigop budget.)
-    ///
-    /// # Errors
-    ///
-    /// If the Script is not able to be parsed to completion.
-    pub fn count_sigops(&self) -> Result<usize, super::Error> { self.count_sigops_internal(true) }
-
-    /// Counts the sigops for this Script using legacy counting.
-    ///
-    /// In Bitcoin Core, there are two ways to count sigops, "accurate" and "legacy".
-    /// This method uses "legacy" counting. This means that OP_CHECKMULTISIG and its
-    /// verify variant count for 20 sigops.
-    ///
-    /// Bitcoin Core uses legacy counting for sigops contained within scriptSigs and
-    /// scriptPubkeys. It uses accurate for redeemScripts (P2SH) and witnessScripts (P2WSH).
-    ///
-    /// (Note: taproot scripts don't count toward the sigop count of the block,
-    /// nor do they have CHECKMULTISIG operations. This function does not count OP_CHECKSIGADD,
-    /// so do not use this to try and estimate if a taproot script goes over the sigop budget.)
-    ///
-    /// # Errors
-    ///
-    /// If the Script is not able to be parsed to completion.
-    pub fn count_sigops_legacy(&self) -> Result<usize, super::Error> {
-        self.count_sigops_internal(false)
-    }
-
-    fn count_sigops_internal(&self, accurate: bool) -> Result<usize, super::Error> {
-        let mut n = 0;
-        let mut pushnum_cache = None;
-        for inst in self.instructions() {
-            match inst? {
-                Instruction::Op(opcode) => {
-                    match opcode {
-                        OP_CHECKSIG | OP_CHECKSIGVERIFY => {
-                            n += 1;
-                        }
-                        OP_CHECKMULTISIG | OP_CHECKMULTISIGVERIFY => {
-                            match (accurate, pushnum_cache) {
-                                (true, Some(pushnum)) => {
-                                    // Add the number of pubkeys in the multisig as sigop count
-                                    n += usize::from(pushnum);
-                                }
-                                _ => {
-                                    // MAX_PUBKEYS_PER_MULTISIG from Bitcoin Core
-                                    // https://github.com/bitcoin/bitcoin/blob/v25.0/src/script/script.h#L29-L30
-                                    n += 20;
-                                }
-                            }
-                        }
-                        _ => {
-                            pushnum_cache = opcode.decode_pushnum();
-                        }
-                    }
-                }
-                Instruction::PushBytes(_) => {
-                    pushnum_cache = None;
-                }
-            }
-        }
-
-        Ok(n)
-    }
-
     /// Iterates over the script instructions.
     ///
     /// Each returned item is a nested enum covering opcodes, datapushes and errors.
diff --git a/bitcoin/src/blockdata/script/mod.rs b/bitcoin/src/blockdata/script/mod.rs
index cc749974..b688ff13 100644
--- a/bitcoin/src/blockdata/script/mod.rs
+++ b/bitcoin/src/blockdata/script/mod.rs
@@ -779,6 +779,7 @@ fn from(error: UintError) -> Self {
 }
 
 #[cfg(feature = "bitcoinconsensus")]
+#[doc(hidden)]
 impl From<bitcoinconsensus::Error> for Error {
     fn from(err: bitcoinconsensus::Error) -> Error { Error::BitcoinConsensus(err) }
 }
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index 255fea61..9c49c8bd 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -1285,6 +1285,8 @@ const fn predict_weight_internal(
 
 /// Predicts the weight of a to-be-constructed transaction in const context.
 ///
+/// *Important: only available in Rust 1.46+*
+///
 /// This is a `const` version of [`predict_weight`] which only allows slices due to current Rust
 /// limitations around `const fn`. Because of these limitations it may be less efficient than
 /// `predict_weight` and thus is intended to be only used in `const` context.
@@ -1402,6 +1404,8 @@ pub fn new<T>(input_script_len: usize, witness_element_lengths: T) -> Self
 
     /// Computes the prediction for a single input in `const` context.
     ///
+    /// *Important: only available in Rust 1.46+*
+    ///
     /// This is a `const` version of [`new`](Self::new) which only allows slices due to current Rust
     /// limitations around `const fn`. Because of these limitations it may be less efficient than
     /// `new` and thus is intended to be only used in `const` context.
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index 1ff023cc..7f6e2f0e 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -93,6 +93,7 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     }
 }
 
+#[doc(hidden)]
 impl From<io::Error> for Error {
     fn from(error: io::Error) -> Self { Error::Io(error) }
 }
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 011bbae4..605e8b88 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -65,14 +65,17 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     }
 }
 
+#[doc(hidden)]
 impl From<base58::Error> for Error {
     fn from(e: base58::Error) -> Error { Error::Base58(e) }
 }
 
+#[doc(hidden)]
 impl From<secp256k1::Error> for Error {
     fn from(e: secp256k1::Error) -> Error { Error::Secp256k1(e) }
 }
 
+#[doc(hidden)]
 impl From<hex::Error> for Error {
     fn from(e: hex::Error) -> Self { Error::Hex(e) }
 }
@@ -253,10 +256,6 @@ pub fn from_private_key<C: secp256k1::Signing>(
     }
 }
 
-impl From<PublicKey> for XOnlyPublicKey {
-    fn from(pk: PublicKey) -> XOnlyPublicKey { pk.inner.into() }
-}
-
 /// An opaque return type for PublicKey::to_sort_key
 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
 pub struct SortKey(u8, [u8; 32], [u8; 32]);
diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs
index 9a15cce1..ffb34c63 100644
--- a/bitcoin/src/crypto/taproot.rs
+++ b/bitcoin/src/crypto/taproot.rs
@@ -26,21 +26,23 @@ pub struct Signature {
 
 impl Signature {
     /// Deserialize from slice
-    pub fn from_slice(sl: &[u8]) -> Result<Self, SigFromSliceError> {
+    pub fn from_slice(sl: &[u8]) -> Result<Self, Error> {
         match sl.len() {
             64 => {
                 // default type
-                let sig = secp256k1::schnorr::Signature::from_slice(sl)?;
+                let sig =
+                    secp256k1::schnorr::Signature::from_slice(sl).map_err(Error::Secp256k1)?;
                 Ok(Signature { sig, hash_ty: TapSighashType::Default })
             }
             65 => {
                 let (hash_ty, sig) = sl.split_last().expect("Slice len checked == 65");
                 let hash_ty = TapSighashType::from_consensus_u8(*hash_ty)
-                    .map_err(|_| SigFromSliceError::InvalidSighashType(*hash_ty))?;
-                let sig = secp256k1::schnorr::Signature::from_slice(sig)?;
+                    .map_err(|_| Error::InvalidSighashType(*hash_ty))?;
+                let sig =
+                    secp256k1::schnorr::Signature::from_slice(sig).map_err(Error::Secp256k1)?;
                 Ok(Signature { sig, hash_ty })
             }
-            len => Err(SigFromSliceError::InvalidSignatureSize(len)),
+            len => Err(Error::InvalidSignatureSize(len)),
         }
     }
 
@@ -57,13 +59,11 @@ pub fn to_vec(self) -> Vec<u8> {
     }
 }
 
-/// An error constructing a [`taproot::Signature`] from a byte slice.
-///
-/// [`taproot::Signature`]: crate::crypto::taproot::Signature
+/// A taproot sig related error.
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 #[non_exhaustive]
-pub enum SigFromSliceError {
-    /// Invalid signature hash type.
+pub enum Error {
+    /// Base58 encoding error
     InvalidSighashType(u8),
     /// Signature has valid size but does not parse correctly
     Secp256k1(secp256k1::Error),
@@ -71,23 +71,22 @@ pub enum SigFromSliceError {
     InvalidSignatureSize(usize),
 }
 
-impl fmt::Display for SigFromSliceError {
+impl fmt::Display for Error {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use SigFromSliceError::*;
-
         match *self {
-            InvalidSighashType(hash_ty) => write!(f, "invalid signature hash type {}", hash_ty),
-            Secp256k1(ref e) =>
+            Error::InvalidSighashType(hash_ty) =>
+                write!(f, "invalid signature hash type {}", hash_ty),
+            Error::Secp256k1(ref e) =>
                 write_err!(f, "taproot signature has correct len but is malformed"; e),
-            InvalidSignatureSize(sz) => write!(f, "invalid taproot signature size: {}", sz),
+            Error::InvalidSignatureSize(sz) => write!(f, "invalid taproot signature size: {}", sz),
         }
     }
 }
 
 #[cfg(feature = "std")]
-impl std::error::Error for SigFromSliceError {
+impl std::error::Error for Error {
     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        use SigFromSliceError::*;
+        use self::Error::*;
 
         match self {
             Secp256k1(e) => Some(e),
@@ -96,6 +95,6 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     }
 }
 
-impl From<secp256k1::Error> for SigFromSliceError {
-    fn from(e: secp256k1::Error) -> Self { SigFromSliceError::Secp256k1(e) }
+impl From<secp256k1::Error> for Error {
+    fn from(e: secp256k1::Error) -> Error { Error::Secp256k1(e) }
 }
diff --git a/bitcoin/src/error.rs b/bitcoin/src/error.rs
index ab296628..0536c72f 100644
--- a/bitcoin/src/error.rs
+++ b/bitcoin/src/error.rs
@@ -2,6 +2,22 @@
 
 //! Contains error types and other error handling tools.
 
-pub(crate) use internals::impl_std_error;
-
 pub use crate::parse::ParseIntError;
+
+/// Impls std::error::Error for the specified type with appropriate attributes, possibly returning
+/// source.
+macro_rules! impl_std_error {
+    // No source available
+    ($type:ty) => {
+        #[cfg(feature = "std")]
+        impl std::error::Error for $type {}
+    };
+    // Struct with $field as source
+    ($type:ty, $field:ident) => {
+        #[cfg(feature = "std")]
+        impl std::error::Error for $type {
+            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.$field) }
+        }
+    };
+}
+pub(crate) use impl_std_error;
diff --git a/bitcoin/src/lib.rs b/bitcoin/src/lib.rs
index eda2fdbb..b12b11bb 100644
--- a/bitcoin/src/lib.rs
+++ b/bitcoin/src/lib.rs
@@ -6,9 +6,9 @@
 //! primitives. It is designed for Rust programs built to work with the Bitcoin
 //! network.
 //!
-//! Except for its dependency on libsecp256k1 (and optionally libbitcoinconsensus),
-//! this library is written entirely in Rust. It illustrates the benefits of
-//! strong type safety, including ownership and lifetime, for financial and/or cryptographic software.
+//! It is also written entirely in Rust to illustrate the benefits of strong type
+//! safety, including ownership and lifetime, for financial and/or cryptographic
+//! software.
 //!
 //! See README.md for detailed documentation about development and supported
 //! environments.
diff --git a/bitcoin/src/network/constants.rs b/bitcoin/src/network/constants.rs
index 202c2d69..2e2ad573 100644
--- a/bitcoin/src/network/constants.rs
+++ b/bitcoin/src/network/constants.rs
@@ -441,7 +441,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
 impl_std_error!(UnknownMagic);
 
 /// Flags to indicate which network services a node supports.
-#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct ServiceFlags(u64);
 
 impl ServiceFlags {
diff --git a/bitcoin/src/psbt/error.rs b/bitcoin/src/psbt/error.rs
index a32beb85..778086b8 100644
--- a/bitcoin/src/psbt/error.rs
+++ b/bitcoin/src/psbt/error.rs
@@ -85,7 +85,7 @@ pub enum Error {
     /// Parsing error indicating invalid ECDSA signatures
     InvalidEcdsaSignature(crate::crypto::ecdsa::Error),
     /// Parsing error indicating invalid taproot signatures
-    InvalidTaprootSignature(crate::crypto::taproot::SigFromSliceError),
+    InvalidTaprootSignature(crate::crypto::taproot::Error),
     /// Parsing error indicating invalid control block
     InvalidControlBlock,
     /// Parsing error indicating invalid leaf version
@@ -203,6 +203,7 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     }
 }
 
+#[doc(hidden)]
 impl From<hashes::FromSliceError> for Error {
     fn from(e: hashes::FromSliceError) -> Error { Error::HashParse(e) }
 }
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index e515f31a..85501cf8 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -258,12 +258,10 @@ fn serialize(&self) -> Vec<u8> { self.to_vec() }
 
 impl Deserialize for taproot::Signature {
     fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
-        use taproot::SigFromSliceError::*;
-
         taproot::Signature::from_slice(bytes).map_err(|e| match e {
-            InvalidSighashType(flag) => Error::NonStandardSighashType(flag as u32),
-            InvalidSignatureSize(_) => Error::InvalidTaprootSignature(e),
-            Secp256k1(..) => Error::InvalidTaprootSignature(e),
+            taproot::Error::InvalidSighashType(flag) => Error::NonStandardSighashType(flag as u32),
+            taproot::Error::InvalidSignatureSize(_) => Error::InvalidTaprootSignature(e),
+            taproot::Error::Secp256k1(..) => Error::InvalidTaprootSignature(e),
         })
     }
 }
diff --git a/bitcoin/src/sign_message.rs b/bitcoin/src/sign_message.rs
index 94b0658f..52f445a4 100644
--- a/bitcoin/src/sign_message.rs
+++ b/bitcoin/src/sign_message.rs
@@ -68,6 +68,7 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
         }
     }
 
+    #[doc(hidden)]
     impl From<secp256k1::Error> for MessageSignatureError {
         fn from(e: secp256k1::Error) -> MessageSignatureError {
             MessageSignatureError::InvalidEncoding(e)
diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs
index ae1b3f45..2cdce7ec 100644
--- a/bitcoin/src/taproot.rs
+++ b/bitcoin/src/taproot.rs
@@ -17,7 +17,7 @@
 use crate::consensus::Encodable;
 use crate::crypto::key::{TapTweak, TweakedPublicKey, UntweakedPublicKey, XOnlyPublicKey};
 // Re-export these so downstream only has to use one `taproot` module.
-pub use crate::crypto::taproot::{SigFromSliceError, Signature};
+pub use crate::crypto::taproot::{Error, Signature};
 use crate::prelude::*;
 use crate::{io, Script, ScriptBuf};
 
@@ -251,9 +251,6 @@ pub fn output_key(&self) -> TweakedPublicKey { self.output_key }
     /// Returns the parity of the output key. See also [`TaprootSpendInfo::output_key`].
     pub fn output_key_parity(&self) -> secp256k1::Parity { self.output_key_parity }
 
-    /// Returns a reference to the internal script map.
-    pub fn script_map(&self) -> &ScriptMerkleProofMap { &self.script_map }
-
     /// Computes the [`TaprootSpendInfo`] from `internal_key` and `node`.
     ///
     /// This is useful when you want to manually build a taproot tree without using
@@ -292,8 +289,7 @@ pub fn from_node_info<C: secp256k1::Verification>(
     }
 
     /// Returns the internal script map.
-    #[deprecated(since = "0.31.0", note = "use Self::script_map instead")]
-    pub fn as_script_map(&self) -> &ScriptMerkleProofMap { self.script_map() }
+    pub fn as_script_map(&self) -> &ScriptMerkleProofMap { &self.script_map }
 
     /// Constructs a [`ControlBlock`] for particular script with the given version.
     ///
diff --git a/internals/Cargo.toml b/internals/Cargo.toml
index 99a3b049..2f79a2f1 100644
--- a/internals/Cargo.toml
+++ b/internals/Cargo.toml
@@ -22,6 +22,5 @@ all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
-serde = { version = "1.0.103", default-features = false, optional = true }
 
 [dev-dependencies]
diff --git a/internals/build.rs b/internals/build.rs
index ab1f0411..6f366409 100644
--- a/internals/build.rs
+++ b/internals/build.rs
@@ -26,8 +26,7 @@ fn main() {
 
     // print cfg for all interesting versions less than or equal to minor
     // 46 adds `track_caller`
-    // 55 adds `kind()` to `ParseIntError`
-    for version in &[46, 55] {
+    for version in &[46] {
         if *version <= minor {
             println!("cargo:rustc-cfg=rust_v_1_{}", version);
         }
diff --git a/internals/src/error.rs b/internals/src/error.rs
index 31580fc7..ff596734 100644
--- a/internals/src/error.rs
+++ b/internals/src/error.rs
@@ -5,11 +5,6 @@
 //! Error handling macros and helpers.
 //!
 
-pub mod input_string;
-mod parse_error;
-
-pub use input_string::InputString;
-
 /// Formats error.
 ///
 /// If `std` feature is OFF appends error source (delimited by `: `). We do this because
@@ -31,21 +26,3 @@ macro_rules! write_err {
         }
     }
 }
-
-/// Impls std::error::Error for the specified type with appropriate attributes, possibly returning
-/// source.
-#[macro_export]
-macro_rules! impl_std_error {
-    // No source available
-    ($type:ty) => {
-        #[cfg(feature = "std")]
-        impl std::error::Error for $type {}
-    };
-    // Struct with $field as source
-    ($type:ty, $field:ident) => {
-        #[cfg(feature = "std")]
-        impl std::error::Error for $type {
-            fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.$field) }
-        }
-    };
-}
diff --git a/internals/src/error/input_string.rs b/internals/src/error/input_string.rs
deleted file mode 100644
index b69751c0..00000000
--- a/internals/src/error/input_string.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-//! Implements the [`InputString`] type storing the parsed input.
-
-use core::fmt;
-
-use storage::Storage;
-
-/// Conditionally stores the input string in parse errors.
-///
-/// This type stores the input string of a parse function depending on whether `alloc` feature is
-/// enabled. When it is enabled, the string is stored inside as `String`. When disabled this is a
-/// zero-sized type and attempt to store a string does nothing.
-///
-/// This provides two methods to format the error strings depending on the context.
-#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
-pub struct InputString(Storage);
-
-impl InputString {
-    /// Displays a message saying `failed to parse <self> as <what>`.
-    ///
-    /// This is normally used whith the `write_err!` macro.
-    pub fn display_cannot_parse<'a, T>(&'a self, what: &'a T) -> CannotParse<'a, T>
-    where
-        T: fmt::Display + ?Sized,
-    {
-        CannotParse { input: self, what }
-    }
-
-    /// Formats a message saying `<self> is not a known <what>`.
-    ///
-    /// This is normally used in leaf parse errors (with no source) when parsing an enum.
-    pub fn unknown_variant<T>(&self, what: &T, f: &mut fmt::Formatter) -> fmt::Result
-    where
-        T: fmt::Display + ?Sized,
-    {
-        storage::unknown_variant(&self.0, what, f)
-    }
-}
-
-macro_rules! impl_from {
-    ($($type:ty),+ $(,)?) => {
-        $(
-            impl From<$type> for InputString {
-                fn from(input: $type) -> Self {
-                    #[allow(clippy::useless_conversion)]
-                    InputString(input.into())
-                }
-            }
-        )+
-    }
-}
-
-impl_from!(&str);
-
-/// Displays message saying `failed to parse <input> as <what>`.
-///
-/// This is created by `display_cannot_parse` method and should be used as
-/// `write_err!("{}", self.input.display_cannot_parse("what is parsed"); self.source)` in parse
-/// error [`Display`](fmt::Display) imlementation if the error has source. If the error doesn't
-/// have a source just use regular `write!` with same formatting arguments.
-pub struct CannotParse<'a, T: fmt::Display + ?Sized> {
-    input: &'a InputString,
-    what: &'a T,
-}
-
-impl<'a, T: fmt::Display + ?Sized> fmt::Display for CannotParse<'a, T> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        storage::cannot_parse(&self.input.0, &self.what, f)
-    }
-}
-
-#[cfg(not(feature = "alloc"))]
-mod storage {
-    use core::fmt;
-
-    #[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
-    pub(super) struct Storage;
-
-    impl fmt::Debug for Storage {
-        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            f.write_str("<unknown input string - compiled without the `alloc` feature>")
-        }
-    }
-
-    impl From<&str> for Storage {
-        fn from(_value: &str) -> Self { Storage }
-    }
-
-    pub(super) fn cannot_parse<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
-    where
-        W: fmt::Display + ?Sized,
-    {
-        write!(f, "failed to parse {}", what)
-    }
-
-    pub(super) fn unknown_variant<W>(_: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
-    where
-        W: fmt::Display + ?Sized,
-    {
-        write!(f, "unknown {}", what)
-    }
-}
-
-#[cfg(feature = "alloc")]
-mod storage {
-    use core::fmt;
-
-    use super::InputString;
-
-    pub(super) type Storage = alloc::string::String;
-
-    pub(super) fn cannot_parse<W>(input: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
-    where
-        W: fmt::Display + ?Sized,
-    {
-        write!(f, "failed to parse '{}' as {}", input, what)
-    }
-
-    pub(super) fn unknown_variant<W>(inp: &Storage, what: &W, f: &mut fmt::Formatter) -> fmt::Result
-    where
-        W: fmt::Display + ?Sized,
-    {
-        write!(f, "'{}' is not a known {}", inp, what)
-    }
-
-    impl_from!(alloc::string::String, alloc::boxed::Box<str>, alloc::borrow::Cow<'_, str>);
-}
diff --git a/internals/src/error/parse_error.rs b/internals/src/error/parse_error.rs
deleted file mode 100644
index 15552fbd..00000000
--- a/internals/src/error/parse_error.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-//! Contains helpers for parsing-related errors.
-
-/// Creates an error type intended for string parsing errors.
-///
-/// The resulting error type has two fields: `input` and `source`. The type of `input` is
-/// [`InputString`](super::InputString), the type of `source` is specified as the second argument
-/// to the macro.
-///
-/// The resulting type is public, conditionally implements [`std::error::Error`] and has a private
-/// `new()` method for convenience.
-///
-/// ## Parameters
-///
-/// * `name` - the name of the error type
-/// * `source` - the type of the source type
-/// * `subject` - English description of the type being parsed (e.g. "a bitcoin amount")
-/// * `derive` - list of derives to add
-#[macro_export]
-macro_rules! parse_error_type {
-    ($vis:vis $name:ident, $source:ty, $subject:expr $(, $derive:path)* $(,)?) => {
-        #[derive(Debug $(, $derive)*)]
-        $vis struct $name {
-            input: $crate::error::InputString,
-            source: $source,
-        }
-
-        impl $name {
-            /// Creates `Self`.
-            fn new<T: Into<$crate::error::InputString>>(input: T, source: $source) -> Self {
-                $name {
-                    input: input.into(),
-                    source,
-                }
-            }
-        }
-
-        impl core::fmt::Display for $name {
-            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
-                $crate::error::write_err!("{}", self.input.display_cannot_parse($subject); self.source)
-            }
-        }
-
-        $crate::error::impl_std_error!($name, source);
-    }
-}
diff --git a/internals/src/lib.rs b/internals/src/lib.rs
index f85e0b8f..9e794aed 100644
--- a/internals/src/lib.rs
+++ b/internals/src/lib.rs
@@ -21,8 +21,6 @@
 pub mod error;
 pub mod hex;
 pub mod macros;
-mod parse;
-pub mod serde;
 
 /// Mainly reexports based on features.
 pub(crate) mod prelude {
diff --git a/internals/src/parse.rs b/internals/src/parse.rs
deleted file mode 100644
index 6b2d20ae..00000000
--- a/internals/src/parse.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-/// Support for parsing strings.
-
-// Impls a single TryFrom conversion
-#[doc(hidden)]
-#[macro_export]
-macro_rules! impl_try_from_stringly {
-    ($from:ty, $to:ty, $error:ty, $func:expr $(, $attr:meta)?) => {
-        $(#[$attr])?
-        impl core::convert::TryFrom<$from> for $to {
-            type Error = $error;
-
-            fn try_from(s: $from) -> Result<Self, Self::Error> {
-                $func(AsRef::<str>::as_ref(s)).map_err(|source| <$error>::new(s, source))
-            }
-        }
-
-    }
-}
-
-/// Implements conversions from various string types.
-///
-/// This macro implements `FromStr` as well as `TryFrom<{stringly}` where `{stringly}` is one of
-/// these types:
-///
-/// * `&str`
-/// * `String`
-/// * `Box<str>`
-/// * `Cow<'_, str>`
-///
-/// The last three are only available with `alloc` feature turned on.
-#[macro_export]
-macro_rules! impl_parse {
-    ($type:ty, $descr:expr, $func:expr, $vis:vis $error:ident, $error_source:ty $(, $error_derive:path)*) => {
-        $crate::parse_error_type!($vis $error, $error_source, $descr $(, $error_derive)*);
-
-        impl core::str::FromStr for $type {
-            type Err = $error;
-
-            fn from_str(s: &str) -> Result<Self, Self::Err> {
-                $func(s).map_err(|source| <$error>::new(s, source))
-            }
-        }
-
-        impl_try_from_stringly!(&str);
-
-        #[cfg(feature = "alloc")]
-        impl_try_from_stringly!(alloc::string::String, $type, $error, $func, cfg_attr(docsrs, doc(feature = "alloc")));
-        #[cfg(feature = "alloc")]
-        impl_try_from_stringly!(alloc::borrow::Cow<'_, str>, $type, $error, $func, cfg_attr(docsrs, doc(feature = "alloc")));
-        #[cfg(feature = "alloc")]
-        impl_try_from_stringly!(alloc::boxed::Box<str>, $type, $error, $func, cfg_attr(docsrs, doc(feature = "alloc")));
-    }
-}
-
-/// Implements conversions from various string types as well as `serde` (de)serialization.
-///
-/// This calls `impl_parse` macro and implements serde deserialization by expecting and parsing a
-/// string and serialization by outputting a string.
-#[macro_export]
-macro_rules! impl_parse_and_serde {
-    ($type:ty, $descr:expr, $func:expr, $error:ident, $error_source:ty $(, $error_derive:path)*) => {
-        impl_parse!($type, $descr, $func, $error, $error_source $(, $error_derive)*);
-
-        // We don't use `serde_string_impl` because we want to avoid allocating input.
-        #[cfg(feature = "serde")]
-        #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
-        impl<'de> $crate::serde::Deserialize<'de> for $type {
-            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
-            where
-                D: $crate::serde::de::Deserializer<'de>,
-            {
-                use core::fmt::{self, Formatter};
-                use core::str::FromStr;
-
-                struct Visitor;
-                impl<'de> $crate::serde::de::Visitor<'de> for Visitor {
-                    type Value = $name;
-
-                    fn expecting(&self, f: &mut Formatter) -> fmt::Result {
-                        f.write_str($descr)
-                    }
-
-                    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
-                    where
-                        E: $crate::serde::de::Error,
-                    {
-                        s.parse().map_err(|error| {
-                            $crate::serde::IntoDeError::try_into_de_error(error)
-                                .unwrap_or_else(|_| E::invalid_value(Unexpected::Str(s), &self))
-                        })
-                    }
-                }
-
-                deserializer.deserialize_str(Visitor)
-            }
-        }
-
-        #[cfg(feature = "serde")]
-        #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
-        impl $crate::serde::Serialize for $name {
-            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-            where
-                S: $crate::serde::Serializer,
-            {
-                serializer.collect_str(&self)
-            }
-        }
-    }
-}
diff --git a/internals/src/serde.rs b/internals/src/serde.rs
deleted file mode 100644
index 14c5a49c..00000000
--- a/internals/src/serde.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-//! Contains extensions of `serde` and internal reexports.
-
-#[cfg(feature = "serde")]
-#[doc(hidden)]
-pub use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
-
-/// Converts given error type to a type implementing [`de::Error`].
-///
-/// This is used in [`Deserialize`] implementations to convert specialized errors into serde
-/// errors.
-#[cfg(feature = "serde")]
-pub trait IntoDeError: Sized {
-    /// Converts to deserializer error possibly outputting vague message.
-    ///
-    /// This method is allowed to return a vague error message if the error type doesn't contain
-    /// enough information to explain the error precisely.
-    fn into_de_error<E: de::Error>(self, expected: Option<&dyn de::Expected>) -> E;
-
-    /// Converts to deserializer error without outputting vague message.
-    ///
-    /// If the error type doesn't contain enough information to explain the error precisely this
-    /// should return `Err(self)` allowing the caller to use its information instead.
-    fn try_into_de_error<E>(self, expected: Option<&dyn de::Expected>) -> Result<E, Self>
-    where
-        E: de::Error,
-    {
-        Ok(self.into_de_error(expected))
-    }
-}
-
-#[cfg(feature = "serde")]
-mod impls {
-    use super::*;
-
-    impl IntoDeError for core::convert::Infallible {
-        fn into_de_error<E: de::Error>(self, _expected: Option<&dyn de::Expected>) -> E {
-            match self {}
-        }
-    }
-
-    impl IntoDeError for core::num::ParseIntError {
-        fn into_de_error<E: de::Error>(self, expected: Option<&dyn de::Expected>) -> E {
-            self.try_into_de_error(expected).unwrap_or_else(|_| {
-                let expected = expected.unwrap_or(&"an integer");
-
-                E::custom(format_args!("invalid string, expected {}", expected))
-            })
-        }
-
-        #[cfg(rust_v_1_55)]
-        fn try_into_de_error<E>(self, expected: Option<&dyn de::Expected>) -> Result<E, Self>
-        where
-            E: de::Error,
-        {
-            use core::num::IntErrorKind::Empty;
-
-            let expected = expected.unwrap_or(&"an integer");
-
-            match self.kind() {
-                Empty => Ok(E::invalid_value(de::Unexpected::Str(""), expected)),
-                _ => Err(self),
-            }
-        }
-
-        #[cfg(not(rust_v_1_55))]
-        fn try_into_de_error<E>(self, _expected: Option<&dyn de::Expected>) -> Result<E, Self>
-        where
-            E: de::Error,
-        {
-            Err(self)
-        }
-    }
-}


Test patch:
diff --git a/bitcoin/src/blockdata/script/tests.rs b/bitcoin/src/blockdata/script/tests.rs
index c112e25f..7b4a68a3 100644
--- a/bitcoin/src/blockdata/script/tests.rs
+++ b/bitcoin/src/blockdata/script/tests.rs
@@ -607,82 +607,6 @@ fn defult_dust_value_tests() {
     assert_eq!(script_p2pkh.dust_value(), crate::Amount::from_sat(546));
 }
 
-#[test]
-fn test_script_get_sigop_count() {
-    assert_eq!(
-        Builder::new()
-            .push_opcode(OP_DUP)
-            .push_opcode(OP_HASH160)
-            .push_slice([42; 20])
-            .push_opcode(OP_EQUAL)
-            .into_script()
-            .count_sigops(),
-        Ok(0)
-    );
-    assert_eq!(
-        Builder::new()
-            .push_opcode(OP_DUP)
-            .push_opcode(OP_HASH160)
-            .push_slice([42; 20])
-            .push_opcode(OP_EQUALVERIFY)
-            .push_opcode(OP_CHECKSIG)
-            .into_script()
-            .count_sigops(),
-        Ok(1)
-    );
-    assert_eq!(
-        Builder::new()
-            .push_opcode(OP_DUP)
-            .push_opcode(OP_HASH160)
-            .push_slice([42; 20])
-            .push_opcode(OP_EQUALVERIFY)
-            .push_opcode(OP_CHECKSIGVERIFY)
-            .push_opcode(OP_PUSHNUM_1)
-            .into_script()
-            .count_sigops(),
-        Ok(1)
-    );
-    let multi = Builder::new()
-        .push_opcode(OP_PUSHNUM_1)
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_opcode(OP_PUSHNUM_3)
-        .push_opcode(OP_CHECKMULTISIG)
-        .into_script();
-    assert_eq!(multi.count_sigops(), Ok(3));
-    assert_eq!(multi.count_sigops_legacy(), Ok(20));
-    let multi_verify = Builder::new()
-        .push_opcode(OP_PUSHNUM_1)
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_opcode(OP_PUSHNUM_3)
-        .push_opcode(OP_CHECKMULTISIGVERIFY)
-        .push_opcode(OP_PUSHNUM_1)
-        .into_script();
-    assert_eq!(multi_verify.count_sigops(), Ok(3));
-    assert_eq!(multi_verify.count_sigops_legacy(), Ok(20));
-    let multi_nopushnum_pushdata = Builder::new()
-        .push_opcode(OP_PUSHNUM_1)
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_opcode(OP_CHECKMULTISIG)
-        .into_script();
-    assert_eq!(multi_nopushnum_pushdata.count_sigops(), Ok(20));
-    assert_eq!(multi_nopushnum_pushdata.count_sigops_legacy(), Ok(20));
-    let multi_nopushnum_op = Builder::new()
-        .push_opcode(OP_PUSHNUM_1)
-        .push_slice([3; 33])
-        .push_slice([3; 33])
-        .push_opcode(OP_DROP)
-        .push_opcode(OP_CHECKMULTISIG)
-        .into_script();
-    assert_eq!(multi_nopushnum_op.count_sigops(), Ok(20));
-    assert_eq!(multi_nopushnum_op.count_sigops_legacy(), Ok(20));
-}
-
 #[test]
 #[cfg(feature = "serde")]
 fn test_script_serde_human_and_not() {