Repository: rust-bitcoin/rust-bitcoin
Pull request #988 (2022-05-04 11:38:48)

Replace consensus `Encodable`/`Decodable` Psbt Serialization
fix https://github.com/rust-bitcoin/rust-bitcoin/issues/934

Instead of using consensus {de,en}code, serialize high-level structures (PartiallySignedTransaciton, Output, Input) borrow the signature from `Serialize`, `Deserialize` traits and implement them on Psbt:

```rs
impl Psbt {
    /// Serialize a value as raw data.
    fn serialize(&self) -> Vec<u8>;

    /// Deserialize a value from raw data.
    fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error>;
}
```

tcharding: Side note: If you use one of the nine keywords [defined by github](https://docs.github.com/en/enterprise-server@3.4/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) the issue will be automatically closed when a PR merges. 'Addresses' isn't on the list, or was that intentional?
DanGould: @tcharding as you note the Serialize Deserialize Psbt Traits are for the key value pairs. I propose we continue to use the consensus_encoding signature with the io::Read and io::Write signature but in non-consensus-related methods. It's more convenient to use a decoder with a pointer than raw byte streams.
DanGould: I've taken a few attempts to solve this after the draft. I think the original reason Carl used consensus encoding is because that module contains convenient, generalized, and now well reviewed serialization patterns. The PSBT-pr reviewers knew the patterns and so ignored their repurpose. We should separate these patterns from the consensus module for their utility across the library. Then the consensus mod will contain consensus layer bitcoin p2p network implementation and a clean serialize mod will contain familiar utilities available for clean application level support for psbt and beyond.
tcharding: Isn't the problem we are trying to solve that implementing `Encodable` for PSBT is a strange thing to do. It does not matter _how_ we serialize stuff in the `psbt` module, if we choose to use `consensus_encode` under the hood thats fine (as we do for all the other implementations) since the exact method of serialization is up to us (no spec), right? The issue that `consensus_encode` accepts a writer may be a better API than `psbt::serialize::Serialize` is a separate issue as is the fact that we have our own `Serialize` trait as well as deriving `serde::Serialize`. 
DanGould: Ah I think you cracked it. The simple solution wins again.

Makes sense to take advantage of `consensus_encode` `consensus_decode` under the hood without exposing the `Encodable` `Decodable` Traits.

> One note, either the traits Serialize and Deserialize have to have there docs changed or else PSBT shouldn't implement them because doing so conflicts with their documentation. I kind of prefer to just implement two methods on PSBT, not sure how others feel about this. Also its all a bit of a tangle so there is probable more than one way to go forward and I do not have a picture in mind of the final destination :)


What's wrong with the doc:
```
//! PSBT serialization.
//!
//! Defines traits used for (de)serializing PSBT values into/from raw
//! bytes from/as PSBT key-value pairs.
```
The [spec](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#specification) includes the whole \<psbt\> as a key-value pair itself

We can implement two methods on PSBT but should also implement them for every non-Consensus PSBT related data that implements consensus de/encode now to fix the kernel of the issue

DanGould: The bip 174 `<keypair> `is defined as `<keypair> := <key> <valuedata>` such that

```
 <keypair> := <key> <value>
 <key> := <keylen> <keytype> <keydata>
 <value> := <valuelen> <valuedata>
```

Our `Pair` struct forgoes <valuelen> because <value> is saved as Vec<u8>, which manages len itself.

https://github.com/rust-bitcoin/rust-bitcoin/blob/5b707069e570bb087d768808d5ea724d67e448c6/src/util/psbt/raw.rs#L31-L41

Instead `Pair` depends consensus `impl Encodable for Vec<u8>` which defines consensus_encode as consensus_encode_with_size, serializing the length as defined in spec.

**Is this desirable since Vec<u8> already manages `len`?** Or should our internal representation be a 1:1 map of the spec struct to memory? What mental model makes the most sense?

This snagged me when I took `buf.extend(Pair.value: Vec<u8>)` granted for <value> and not as just <valuedata>.





DanGould: Another major question I have is whether we want to keep the `serialize(&self) -> Vec<u8>` signatuer, great for in memory operations, or `consensus_encode(&self, &mut w) -> Result<usize, io::Error>`  with results. The serialize is nice because we don't have to account for whole categories of errors, while the latter is nice to write to other mediums and return errors from things like the file system if a disk were ejected.
apoelstra: If we impl `consensus_encode` then we get [`serialize()` for free](https://docs.rs/bitcoin/latest/bitcoin/consensus/encode/fn.serialize.html).
DanGould: > If we impl `consensus_encode` then we get [`serialize()` for free](https://docs.rs/bitcoin/latest/bitcoin/consensus/encode/fn.serialize.html).

@apoelstra now I'm confused. Isn't this issue trying to fix that all these application layer messages use `impl Encodable`/`impl Encodable` but should not? re: #934 
Kixunil: I'd love to have a `genio`-style trait with associated `Error`. If this is considered too much then probably impl for writer and unwrap the result in `fn serialize`.
apoelstra: > > If we impl `consensus_encode` then we get [`serialize()` for free](https://docs.rs/bitcoin/latest/bitcoin/consensus/encode/fn.serialize.html).
> 
> @apoelstra now I'm confused. Isn't this issue trying to fix that all these application layer messages use `impl Encodable`/`impl Decodable` but should not? re: #934

Ah, yeah, I see. That's pretty annoying. To answer your original question yes, I woud like to keep the `serialize(&self) -> Vec<u8>` siganture.
DanGould: This should work end to end now. I welcome feedback on the design. I'm not sure why the fuzz test fails yet.

@Kixunil I agree this Pr is prerequisite to fixing #837, but I think the fix another Pr. The `consensus` / `psbt` untangling looks more involved than it appears on the surface in my brief attempt. One thing would be to bring over an `io::Error`  psbt::Error which is somewhat involved since io::Error breaks `#[derive(Eq)]` which is required in tests. My attempt building on this pr is [on this branch](https://github.com/DanGould/rust-bitcoin/tree/psbt-ser-error)
Kixunil: @DanGould I believe that the best error untangling would be to make decodable error associated.

Regarding fuzzing, weird I don't see the reproducer in the output.
DanGould: > I believe that the best error untangling would be to make decodable error associated. -- @Kixunil 

I took a stab keeping the `io::Error` as associated `encode::Error` in [this commit](https://github.com/DanGould/rust-bitcoin/commit/f47efcbec5437b7b17d6eedc69a65fb29908ca7d). It works.

I wonder however, is a problem to instantiate `consensus::encode::Error` in this module for `io::Error`? This instance does not come from `consensus` module behavior? I would not consider this solution a regression since we were already using `encode::Error` in this case before, but it rhymes with the design problem this PR intends to fix.

Kixunil: I meant having associated `Error` type at `Decodable` trait. Encoding should only fail if the writer fails so we should have `io::Error` there (at least until we have something like `genio`).

> is a problem to instantiate consensus::encode::Error in this module for `io::Error`?

It certainly does look strange. Maybe we should have this shared type:

```rust
/// Error used when reading or decoding fails.
pub enum ReadError<Io, Decode> {
    /// Reading failed
    Io(Io),
    /// Decoding failed
    Decode(Decode), // consensus and PSBT error here
}
```

However this one will be annoying to use with `?` :( We could have `ResultExt` to provide `decode()` and `io()` methods to make it easier.

If that's not acceptable then I think deduplicated IO error is better.
DanGould: @tcharding @sanket1729 would you please take a look at this? This should be a complete, working replacement.
tcharding: I don't think the error stuff is perfect but this PR looks like progress to me. Thanks @DanGould!
DanGould: I plan to fix error stuff in the next PR. Now we have a way for it to be fixed 🫡
tcharding: I believe your fuzz CI fail is because there are a bunch of funny files in `hfuzz_workspace/deserialize_psbt`.
DanGould: @tcharding are you implying this is an issue with the code, the test, or the test environment with hfuzz_workspace? I'm guessing this a caching CI environment issue. hfuzz_workspace does not look like a source file.
DanGould: @sanket1729 Thank you for clarifying. I didn't realize "62" was the fuzz input sitting by its lonesome in console output there.

I made one more ammendment in this recent push, using `crate::io::Cursor` instead of finding the right `Cursor` switching  based on `cfg` features:

https://github.com/rust-bitcoin/rust-bitcoin/blob/27362c10f51682ad2a64312413c787195f3346a0/bitcoin/src/psbt/macros.rs#L55-L60
tcharding: Guess I was wrong :)
apoelstra: In  1b64e702aa838a54f83f5a2267657d2c41d8e8e9

`cargo +nightly build --features=base64` fails with
```

error[E0277]: the trait bound `PartiallySignedTransaction: Encodable` is not satisfied
   --> bitcoin/src/psbt/mod.rs:793:75
    |
793 |             write!(f, "{}", Base64Display::with_config(&encode::serialize(self), base64::STANDARD))
    |                                                         ----------------- ^^^^ the trait `Encodable` is not implemented for `PartiallySignedTransaction`
    |                                                         |
    |                                                         required by a bound introduced by this call
    |
    = help: the following other types implement trait `Encodable`:
              &'a T
              &'a mut T
              (T0, T1)
              (T0, T1, T2)
              (T0, T1, T2, T3)
              (T0, T1, T2, T3, T4)
              (T0, T1, T2, T3, T4, T5)
              (T0, T1, T2, T3, T4, T5, T6)
            and 107 others
note: required by a bound in `serialize`
   --> bitcoin/src/consensus/encode.rs:118:21
    |
118 | pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
    |                     ^^^^^^^^^ required by this bound in `serialize`

error[E0277]: the trait bound `PartiallySignedTransaction: Decodable` is not satisfied
   --> bitcoin/src/psbt/mod.rs:803:13
    |
803 |             encode::deserialize(&data).map_err(PsbtParseError::PsbtEncoding)
    |             ^^^^^^^^^^^^^^^^^^^ the trait `Decodable` is not implemented for `PartiallySignedTransaction`
    |
    = help: the following other types implement trait `Decodable`:
              (T0, T1)
              (T0, T1, T2)
              (T0, T1, T2, T3)
              (T0, T1, T2, T3, T4)
              (T0, T1, T2, T3, T4, T5)
              (T0, T1, T2, T3, T4, T5, T6)
              (T0, T1, T2, T3, T4, T5, T6, T7)
              AddrV2
            and 101 others
note: required by a bound in `deserialize`
   --> bitcoin/src/consensus/encode.rs:132:23
    |
132 | pub fn deserialize<T: Decodable>(data: &[u8]) -> Result<T, Error> {
    |                       ^^^^^^^^^ required by this bound in `deserialize`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `bitcoin` due to 2 previous errors
```

Patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index eb1d178c..c4113cc6 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -14,26 +14,26 @@ jobs:
             env:
               DO_COV: true
               DO_LINT: true
-              AS_DEPENDENCY: false
+              AS_DEPENDENCY: true
               DO_NO_STD: true
               DO_FEATURE_MATRIX: true # Currently only used in hashes crate.
               DO_SCHEMARS_TESTS: true # Currently only used in hashes crate.
           - rust: beta
             env:
-              AS_DEPENDENCY: false
+              AS_DEPENDENCY: true
               DO_NO_STD: true
           - rust: nightly
             env:
               DO_BENCH: true
-              AS_DEPENDENCY: false
+              AS_DEPENDENCY: true
               DO_NO_STD: true
               DO_DOCS: true
           - rust: 1.41.1
             env:
-              AS_DEPENDENCY: false
+              AS_DEPENDENCY: true
           - rust: 1.47
             env:
-              AS_DEPENDENCY: false
+              AS_DEPENDENCY: true
               DO_NO_STD: true
     steps:
       - name: Checkout Crate
diff --git a/Cargo.toml b/Cargo.toml
index 160c9a73..c68aac6f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,2 @@
 [workspace]
 members = ["bitcoin", "hashes", "internals"]
-
-[patch.crates-io.bitcoin_hashes]
-path = "hashes"
diff --git a/bitcoin/CHANGELOG.md b/bitcoin/CHANGELOG.md
index 099646b7..64859f02 100644
--- a/bitcoin/CHANGELOG.md
+++ b/bitcoin/CHANGELOG.md
@@ -1,12 +1,3 @@
-# 0.30 - 2023-XX-XX "The First Crate-Smashing Release"
-
-* Drop `PackedLockTime`, [replace with richer `LockTime` everywhere](https://github.com/rust-bitcoin/rust-bitcoin/pull/1330)
-  Be aware that `LockTime` does not have an `Ord` implementation, so users who need a
-  total ordering on locktimes will be forced to wrap this type. In `Transaction`, which
-  contains a `LockTime` but is `Ord`, we have manually sorted the locktimes based on
-  their consensus encoding. This ordering is somewhat arbitrary -- there is no total
-  ordering on locktimes since they may be measured in either blocks or seconds.
-
 # 0.29 - 2022-07-20 "Edition 2018 Release"
 
 As promised, this is our quick release to bring on edition 2018 by increasing our MSRV to Rust
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index f3038367..c903f384 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -38,7 +38,7 @@ rustdoc-args = ["--cfg", "docsrs"]
 bitcoin-internals = { path = "../internals" }
 bech32 = { version = "0.9.0", default-features = false }
 bitcoin_hashes = { version = "0.11.0", default-features = false }
-secp256k1 = { version = "0.25.0", default-features = false, features = ["bitcoin_hashes"] }
+secp256k1 = { version = "0.24.0", default-features = false, features = ["bitcoin_hashes"] }
 core2 = { version = "0.3.0", optional = true, default-features = false }
 
 base64 = { version = "0.13.0", optional = true }
@@ -51,7 +51,7 @@ hashbrown = { version = "0.8", optional = true }
 serde_json = "1.0.0"
 serde_test = "1.0.19"
 serde_derive = "1.0.103"
-secp256k1 = { version = "0.25.0", features = [ "recovery", "rand-std" ] }
+secp256k1 = { version = "0.24.0", features = [ "recovery", "rand-std" ] }
 bincode = "1.3.1"
 
 [[example]]
diff --git a/bitcoin/embedded/Cargo.toml b/bitcoin/embedded/Cargo.toml
index 45bea733..63742c10 100644
--- a/bitcoin/embedded/Cargo.toml
+++ b/bitcoin/embedded/Cargo.toml
@@ -27,6 +27,3 @@ bench = false
 codegen-units = 1 # better optimizations
 debug = true # symbols are nice and they don't increase the size on Flash
 lto = true # better optimizations
-
-[patch.crates-io.bitcoin_hashes]
-path = "../../hashes"
diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs
index b6405d0b..b778251a 100644
--- a/bitcoin/examples/ecdsa-psbt.rs
+++ b/bitcoin/examples/ecdsa-psbt.rs
@@ -236,16 +236,14 @@ fn update_psbt(&self, mut psbt: Psbt) -> Result<Psbt> {
     /// Finalizes the PSBT, in BIP174 parlance this is the 'Finalizer'.
     /// This is just an example. For a production-ready PSBT Finalizer, use [rust-miniscript](https://docs.rs/miniscript/latest/miniscript/psbt/trait.PsbtExt.html#tymethod.finalize)
     fn finalize_psbt(&self, mut psbt: Psbt) -> Result<Psbt> {
-        use bitcoin::psbt::serialize::Serialize;
-
         if psbt.inputs.is_empty() {
             return Err(psbt::SignError::MissingInputUtxo.into());
         }
 
         let sigs: Vec<_> = psbt.inputs[0].partial_sigs.values().collect();
         let mut script_witness: Witness = Witness::new();
-        script_witness.push(&sigs[0].serialize());
-        script_witness.push(self.input_xpub.to_pub().serialize());
+        script_witness.push(&sigs[0].to_vec());
+        script_witness.push(self.input_xpub.to_pub().to_bytes());
 
         psbt.inputs[0].final_script_witness = Some(script_witness);
 
diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs
index 64928d01..ecde74c0 100644
--- a/bitcoin/examples/taproot-psbt.rs
+++ b/bitcoin/examples/taproot-psbt.rs
@@ -85,7 +85,6 @@
 use bitcoin::hashes::Hash;
 use bitcoin::key::XOnlyPublicKey;
 use bitcoin::opcodes::all::{OP_CHECKSIG, OP_CLTV, OP_DROP};
-use bitcoin::psbt::serialize::Serialize;
 use bitcoin::psbt::{self, Input, Output, Psbt, PsbtSighashType};
 use bitcoin::schnorr::{self, TapTweak};
 use bitcoin::secp256k1::{Message, Secp256k1};
@@ -317,7 +316,7 @@ fn generate_bip86_key_spend_tx(
     // FINALIZER
     psbt.inputs.iter_mut().for_each(|input| {
         let mut script_witness: Witness = Witness::new();
-        script_witness.push(input.tap_key_sig.unwrap().serialize());
+        script_witness.push(input.tap_key_sig.unwrap().to_vec());
         input.final_script_witness = Some(script_witness);
 
         // Clear all the data fields as per the spec.
@@ -397,7 +396,7 @@ fn create_inheritance_funding_tx(
 
         // Build up the leaf script and combine with internal key into a taproot commitment
         let script = Self::time_lock_script(lock_time, beneficiary_key);
-        let leaf_hash = script.tapscript_leaf_hash();
+        let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::TapScript);
 
         let taproot_spend_info = TaprootBuilder::new()
             .add_leaf(0, script.clone())?
@@ -497,7 +496,7 @@ fn refresh_tx(
             )
             .unwrap();
             let script = Self::time_lock_script(lock_time, beneficiary_key);
-            let leaf_hash = script.tapscript_leaf_hash();
+            let leaf_hash = TapLeafHash::from_script(&script, LeafVersion::TapScript);
 
             let taproot_spend_info = TaprootBuilder::new()
                 .add_leaf(0, script.clone())?
@@ -550,7 +549,7 @@ fn refresh_tx(
             // FINALIZER
             psbt.inputs.iter_mut().for_each(|input| {
                 let mut script_witness: Witness = Witness::new();
-                script_witness.push(input.tap_key_sig.unwrap().serialize());
+                script_witness.push(input.tap_key_sig.unwrap().to_vec());
                 input.final_script_witness = Some(script_witness);
 
                 // Clear all the data fields as per the spec.
@@ -686,10 +685,10 @@ fn spend_inheritance(
         psbt.inputs.iter_mut().for_each(|input| {
             let mut script_witness: Witness = Witness::new();
             for (_, signature) in input.tap_script_sigs.iter() {
-                script_witness.push(signature.serialize());
+                script_witness.push(signature.to_vec());
             }
             for (control_block, (script, _)) in input.tap_scripts.iter() {
-                script_witness.push(script.serialize());
+                script_witness.push(script.to_bytes());
                 script_witness.push(control_block.serialize());
             }
             input.final_script_witness = Some(script_witness);
diff --git a/bitcoin/fuzz/Cargo.toml b/bitcoin/fuzz/Cargo.toml
index dc07ba07..c8e8ae9f 100644
--- a/bitcoin/fuzz/Cargo.toml
+++ b/bitcoin/fuzz/Cargo.toml
@@ -63,6 +63,3 @@ path = "fuzz_targets/script_bytes_to_asm_fmt.rs"
 [[bin]]
 name = "deserialize_witness"
 path = "fuzz_targets/deserialize_witness.rs"
-
-[patch.crates-io.bitcoin_hashes]
-path = "../../hashes"
diff --git a/bitcoin/fuzz/fuzz_targets/deserialize_psbt.rs b/bitcoin/fuzz/fuzz_targets/deserialize_psbt.rs
index 7556679b..284c8655 100644
--- a/bitcoin/fuzz/fuzz_targets/deserialize_psbt.rs
+++ b/bitcoin/fuzz/fuzz_targets/deserialize_psbt.rs
@@ -1,14 +1,14 @@
 extern crate bitcoin;
 
 fn do_test(data: &[u8]) {
-    let psbt: Result<bitcoin::psbt::PartiallySignedTransaction, _> = bitcoin::consensus::encode::deserialize(data);
+    let psbt: Result<bitcoin::psbt::PartiallySignedTransaction, _> = bitcoin::psbt::Psbt::deserialize(data);
     match psbt {
         Err(_) => {},
         Ok(psbt) => {
-            let ser = bitcoin::consensus::encode::serialize(&psbt);
-            let deser: bitcoin::psbt::PartiallySignedTransaction  = bitcoin::consensus::encode::deserialize(&ser).unwrap();
+            let ser = bitcoin::psbt::Psbt::serialize(&psbt);
+            let deser = bitcoin::psbt::Psbt::deserialize(&ser).unwrap();
             // Since the fuzz data could order psbt fields differently, we compare to our deser/ser instead of data
-            assert_eq!(ser, bitcoin::consensus::encode::serialize(&deser));
+            assert_eq!(ser, bitcoin::psbt::Psbt::serialize(&deser));
         }
     }
 }
diff --git a/bitcoin/src/address.rs b/bitcoin/src/address.rs
index 989bbd7b..a0da8586 100644
--- a/bitcoin/src/address.rs
+++ b/bitcoin/src/address.rs
@@ -447,7 +447,7 @@ pub fn p2sh(script: &script::Script) -> Result<Payload, Error> {
     pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
         Ok(Payload::WitnessProgram {
             version: WitnessVersion::V0,
-            program: pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.as_ref().to_vec(),
+            program: pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.to_vec(),
         })
     }
 
@@ -455,7 +455,7 @@ pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
     pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
         let builder = script::Builder::new()
             .push_int(0)
-            .push_slice(pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.as_ref());
+            .push_slice(&pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
 
         Ok(Payload::ScriptHash(builder.into_script().script_hash()))
     }
@@ -464,14 +464,14 @@ pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
     pub fn p2wsh(script: &script::Script) -> Payload {
         Payload::WitnessProgram {
             version: WitnessVersion::V0,
-            program: script.wscript_hash().as_ref().to_vec(),
+            program: script.wscript_hash().to_vec(),
         }
     }
 
     /// Create a pay to script payload that embeds a witness pay to script hash address
     pub fn p2shwsh(script: &script::Script) -> Payload {
         let ws =
-            script::Builder::new().push_int(0).push_slice(script.wscript_hash().as_ref()).into_script();
+            script::Builder::new().push_int(0).push_slice(&script.wscript_hash()).into_script();
 
         Payload::ScriptHash(ws.script_hash())
     }
@@ -502,8 +502,8 @@ pub fn p2tr_tweaked(output_key: TweakedPublicKey) -> Payload {
     /// Returns a byte slice of the payload
     pub fn as_bytes(&self) -> &[u8] {
         match self {
-            Payload::ScriptHash(hash) => hash.as_ref(),
-            Payload::PubkeyHash(hash) => hash.as_ref(),
+            Payload::ScriptHash(hash) => hash,
+            Payload::PubkeyHash(hash) => hash,
             Payload::WitnessProgram { program, .. } => program,
         }
     }
@@ -737,9 +737,9 @@ pub fn is_related_to_pubkey(&self, pubkey: &PublicKey) -> bool {
         let payload = self.payload.as_bytes();
         let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner);
 
-        (*pubkey_hash.as_ref() == *payload)
+        (*pubkey_hash == *payload)
             || (xonly_pubkey.serialize() == *payload)
-            || (*segwit_redeem_hash(&pubkey_hash).as_ref() == *payload)
+            || (*segwit_redeem_hash(&pubkey_hash) == *payload)
     }
 
     /// Returns true if the supplied xonly public key can be used to derive the address.
@@ -871,10 +871,10 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f
 }
 
 /// Convert a byte array of a pubkey hash into a segwit redeem hash
-fn segwit_redeem_hash(pubkey_hash: &PubkeyHash) -> crate::hashes::hash160::Hash {
+fn segwit_redeem_hash(pubkey_hash: &[u8]) -> crate::hashes::hash160::Hash {
     let mut sha_engine = sha256::Hash::engine();
     sha_engine.input(&[0, 20]);
-    sha_engine.input(pubkey_hash.as_ref());
+    sha_engine.input(pubkey_hash);
     crate::hashes::hash160::Hash::from_engine(sha_engine)
 }
 
diff --git a/bitcoin/src/amount.rs b/bitcoin/src/amount.rs
index d1c50744..b121dc9a 100644
--- a/bitcoin/src/amount.rs
+++ b/bitcoin/src/amount.rs
@@ -458,7 +458,7 @@ fn fmt_satoshi_in(
 
 /// Amount
 ///
-/// The [Amount] type can be used to express Bitcoin amounts that support
+/// The [Amount] type can be used to express Bitcoin amounts that supports
 /// arithmetic and conversion to various denominations.
 ///
 ///
@@ -802,7 +802,7 @@ enum DisplayStyle {
 
 /// SignedAmount
 ///
-/// The [SignedAmount] type can be used to express Bitcoin amounts that support
+/// The [SignedAmount] type can be used to express Bitcoin amounts that supports
 /// arithmetic and conversion to various denominations.
 ///
 ///
diff --git a/bitcoin/src/blockdata/locktime/absolute.rs b/bitcoin/src/blockdata/locktime/absolute.rs
index 34c255f3..81cc8501 100644
--- a/bitcoin/src/blockdata/locktime/absolute.rs
+++ b/bitcoin/src/blockdata/locktime/absolute.rs
@@ -43,14 +43,6 @@
 /// Used for transaction lock time (`nLockTime` in Bitcoin Core and [`crate::Transaction::lock_time`]
 /// in this library) and also for the argument to opcode 'OP_CHECKLOCKTIMEVERIFY`.
 ///
-/// ### Note on ordering
-///
-/// Because locktimes may be height- or time-based, and these metrics are incommensurate, there
-/// is no total ordering on locktimes. We therefore have implemented [`PartialOrd`] but not [`Ord`].
-/// For [`crate::Transaction`], which has a locktime field, we implement a total ordering to make
-/// it easy to store transactions in sorted data structures, and use the locktime's 32-bit integer
-/// consensus encoding to order it.
-///
 /// ### Relevant BIPs
 ///
 /// * [BIP-65 OP_CHECKLOCKTIMEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0065.mediawiki)
diff --git a/bitcoin/src/blockdata/script.rs b/bitcoin/src/blockdata/script.rs
index dc3a0f91..df2c18d0 100644
--- a/bitcoin/src/blockdata/script.rs
+++ b/bitcoin/src/blockdata/script.rs
@@ -209,12 +209,6 @@ pub fn wscript_hash(&self) -> WScriptHash {
         WScriptHash::hash(self.as_bytes())
     }
 
-    /// Computes leaf hash of tapscript.
-    #[inline]
-    pub fn tapscript_leaf_hash(&self) -> TapLeafHash {
-        TapLeafHash::from_script(self, LeafVersion::TapScript)
-    }
-
     /// Returns the length in bytes of the script.
     #[inline]
     pub fn len(&self) -> usize { self.0.len() }
@@ -244,7 +238,7 @@ pub fn to_v0_p2wsh(&self) -> ScriptBuf {
     /// the current script, assuming that the script is a Tapscript.
     #[inline]
     pub fn to_v1_p2tr<C: Verification>(&self, secp: &Secp256k1<C>, internal_key: UntweakedPublicKey) -> ScriptBuf {
-        let leaf_hash = self.tapscript_leaf_hash();
+        let leaf_hash = TapLeafHash::from_script(self, LeafVersion::TapScript);
         let merkle_root = TapBranchHash::from_inner(leaf_hash.into_inner());
         ScriptBuf::new_v1_p2tr(secp, internal_key, Some(merkle_root))
     }
@@ -2350,10 +2344,6 @@ fn script_hashes() {
         let script = hex_script!("410446ef0102d1ec5240f0d061a4246c1bdef63fc3dbab7733052fbbf0ecd8f41fc26bf049ebb4f9527f374280259e7cfa99c48b0e3f39c51347a19a5819651503a5ac");
         assert_eq!(script.script_hash().to_hex(), "8292bcfbef1884f73c813dfe9c82fd7e814291ea");
         assert_eq!(script.wscript_hash().to_hex(), "3e1525eb183ad4f9b3c5fa3175bdca2a52e947b135bbb90383bf9f6408e2c324");
-	assert_eq!(
-	    hex_script!("20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac").tapscript_leaf_hash().to_hex(),
-	    "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21"
-	);
     }
 
     #[test]
diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs
index f41c2243..2a4b374b 100644
--- a/bitcoin/src/blockdata/transaction.rs
+++ b/bitcoin/src/blockdata/transaction.rs
@@ -225,38 +225,6 @@ impl TxIn {
     pub fn enables_lock_time(&self) -> bool {
         self.sequence != Sequence::MAX
     }
-
-    /// The weight of the TxIn when it's included in a legacy transaction (i.e., a transaction
-    /// having only legacy inputs).
-    ///
-    /// The witness weight is ignored here even when the witness is non-empty.
-    /// If you want the witness to be taken into account, use `TxIn::segwit_weight` instead.
-    ///
-    /// Keep in mind that when adding a TxIn to a transaction, the total weight of the transaction
-    /// might increase more than `TxIn::legacy_weight`. This happens when the new input added causes
-    /// the input length `VarInt` to increase its encoding length.
-    pub fn legacy_weight(&self) -> usize {
-        let script_sig_size = self.script_sig.len();
-        // Size in vbytes:
-        // previous_output (36) + script_sig varint len + script_sig push + sequence (4)
-        // We then multiply by 4 to convert to WU
-        (36 + VarInt(script_sig_size as u64).len() + script_sig_size + 4) * 4
-    }
-
-    /// The weight of the TxIn when it's included in a segwit transaction (i.e., a transcation
-    /// having at least one segwit input).
-    ///
-    /// This always takes into account the witness, even when empty, in which
-    /// case 1WU for the witness length varint (`00`) is included.
-    ///
-    /// Keep in mind that when adding a TxIn to a transaction, the total weight of the transaction
-    /// might increase more than `TxIn::segwit_weight`. This happens when:
-    /// - the new input added causes the input length `VarInt` to increase its encoding length
-    /// - the new input is the first segwit input added - this will add an additional 2WU to the
-    ///   transaction weight to take into account the segwit marker
-    pub fn segwit_weight(&self) -> usize {
-        self.legacy_weight() + self.witness.serialized_len()
-    }
 }
 
 impl Default for TxIn {
@@ -501,21 +469,6 @@ pub struct TxOut {
     pub script_pubkey: ScriptBuf
 }
 
-impl TxOut {
-    /// The weight of the txout in witness units
-    ///
-    /// Keep in mind that when adding a TxOut to a transaction, the total weight of the transaction
-    /// might increase more than `TxOut::weight`. This happens when the new output added causes
-    /// the output length `VarInt` to increase its encoding length.
-    pub fn weight(&self) -> usize {
-        let script_len = self.script_pubkey.len();
-        // In vbytes:
-        // value (8) + script varint len + script push
-        // Then we multiply by 4 to convert to WU
-        (8 + VarInt(script_len as u64).len() + script_len) * 4
-    }
-}
-
 // This is used as a "null txout" in consensus signing code.
 impl Default for TxOut {
     fn default() -> TxOut {
@@ -627,19 +580,6 @@ pub fn map_err<E2, F>(self, f: F) -> EncodeSigningDataResult<E2> where F: FnOnce
 ///
 /// We therefore deviate from the spec by always using the Segwit witness encoding
 /// for 0-input transactions, which results in unambiguously parseable transactions.
-///
-/// ### A note on ordering
-///
-/// This type implements `Ord`, even though it contains a locktime, which is not
-/// itself `Ord`. This was done to simplify applications that may need to hold
-/// transactions inside a sorted container. We have ordered the locktimes based
-/// on their representation as a `u32`, which is not a semantically meaningful
-/// order, and therefore the ordering on `Transaction` itself is not semantically
-/// meaningful either.
-///
-/// The ordering is, however, consistent with the ordering present in this library
-/// before this change, so users should not notice any breakage (here) when
-/// transitioning from 0.29 to 0.30.
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
@@ -1566,50 +1506,6 @@ fn sequence_from_str_hex_invalid_hex_should_err() {
         let result = Sequence::from_hex_str(hex);
         assert!(result.is_err());
     }
-
-    #[test]
-    fn txin_txout_weight_tests() {
-        // [(is_segwit, tx_hex)]
-        let txs = [
-                // one segwit input (P2WPKH)
-                (true, "020000000001018a763b78d3e17acea0625bf9e52b0dc1beb2241b2502185348ba8ff4a253176e0100000000ffffffff0280d725000000000017a914c07ed639bd46bf7087f2ae1dfde63b815a5f8b488767fda20300000000160014869ec8520fa2801c8a01bfdd2e82b19833cd0daf02473044022016243edad96b18c78b545325aaff80131689f681079fb107a67018cb7fb7830e02205520dae761d89728f73f1a7182157f6b5aecf653525855adb7ccb998c8e6143b012103b9489bde92afbcfa85129a82ffa512897105d1a27ad9806bded27e0532fc84e700000000"),
-                // one segwit input (P2WSH)
-                (true, "01000000000101a3ccad197118a2d4975fadc47b90eacfdeaf8268adfdf10ed3b4c3b7e1ad14530300000000ffffffff0200cc5501000000001976a91428ec6f21f4727bff84bb844e9697366feeb69f4d88aca2a5100d00000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220548f11130353b3a8f943d2f14260345fc7c20bde91704c9f1cbb5456355078cd0220383ed4ed39b079b618bcb279bbc1f2ca18cb028c4641cb522c9c5868c52a0dc20147304402203c332ecccb3181ca82c0600520ee51fee80d3b4a6ab110945e59475ec71e44ac0220679a11f3ca9993b04ccebda3c834876f353b065bb08f50076b25f5bb93c72ae1016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000"),
-                // one segwit input (P2WPKH) and two legacy inputs (P2PKH)
-                (true, "010000000001036b6b6ac7e34e97c53c1cc74c99c7948af2e6aac75d8778004ae458d813456764000000006a473044022001deec7d9075109306320b3754188f81a8236d0d232b44bc69f8309115638b8f02204e17a5194a519cf994d0afeea1268740bdc10616b031a521113681cc415e815c012103488d3272a9fad78ee887f0684cb8ebcfc06d0945e1401d002e590c7338b163feffffffffc75bd7aa6424aee972789ec28ba181254ee6d8311b058d165bd045154d7660b0000000006b483045022100c8641bcbee3e4c47a00417875015d8c5d5ea918fb7e96f18c6ffe51bc555b401022074e2c46f5b1109cd79e39a9aa203eadd1d75356415e51d80928a5fb5feb0efee0121033504b4c6dfc3a5daaf7c425aead4c2dbbe4e7387ce8e6be2648805939ecf7054ffffffff494df3b205cd9430a26f8e8c0dc0bb80496fbc555a524d6ea307724bc7e60eee0100000000ffffffff026d861500000000001976a9145c54ed1360072ebaf56e87693b88482d2c6a101588ace407000000000000160014761e31e2629c6e11936f2f9888179d60a5d4c1f900000247304402201fa38a67a63e58b67b6cfffd02f59121ca1c8a1b22e1efe2573ae7e4b4f06c2b022002b9b431b58f6e36b3334fb14eaecee7d2f06967a77ef50d8d5f90dda1057f0c01210257dc6ce3b1100903306f518ee8fa113d778e403f118c080b50ce079fba40e09a00000000"),
-                // three legacy inputs (P2PKH)
-                (false, "0100000003e4d7be4314204a239d8e00691128dca7927e19a7339c7948bde56f669d27d797010000006b483045022100b988a858e2982e2daaf0755b37ad46775d6132057934877a5badc91dee2f66ff022020b967c1a2f0916007662ec609987e951baafa6d4fda23faaad70715611d6a2501210254a2dccd8c8832d4677dc6f0e562eaaa5d11feb9f1de2c50a33832e7c6190796ffffffff9e22eb1b3f24c260187d716a8a6c2a7efb5af14a30a4792a6eeac3643172379c000000006a47304402207df07f0cd30dca2cf7bed7686fa78d8a37fe9c2254dfdca2befed54e06b779790220684417b8ff9f0f6b480546a9e90ecee86a625b3ea1e4ca29b080da6bd6c5f67e01210254a2dccd8c8832d4677dc6f0e562eaaa5d11feb9f1de2c50a33832e7c6190796ffffffff1123df3bfb503b59769731da103d4371bc029f57979ebce68067768b958091a1000000006a47304402207a016023c2b0c4db9a7d4f9232fcec2193c2f119a69125ad5bcedcba56dd525e02206a734b3a321286c896759ac98ebfd9d808df47f1ce1fbfbe949891cc3134294701210254a2dccd8c8832d4677dc6f0e562eaaa5d11feb9f1de2c50a33832e7c6190796ffffffff0200c2eb0b000000001976a914e5eb3e05efad136b1405f5c2f9adb14e15a35bb488ac88cfff1b000000001976a9144846db516db3130b7a3c92253599edec6bc9630b88ac00000000"),
-                // one segwit input (P2TR)
-                (true, "01000000000101b5cee87f1a60915c38bb0bc26aaf2b67be2b890bbc54bb4be1e40272e0d2fe0b0000000000ffffffff025529000000000000225120106daad8a5cb2e6fc74783714273bad554a148ca2d054e7a19250e9935366f3033760000000000002200205e6d83c44f57484fd2ef2a62b6d36cdcd6b3e06b661e33fd65588a28ad0dbe060141df9d1bfce71f90d68bf9e9461910b3716466bfe035c7dbabaa7791383af6c7ef405a3a1f481488a91d33cd90b098d13cb904323a3e215523aceaa04e1bb35cdb0100000000"),
-                // one legacy input (P2PKH)
-                (false, "0100000001c336895d9fa674f8b1e294fd006b1ac8266939161600e04788c515089991b50a030000006a47304402204213769e823984b31dcb7104f2c99279e74249eacd4246dabcf2575f85b365aa02200c3ee89c84344ae326b637101a92448664a8d39a009c8ad5d147c752cbe112970121028b1b44b4903c9103c07d5a23e3c7cf7aeb0ba45ddbd2cfdce469ab197381f195fdffffff040000000000000000536a4c5058325bb7b7251cf9e36cac35d691bd37431eeea426d42cbdecca4db20794f9a4030e6cb5211fabf887642bcad98c9994430facb712da8ae5e12c9ae5ff314127d33665000bb26c0067000bb0bf00322a50c300000000000017a9145ca04fdc0a6d2f4e3f67cfeb97e438bb6287725f8750c30000000000001976a91423086a767de0143523e818d4273ddfe6d9e4bbcc88acc8465003000000001976a914c95cbacc416f757c65c942f9b6b8a20038b9b12988ac00000000"),
-            ];
-
-        let empty_transaction_size = Transaction {
-            version: 0,
-            lock_time: absolute::LockTime::ZERO,
-            input: vec![],
-            output: vec![],
-        }
-        .weight();
-
-        for (is_segwit, tx) in &txs {
-            let txin_weight = if *is_segwit {
-                TxIn::segwit_weight
-            } else {
-                TxIn::legacy_weight
-            };
-            let tx: Transaction = deserialize(Vec::from_hex(tx).unwrap().as_slice()).unwrap();
-            // The empty tx size doesn't include the segwit marker (`0001`), so, in case of segwit txs,
-            // we have to manually add it ourselves
-            let segwit_marker_size = if *is_segwit { 2 } else { 0 };
-            let calculated_size = empty_transaction_size
-                + segwit_marker_size
-                + tx.input.iter().fold(0, |sum, i| sum + txin_weight(i))
-                + tx.output.iter().fold(0, |sum, o| sum + o.weight());
-            assert_eq!(calculated_size, tx.weight());
-        }
-    }
 }
 
 #[cfg(bench)]
diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs
index 7c54ee7d..3061961e 100644
--- a/bitcoin/src/blockdata/witness.rs
+++ b/bitcoin/src/blockdata/witness.rs
@@ -312,11 +312,9 @@ pub fn nth(&self, index: usize) -> Option<&[u8]> {
 
     /// Get Tapscript following BIP341 rules regarding accounting for an annex.
     ///
-    /// This does not guarantee that this represents a P2TR [`Witness`]. It
-    /// merely gets the second to last or third to last element depending on
-    /// the first byte of the last element being equal to 0x50. See
-    /// [Script::is_v1_p2tr](crate::blockdata::script::Script::is_v1_p2tr) to
-    /// check whether this is actually a Taproot witness.
+    /// This does not guarantee that this represents a P2TR [`Witness`].
+    /// It merely gets the second to last or third to last element depending
+    /// on the first byte of the last element being equal to 0x50.
     pub fn tapscript(&self) -> Option<&[u8]> {
         let len = self.len();
         self
diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs
index 1d2311a4..ef3d7344 100644
--- a/bitcoin/src/consensus/encode.rs
+++ b/bitcoin/src/consensus/encode.rs
@@ -159,7 +159,7 @@ pub trait WriteExt : io::Write {
     fn emit_u32(&mut self, v: u32) -> Result<(), io::Error>;
     /// Outputs a 16-bit unsigned integer.
     fn emit_u16(&mut self, v: u16) -> Result<(), io::Error>;
-    /// Outputs an 8-bit unsigned integer.
+    /// Outputs a 8-bit unsigned integer.
     fn emit_u8(&mut self, v: u8) -> Result<(), io::Error>;
 
     /// Outputs a 64-bit signed integer.
@@ -168,7 +168,7 @@ pub trait WriteExt : io::Write {
     fn emit_i32(&mut self, v: i32) -> Result<(), io::Error>;
     /// Outputs a 16-bit signed integer.
     fn emit_i16(&mut self, v: i16) -> Result<(), io::Error>;
-    /// Outputs an 8-bit signed integer.
+    /// Outputs a 8-bit signed integer.
     fn emit_i8(&mut self, v: i8) -> Result<(), io::Error>;
 
     /// Outputs a boolean.
@@ -186,7 +186,7 @@ pub trait ReadExt : io::Read {
     fn read_u32(&mut self) -> Result<u32, Error>;
     /// Reads a 16-bit unsigned integer.
     fn read_u16(&mut self) -> Result<u16, Error>;
-    /// Reads an 8-bit unsigned integer.
+    /// Reads a 8-bit unsigned integer.
     fn read_u8(&mut self) -> Result<u8, Error>;
 
     /// Reads a 64-bit signed integer.
@@ -195,7 +195,7 @@ pub trait ReadExt : io::Read {
     fn read_i32(&mut self) -> Result<i32, Error>;
     /// Reads a 16-bit signed integer.
     fn read_i16(&mut self) -> Result<i16, Error>;
-    /// Reads an 8-bit signed integer.
+    /// Reads a 8-bit signed integer.
     fn read_i8(&mut self) -> Result<i8, Error>;
 
     /// Reads a boolean.
diff --git a/bitcoin/src/hash_types.rs b/bitcoin/src/hash_types.rs
index b8b28cce..816dde21 100644
--- a/bitcoin/src/hash_types.rs
+++ b/bitcoin/src/hash_types.rs
@@ -46,12 +46,6 @@ mod newtypes {
     hash_newtype!(Wtxid, sha256d::Hash, 32, doc="A bitcoin witness transaction ID.");
     hash_newtype!(BlockHash, sha256d::Hash, 32, doc="A bitcoin block hash.");
     hash_newtype!(Sighash, sha256d::Hash, 32, doc="Hash of the transaction according to the signature algorithm");
-    impl secp256k1::ThirtyTwoByteHash for Sighash {
-        fn into_32(self) -> [u8; 32] {
-            use hashes::Hash;
-            *self.as_inner()
-        }
-    }
 
     hash_newtype!(PubkeyHash, hash160::Hash, 20, doc="A hash of a public key.");
     hash_newtype!(ScriptHash, hash160::Hash, 20, doc="A hash of Bitcoin Script bytecode.");
diff --git a/bitcoin/src/network/message.rs b/bitcoin/src/network/message.rs
index 6426f529..1f533fa6 100644
--- a/bitcoin/src/network/message.rs
+++ b/bitcoin/src/network/message.rs
@@ -38,14 +38,14 @@
 pub struct CommandString(Cow<'static, str>);
 
 impl CommandString {
-    /// Converts `&'static str` to `CommandString`
+    /// Convert `&'static str` to `CommandString`
     ///
     /// This is more efficient for string literals than non-static conversions because it avoids
     /// allocation.
     ///
     /// # Errors
     ///
-    /// Returns an error if, and only if, the string is
+    /// Returns an error if and only if the string is
     /// larger than 12 characters in length.
     pub fn try_from_static(s: &'static str) -> Result<CommandString, CommandStringError> {
         Self::try_from_static_cow(s.into())
@@ -582,7 +582,7 @@ fn full_round_ser_der_raw_network_message_test() {
                 flags: BloomFlags::All,
             }),
             NetworkMessage::FilterAdd(FilterAdd { data: script.as_bytes().to_vec() }),
-            NetworkMessage::FilterAdd(FilterAdd { data: hash([29u8; 32]).as_ref().to_vec() }),
+            NetworkMessage::FilterAdd(FilterAdd { data: hash([29u8; 32]).to_vec() }),
             NetworkMessage::FilterClear,
             NetworkMessage::GetCFilters(GetCFilters {
                 filter_type: 2,
diff --git a/bitcoin/src/psbt/macros.rs b/bitcoin/src/psbt/macros.rs
index ddfce8aa..dd820e81 100644
--- a/bitcoin/src/psbt/macros.rs
+++ b/bitcoin/src/psbt/macros.rs
@@ -2,7 +2,7 @@
 
 #[allow(unused_macros)]
 macro_rules! hex_psbt {
-    ($s:expr) => { $crate::consensus::deserialize::<$crate::psbt::PartiallySignedTransaction>(&<$crate::prelude::Vec<u8> as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()) };
+    ($s:expr) => { <$crate::psbt::PartiallySignedTransaction>::deserialize(&<$crate::prelude::Vec<u8> as $crate::hashes::hex::FromHex>::from_hex($s).unwrap()) };
 }
 
 macro_rules! combine {
@@ -40,29 +40,37 @@ fn serialize(&self) -> $crate::prelude::Vec<u8> {
     };
 }
 
-macro_rules! impl_psbtmap_consensus_encoding {
+macro_rules! impl_psbtmap_serialize {
     ($thing:ty) => {
-        impl $crate::consensus::Encodable for $thing {
-            fn consensus_encode<W: $crate::io::Write + ?Sized>(
-                &self,
-                w: &mut W,
-            ) -> Result<usize, $crate::io::Error> {
-                self.consensus_encode_map(w)
+        impl $crate::psbt::serialize::Serialize for $thing {
+            fn serialize(&self) -> Vec<u8> {
+                self.serialize_map()
+            }
+        }
+    };
+}
+
+macro_rules! impl_psbtmap_deserialize {
+    ($thing:ty) => {
+        impl $crate::psbt::serialize::Deserialize for $thing {
+            fn deserialize(bytes: &[u8]) -> Result<Self, $crate::consensus::encode::Error> {
+                let mut decoder = crate::io::Cursor::new(bytes);
+                Self::decode(&mut decoder)
             }
         }
     };
 }
 
-macro_rules! impl_psbtmap_consensus_decoding {
+macro_rules! impl_psbtmap_decoding {
     ($thing:ty) => {
-        impl $crate::consensus::Decodable for $thing {
-            fn consensus_decode<R: $crate::io::Read + ?Sized>(
+        impl $thing {
+            pub(crate) fn decode<R: $crate::io::Read + ?Sized>(
                 r: &mut R,
             ) -> Result<Self, $crate::consensus::encode::Error> {
                 let mut rv: Self = core::default::Default::default();
 
                 loop {
-                    match $crate::consensus::Decodable::consensus_decode(r) {
+                    match $crate::psbt::raw::Pair::decode(r) {
                         Ok(pair) => rv.insert_pair(pair)?,
                         Err($crate::consensus::encode::Error::Psbt($crate::psbt::Error::NoMorePairs)) => return Ok(rv),
                         Err(e) => return Err(e),
@@ -73,10 +81,11 @@ fn consensus_decode<R: $crate::io::Read + ?Sized>(
     };
 }
 
-macro_rules! impl_psbtmap_consensus_enc_dec_oding {
+macro_rules! impl_psbtmap_ser_de_serialize {
     ($thing:ty) => {
-        impl_psbtmap_consensus_decoding!($thing);
-        impl_psbtmap_consensus_encoding!($thing);
+        impl_psbtmap_decoding!($thing);
+        impl_psbtmap_serialize!($thing);
+        impl_psbtmap_deserialize!($thing);
     };
 }
 
diff --git a/bitcoin/src/psbt/map/global.rs b/bitcoin/src/psbt/map/global.rs
index a337e7c6..f37a2c84 100644
--- a/bitcoin/src/psbt/map/global.rs
+++ b/bitcoin/src/psbt/map/global.rs
@@ -7,8 +7,8 @@
 use crate::io::{self, Cursor, Read};
 
 use crate::blockdata::transaction::Transaction;
-use crate::consensus::{encode, Encodable, Decodable};
 use crate::consensus::encode::MAX_VEC_SIZE;
+use crate::consensus::{encode, Decodable};
 use crate::psbt::map::Map;
 use crate::psbt::{raw, Error, PartiallySignedTransaction};
 use crate::bip32::{ExtendedPubKey, Fingerprint, DerivationPath, ChildNumber};
@@ -23,7 +23,7 @@
 const PSBT_GLOBAL_PROPRIETARY: u8 = 0xFC;
 
 impl Map for PartiallySignedTransaction {
-    fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
+    fn get_pairs(&self) -> Vec<raw::Pair> {
         let mut rv: Vec<raw::Pair> = Default::default();
 
         rv.push(raw::Pair {
@@ -35,10 +35,10 @@ fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
                 // Manually serialized to ensure 0-input txs are serialized
                 // without witnesses.
                 let mut ret = Vec::new();
-                self.unsigned_tx.version.consensus_encode(&mut ret)?;
-                self.unsigned_tx.input.consensus_encode(&mut ret)?;
-                self.unsigned_tx.output.consensus_encode(&mut ret)?;
-                self.unsigned_tx.lock_time.consensus_encode(&mut ret)?;
+                ret.extend(encode::serialize(&self.unsigned_tx.version));
+                ret.extend(encode::serialize(&self.unsigned_tx.input));
+                ret.extend(encode::serialize(&self.unsigned_tx.output));
+                ret.extend(encode::serialize(&self.unsigned_tx.lock_time));
                 ret
             },
         });
@@ -83,12 +83,12 @@ fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
             });
         }
 
-        Ok(rv)
+        rv
     }
 }
 
 impl PartiallySignedTransaction {
-    pub(crate) fn consensus_decode_global<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+    pub(crate) fn decode_global<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let mut r = r.take(MAX_VEC_SIZE as u64);
         let mut tx: Option<Transaction> = None;
         let mut version: Option<u32> = None;
@@ -97,7 +97,7 @@ pub(crate) fn consensus_decode_global<R: io::Read + ?Sized>(r: &mut R) -> Result
         let mut proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>> = Default::default();
 
         loop {
-            match raw::Pair::consensus_decode(&mut r) {
+            match raw::Pair::decode(&mut r) {
                 Ok(pair) => {
                     match pair.key.type_value {
                         PSBT_GLOBAL_UNSIGNED_TX => {
diff --git a/bitcoin/src/psbt/map/input.rs b/bitcoin/src/psbt/map/input.rs
index b2cc5e23..fcc18538 100644
--- a/bitcoin/src/psbt/map/input.rs
+++ b/bitcoin/src/psbt/map/input.rs
@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: CC0-1.0
 
 use crate::prelude::*;
-use crate::io;
 
 use core::fmt;
 use core::str::FromStr;
@@ -394,7 +393,7 @@ pub fn combine(&mut self, other: Self) {
 }
 
 impl Map for Input {
-    fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
+    fn get_pairs(&self) -> Vec<raw::Pair> {
         let mut rv: Vec<raw::Pair> = Default::default();
 
         impl_psbt_get_pair! {
@@ -486,11 +485,11 @@ fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
             });
         }
 
-        Ok(rv)
+        rv
     }
 }
 
-impl_psbtmap_consensus_enc_dec_oding!(Input);
+impl_psbtmap_ser_de_serialize!(Input);
 
 fn psbt_insert_hash_pair<H>(
     map: &mut BTreeMap<H, Vec<u8>>,
diff --git a/bitcoin/src/psbt/map/mod.rs b/bitcoin/src/psbt/map/mod.rs
index d22dc226..ebc1c236 100644
--- a/bitcoin/src/psbt/map/mod.rs
+++ b/bitcoin/src/psbt/map/mod.rs
@@ -2,9 +2,6 @@
 
 use crate::prelude::*;
 
-use crate::io;
-
-use crate::consensus::encode;
 use crate::psbt::raw;
 
 mod global;
@@ -14,18 +11,26 @@
 pub use self::input::{Input, PsbtSighashType};
 pub use self::output::{Output, TapTree, IncompleteTapTree};
 
+use super::serialize::Serialize;
+
 /// A trait that describes a PSBT key-value map.
 pub(super) trait Map {
     /// Attempt to get all key-value pairs.
-    fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error>;
-
-    /// Encodes map data with bitcoin consensus encoding.
-    fn consensus_encode_map<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        let mut len = 0;
-        for pair in Map::get_pairs(self)? {
-            len += encode::Encodable::consensus_encode(&pair, w)?;
+    fn get_pairs(&self) -> Vec<raw::Pair>;
+
+    /// Serialize Psbt binary map data according to BIP-174 specification.
+    ///
+    /// <map> := <keypair>* 0x00
+    ///
+    /// Why is the separator here 0x00 instead of 0xff? The separator here is used to distinguish between each chunk of data.
+    /// A separator of 0x00 would mean that the unserializer can read it as a key length of 0, which would never occur with
+    /// actual keys. It can thus be used as a separator and allow for easier unserializer implementation.
+    fn serialize_map(&self) -> Vec<u8> {
+        let mut buf = Vec::new();
+        for pair in Map::get_pairs(self) {
+            buf.extend(&pair.serialize());
         }
-
-        Ok(len + encode::Encodable::consensus_encode(&0x00_u8, w)?)
+        buf.push(0x00_u8);
+        buf
     }
 }
diff --git a/bitcoin/src/psbt/map/output.rs b/bitcoin/src/psbt/map/output.rs
index b321fbfd..d360ac4d 100644
--- a/bitcoin/src/psbt/map/output.rs
+++ b/bitcoin/src/psbt/map/output.rs
@@ -4,8 +4,6 @@
 use core;
 use core::convert::TryFrom;
 
-use crate::io;
-
 use crate::blockdata::script::ScriptBuf;
 use crate::consensus::encode;
 use secp256k1::XOnlyPublicKey;
@@ -279,7 +277,7 @@ pub fn combine(&mut self, other: Self) {
 }
 
 impl Map for Output {
-    fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
+    fn get_pairs(&self) -> Vec<raw::Pair> {
         let mut rv: Vec<raw::Pair> = Default::default();
 
         impl_psbt_get_pair! {
@@ -320,8 +318,8 @@ fn get_pairs(&self) -> Result<Vec<raw::Pair>, io::Error> {
             });
         }
 
-        Ok(rv)
+        rv
     }
 }
 
-impl_psbtmap_consensus_enc_dec_oding!(Output);
+impl_psbtmap_ser_de_serialize!(Output);
diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs
index e6a01e4d..ad22a088 100644
--- a/bitcoin/src/psbt/mod.rs
+++ b/bitcoin/src/psbt/mod.rs
@@ -17,11 +17,9 @@
 use bitcoin_internals::write_err;
 
 use crate::{prelude::*, Amount};
-use crate::io;
 
 use crate::blockdata::script::ScriptBuf;
 use crate::blockdata::transaction::{Transaction, TxOut};
-use crate::consensus::{encode, Encodable, Decodable};
 use crate::bip32::{self, ExtendedPrivKey, ExtendedPubKey, KeySource};
 use crate::crypto::ecdsa;
 use crate::crypto::key::{PublicKey, PrivateKey};
@@ -39,7 +37,6 @@
 
 mod map;
 pub use self::map::{Input, Output, TapTree, PsbtSighashType, IncompleteTapTree};
-use self::map::Map;
 
 /// Partially signed transaction, commonly referred to as a PSBT.
 pub type Psbt = PartiallySignedTransaction;
@@ -357,7 +354,7 @@ pub fn sighash_ecdsa<T: Deref<Target=Transaction>>(
             }
         };
 
-        Ok((Message::from(sighash), hash_ty))
+        Ok((Message::from_slice(&sighash).expect("sighashes are 32 bytes"), hash_ty))
     }
 
     /// Returns the spending utxo for this PSBT's input at `input_index`.
@@ -751,7 +748,7 @@ mod display_from_str {
     use super::PartiallySignedTransaction;
     use core::fmt::{Display, Formatter, self};
     use core::str::FromStr;
-    use crate::consensus::encode::{Error, self};
+    use crate::consensus::encode::Error;
     use base64::display::Base64Display;
     use bitcoin_internals::write_err;
 
@@ -793,7 +790,7 @@ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
     #[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
     impl Display for PartiallySignedTransaction {
         fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-            write!(f, "{}", Base64Display::with_config(&encode::serialize(self), base64::STANDARD))
+            write!(f, "{}", Base64Display::with_config(&self.serialize(), base64::STANDARD))
         }
     }
 
@@ -803,7 +800,7 @@ impl FromStr for PartiallySignedTransaction {
 
         fn from_str(s: &str) -> Result<Self, Self::Err> {
             let data = base64::decode(s).map_err(PsbtParseError::Base64Encoding)?;
-            encode::deserialize(&data).map_err(PsbtParseError::PsbtEncoding)
+            PartiallySignedTransaction::deserialize(&data).map_err(PsbtParseError::PsbtEncoding)
         }
     }
 }
@@ -811,72 +808,6 @@ fn from_str(s: &str) -> Result<Self, Self::Err> {
 #[cfg_attr(docsrs, doc(cfg(feature = "base64")))]
 pub use self::display_from_str::PsbtParseError;
 
-impl Encodable for PartiallySignedTransaction {
-    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        let mut len = 0;
-        len += b"psbt".consensus_encode(w)?;
-
-        len += 0xff_u8.consensus_encode(w)?;
-
-        len += self.consensus_encode_map(w)?;
-
-        for i in &self.inputs {
-            len += i.consensus_encode(w)?;
-        }
-
-        for i in &self.outputs {
-            len += i.consensus_encode(w)?;
-        }
-
-        Ok(len)
-    }
-}
-
-impl Decodable for PartiallySignedTransaction {
-    fn consensus_decode_from_finite_reader<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
-        let magic: [u8; 4] = Decodable::consensus_decode(r)?;
-
-        if *b"psbt" != magic {
-            return Err(Error::InvalidMagic.into());
-        }
-
-        if 0xff_u8 != u8::consensus_decode(r)? {
-            return Err(Error::InvalidSeparator.into());
-        }
-
-        let mut global = PartiallySignedTransaction::consensus_decode_global(r)?;
-        global.unsigned_tx_checks()?;
-
-        let inputs: Vec<Input> = {
-            let inputs_len: usize = global.unsigned_tx.input.len();
-
-            let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
-
-            for _ in 0..inputs_len {
-                inputs.push(Decodable::consensus_decode(r)?);
-            }
-
-            inputs
-        };
-
-        let outputs: Vec<Output> = {
-            let outputs_len: usize = global.unsigned_tx.output.len();
-
-            let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
-
-            for _ in 0..outputs_len {
-                outputs.push(Decodable::consensus_decode(r)?);
-            }
-
-            outputs
-        };
-
-        global.inputs = inputs;
-        global.outputs = outputs;
-        Ok(global)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -885,6 +816,7 @@ mod tests {
     use crate::hashes::hex::FromHex;
     use crate::hashes::{sha256, hash160, Hash, ripemd160};
     use crate::hash_types::Txid;
+    use crate::psbt::serialize::{Serialize, Deserialize};
 
     use secp256k1::{Secp256k1, self};
     #[cfg(feature = "rand")]
@@ -893,7 +825,6 @@ mod tests {
     use crate::blockdata::script::ScriptBuf;
     use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
     use crate::network::constants::Network::Bitcoin;
-    use crate::consensus::encode::{deserialize, serialize, serialize_hex};
     use crate::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, KeySource};
     use crate::psbt::map::{Output, Input};
     use crate::psbt::raw;
@@ -919,7 +850,7 @@ fn trivial_psbt() {
             inputs: vec![],
             outputs: vec![],
         };
-        assert_eq!(serialize_hex(&psbt), "70736274ff01000a0200000000000000000000");
+        assert_eq!(psbt.serialize_hex(), "70736274ff01000a0200000000000000000000");
     }
 
     #[test]
@@ -967,7 +898,7 @@ fn serialize_then_deserialize_output() {
             ..Default::default()
         };
 
-        let actual: Output = deserialize(&serialize(&expected)).unwrap();
+        let actual = Output::deserialize(&expected.serialize()).unwrap();
 
         assert_eq!(expected, actual);
     }
@@ -1012,8 +943,7 @@ fn serialize_then_deserialize_global() {
             outputs: vec![Output::default(), Output::default()],
         };
 
-        let actual: PartiallySignedTransaction = deserialize(&serialize(&expected)).unwrap();
-
+        let actual: Psbt = Psbt::deserialize(&expected.serialize()).unwrap();
         assert_eq!(expected, actual);
     }
 
@@ -1027,7 +957,7 @@ fn serialize_then_deserialize_psbtkvpair() {
             value: vec![69u8, 42u8, 4u8],
         };
 
-        let actual: raw::Pair = deserialize(&serialize(&expected)).unwrap();
+        let actual = raw::Pair::deserialize(&expected.serialize()).unwrap();
 
         assert_eq!(expected, actual);
     }
@@ -1036,7 +966,7 @@ fn serialize_then_deserialize_psbtkvpair() {
     fn deserialize_and_serialize_psbt_with_two_partial_sigs() {
         let hex = "70736274ff0100890200000001207ae985d787dfe6143d5c58fad79cc7105e0e799fcf033b7f2ba17e62d7b3200000000000ffffffff02563d03000000000022002019899534b9a011043c0dd57c3ff9a381c3522c5f27c6a42319085b56ca543a1d6adc020000000000220020618b47a07ebecca4e156edb1b9ea7c24bdee0139fc049237965ffdaf56d5ee73000000000001012b801a0600000000002200201148e93e9315e37dbed2121be5239257af35adc03ffdfc5d914b083afa44dab82202025fe7371376d53cf8a2783917c28bf30bd690b0a4d4a207690093ca2b920ee076473044022007e06b362e89912abd4661f47945430739b006a85d1b2a16c01dc1a4bd07acab022061576d7aa834988b7ab94ef21d8eebd996ea59ea20529a19b15f0c9cebe3d8ac01220202b3fe93530020a8294f0e527e33fbdff184f047eb6b5a1558a352f62c29972f8a473044022002787f926d6817504431ee281183b8119b6845bfaa6befae45e13b6d430c9d2f02202859f149a6cd26ae2f03a107e7f33c7d91730dade305fe077bae677b5d44952a01010547522102b3fe93530020a8294f0e527e33fbdff184f047eb6b5a1558a352f62c29972f8a21025fe7371376d53cf8a2783917c28bf30bd690b0a4d4a207690093ca2b920ee07652ae0001014752210283ef76537f2d58ae3aa3a4bd8ae41c3f230ccadffb1a0bd3ca504d871cff05e7210353d79cc0cb1396f4ce278d005f16d948e02a6aec9ed1109f13747ecb1507b37b52ae00010147522102b3937241777b6665e0d694e52f9c1b188433641df852da6fc42187b5d8a368a321034cdd474f01cc5aa7ff834ad8bcc882a87e854affc775486bc2a9f62e8f49bd7852ae00";
         let psbt: PartiallySignedTransaction = hex_psbt!(hex).unwrap();
-        assert_eq!(hex, serialize_hex(&psbt));
+        assert_eq!(hex, psbt.serialize_hex());
     }
 
     #[cfg(feature = "serde")]
@@ -1147,7 +1077,6 @@ mod bip_vectors {
 
         use crate::blockdata::script::ScriptBuf;
         use crate::blockdata::transaction::{Transaction, TxIn, TxOut, OutPoint, Sequence};
-        use crate::consensus::encode::serialize_hex;
         use crate::blockdata::locktime::absolute;
         use crate::psbt::map::{Map, Input, Output};
         use crate::psbt::{raw, PartiallySignedTransaction, Error};
@@ -1322,7 +1251,7 @@ fn valid_vector_1() {
 
             let base16str = "70736274ff0100750200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf60000000000feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300000100fda5010100000000010289a3c71eab4d20e0371bbba4cc698fa295c9463afa2e397f8533ccb62f9567e50100000017160014be18d152a9b012039daf3da7de4f53349eecb985ffffffff86f8aa43a71dff1448893a530a7237ef6b4608bbb2dd2d0171e63aec6a4890b40100000017160014fe3e9ef1a745e974d902c4355943abcb34bd5353ffffffff0200c2eb0b000000001976a91485cff1097fd9e008bb34af709c62197b38978a4888ac72fef84e2c00000017a914339725ba21efd62ac753a9bcd067d6c7a6a39d05870247304402202712be22e0270f394f568311dc7ca9a68970b8025fdd3b240229f07f8a5f3a240220018b38d7dcd314e734c9276bd6fb40f673325bc4baa144c800d2f2f02db2765c012103d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f210502483045022100d12b852d85dcd961d2f5f4ab660654df6eedcc794c0c33ce5cc309ffb5fce58d022067338a8e0e1725c197fb1a88af59f51e44e4255b20167c8684031c05d1f2592a01210223b72beef0965d10be0778efecd61fcac6f79a4ea169393380734464f84f2ab300000000000000";
 
-            assert_eq!(serialize_hex(&unserialized), base16str);
+            assert_eq!(unserialized.serialize_hex(), base16str);
             assert_eq!(unserialized, hex_psbt!(base16str).unwrap());
 
             #[cfg(feature = "base64")] {
@@ -1353,7 +1282,7 @@ fn valid_vector_2() {
             assert_eq!(redeem_script.to_p2sh(), expected_out);
 
             for output in psbt.outputs {
-                assert_eq!(output.get_pairs().unwrap().len(), 0)
+                assert_eq!(output.get_pairs().len(), 0)
             }
         }
 
@@ -1399,7 +1328,7 @@ fn valid_vector_4() {
             assert_eq!(redeem_script.to_p2sh(), expected_out);
 
             for output in psbt.outputs {
-                assert!(!output.get_pairs().unwrap().is_empty())
+                assert!(!output.get_pairs().is_empty())
             }
         }
 
@@ -1452,7 +1381,6 @@ fn valid_vector_6() {
 
     mod bip_371_vectors {
         use super::*;
-        use super::serialize;
 
         #[test]
         fn invalid_vectors() {
@@ -1482,8 +1410,8 @@ fn invalid_vectors() {
         }
 
         fn rtt_psbt(psbt: PartiallySignedTransaction) {
-            let enc = serialize(&psbt);
-            let psbt2 = deserialize::<PartiallySignedTransaction>(&enc).unwrap();
+            let enc = Psbt::serialize(&psbt);
+            let psbt2 = Psbt::deserialize(&enc).unwrap();
             assert_eq!(psbt, psbt2);
         }
 
@@ -1634,7 +1562,7 @@ fn serialize_and_deserialize_preimage_psbt() {
         unserialized.inputs[0].hash160_preimages = hash160_preimages;
         unserialized.inputs[0].sha256_preimages = sha256_preimages;
 
-        let rtt: PartiallySignedTransaction = hex_psbt!(&serialize_hex(&unserialized)).unwrap();
+        let rtt: PartiallySignedTransaction = hex_psbt!(&unserialized.serialize_hex()).unwrap();
         assert_eq!(rtt, unserialized);
 
         // Now add an ripemd160 with incorrect preimage
@@ -1643,7 +1571,7 @@ fn serialize_and_deserialize_preimage_psbt() {
         unserialized.inputs[0].ripemd160_preimages = ripemd160_preimages;
 
         // Now the roundtrip should fail as the preimage is incorrect.
-        let rtt: Result<PartiallySignedTransaction, _> = hex_psbt!(&serialize_hex(&unserialized));
+        let rtt: Result<PartiallySignedTransaction, _> = hex_psbt!(&unserialized.serialize_hex());
         assert!(rtt.is_err());
     }
 
@@ -1656,7 +1584,7 @@ fn serialize_and_deserialize_proprietary() {
             key: b"test".to_vec(),
         }, b"test".to_vec());
         assert!(!psbt.proprietary.is_empty());
-        let rtt: PartiallySignedTransaction = hex_psbt!(&serialize_hex(&psbt)).unwrap();
+        let rtt: PartiallySignedTransaction = hex_psbt!(&psbt.serialize_hex()).unwrap();
         assert!(!rtt.proprietary.is_empty());
     }
 
diff --git a/bitcoin/src/psbt/raw.rs b/bitcoin/src/psbt/raw.rs
index 0d2e4202..49a1ddc1 100644
--- a/bitcoin/src/psbt/raw.rs
+++ b/bitcoin/src/psbt/raw.rs
@@ -10,11 +10,13 @@
 use core::fmt;
 use core::convert::TryFrom;
 
-use crate::io;
+use crate::io::{self, Cursor};
 use crate::consensus::encode::{self, ReadExt, WriteExt, Decodable, Encodable, VarInt, serialize, deserialize, MAX_VEC_SIZE};
 use crate::hashes::hex;
 use crate::psbt::Error;
 
+use super::serialize::{Serialize, Deserialize};
+
 /// A PSBT key in its raw byte form.
 #[derive(Debug, PartialEq, Hash, Eq, Clone, Ord, PartialOrd)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -23,18 +25,21 @@ pub struct Key {
     /// The type of this PSBT key.
     pub type_value: u8,
     /// The key itself in raw byte form.
+    /// `<key> := <keylen> <keytype> <keydata>`
     #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::hex_bytes"))]
     pub key: Vec<u8>,
 }
 
 /// A PSBT key-value pair in its raw byte form.
+/// `<keypair> := <key> <value>`
 #[derive(Debug, PartialEq, Eq)]
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "serde", serde(crate = "actual_serde"))]
 pub struct Pair {
     /// The key of this key-value pair.
     pub key: Key,
-    /// The value of this key-value pair in raw byte form.
+    /// The value data of this key-value pair in raw byte form.
+    /// `<value> := <valuelen> <valuedata>`
     #[cfg_attr(feature = "serde", serde(with = "crate::serde_utils::hex_bytes"))]
     pub value: Vec<u8>,
 }
@@ -66,8 +71,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
-impl Decodable for Key {
-    fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+impl Key {
+    pub(crate) fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         let VarInt(byte_size): VarInt = Decodable::consensus_decode(r)?;
 
         if byte_size == 0 {
@@ -94,32 +99,42 @@ fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Err
     }
 }
 
-impl Encodable for Key {
-    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        let mut len = 0;
-        len += VarInt((self.key.len() + 1) as u64).consensus_encode(w)?;
+impl Serialize for Key {
+    fn serialize(&self) -> Vec<u8> {
+        let mut buf = Vec::new();
+        VarInt((self.key.len() + 1) as u64).consensus_encode(&mut buf).expect("in-memory writers don't error");
 
-        len += self.type_value.consensus_encode(w)?;
+        self.type_value.consensus_encode(&mut buf).expect("in-memory writers don't error");
 
         for key in &self.key {
-            len += key.consensus_encode(w)?
+            key.consensus_encode(&mut buf).expect("in-memory writers don't error");
         }
 
-        Ok(len)
+        buf
     }
 }
 
-impl Encodable for Pair {
-    fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
-        let len = self.key.consensus_encode(w)?;
-        Ok(len + self.value.consensus_encode(w)?)
+impl Serialize for Pair {
+    fn serialize(&self) -> Vec<u8> {
+        let mut buf = Vec::new();
+        buf.extend(self.key.serialize());
+        // <value> := <valuelen> <valuedata>
+        self.value.consensus_encode(&mut buf).unwrap();
+        buf
     }
 }
 
-impl Decodable for Pair {
-    fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
+impl Deserialize for Pair {
+    fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
+        let mut decoder = Cursor::new(bytes);
+        Pair::decode(&mut decoder)
+    }
+}
+
+impl Pair {
+    pub(crate) fn decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
         Ok(Pair {
-            key: Decodable::consensus_decode(r)?,
+            key: Key::decode(r)?,
             value: Decodable::consensus_decode(r)?,
         })
     }
diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs
index 0fd909a7..0730af1c 100644
--- a/bitcoin/src/psbt/serialize.rs
+++ b/bitcoin/src/psbt/serialize.rs
@@ -2,8 +2,8 @@
 
 //! PSBT serialization.
 //!
-//! Defines traits used for (de)serializing PSBT values into/from raw
-//! bytes from/as PSBT key-value pairs.
+//! Traits to serialize PSBT values to and from raw bytes
+//! according to the BIP-174 specification.
 //!
 
 use crate::prelude::*;
@@ -18,26 +18,102 @@
 use crate::bip32::{ChildNumber, Fingerprint, KeySource};
 use crate::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
 use crate::crypto::{ecdsa, schnorr};
-use crate::psbt;
+use crate::psbt::{self, Error, PartiallySignedTransaction};
 use crate::taproot::{TapBranchHash, TapLeafHash, ControlBlock, LeafVersion};
 use crate::crypto::key::PublicKey;
 
-use super::map::{TapTree, PsbtSighashType};
+use super::map::{Map, Input, Output, TapTree, PsbtSighashType};
+use super::Psbt;
 
 use crate::taproot::TaprootBuilder;
 /// A trait for serializing a value as raw data for insertion into PSBT
-/// key-value pairs.
-pub trait Serialize {
+/// key-value maps.
+pub(crate) trait Serialize {
     /// Serialize a value as raw data.
     fn serialize(&self) -> Vec<u8>;
 }
 
-/// A trait for deserializing a value from raw data in PSBT key-value pairs.
-pub trait Deserialize: Sized {
+/// A trait for deserializing a value from raw data in PSBT key-value maps.
+pub(crate) trait Deserialize: Sized {
     /// Deserialize a value from raw data.
     fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error>;
 }
 
+impl PartiallySignedTransaction {
+    /// Serialize a value as bytes in hex.
+    pub fn serialize_hex(&self) -> String {
+        bitcoin_hashes::hex::ToHex::to_hex(&self.serialize()[..])
+    }
+
+    /// Serialize as raw binary data
+    pub fn serialize(&self) -> Vec<u8> {
+        let mut buf: Vec<u8> = Vec::new();
+
+        //  <magic>
+        buf.extend(b"psbt");
+
+        buf.push(0xff_u8);
+
+        buf.extend(self.serialize_map());
+
+        for i in &self.inputs {
+            buf.extend(i.serialize_map());
+        }
+
+        for i in &self.outputs {
+            buf.extend(i.serialize_map());
+        }
+
+        buf
+    }
+
+
+    /// Deserialize a value from raw binary data.
+    pub fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
+        const MAGIC_BYTES: &[u8] = b"psbt";
+        if bytes.get(0..MAGIC_BYTES.len()) != Some(MAGIC_BYTES) {
+            return Err(Error::InvalidMagic.into());
+        }
+
+        const PSBT_SERPARATOR: u8 = 0xff_u8;
+        if bytes.get(MAGIC_BYTES.len()) != Some(&PSBT_SERPARATOR) {
+            return Err(Error::InvalidSeparator.into());
+        }
+
+        let mut d = bytes.get(5..).ok_or(Error::NoMorePairs)?;
+
+        let mut global = Psbt::decode_global(&mut d)?;
+        global.unsigned_tx_checks()?;
+
+        let inputs: Vec<Input> = {
+            let inputs_len: usize = (global.unsigned_tx.input).len();
+
+            let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
+
+            for _ in 0..inputs_len {
+                inputs.push(Input::decode(&mut d)?);
+            }
+
+            inputs
+        };
+
+        let outputs: Vec<Output> = {
+            let outputs_len: usize = (global.unsigned_tx.output).len();
+
+            let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
+
+            for _ in 0..outputs_len {
+                outputs.push(Output::decode(&mut d)?);
+            }
+
+            outputs
+        };
+
+        global.inputs = inputs;
+        global.outputs = outputs;
+        Ok(global)
+    }
+}
 impl_psbt_de_serialize!(Transaction);
 impl_psbt_de_serialize!(TxOut);
 impl_psbt_de_serialize!(Witness);
@@ -400,4 +476,11 @@ fn can_deserialize_non_standard_psbt_sighash_type() {
         let sighash = PsbtSighashType::deserialize(&non_standard_sighash);
         assert!(sighash.is_ok())
     }
+
+    #[test]
+    #[should_panic(expected = "InvalidMagic")]
+    fn invalid_vector_1() {
+        let hex_psbt = b"0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300";
+        PartiallySignedTransaction::deserialize(hex_psbt).unwrap();
+    }
 }
diff --git a/bitcoin/src/sighash.rs b/bitcoin/src/sighash.rs
index 6433e6b8..623318f7 100644
--- a/bitcoin/src/sighash.rs
+++ b/bitcoin/src/sighash.rs
@@ -963,9 +963,15 @@ fn segwit_cache(&mut self) -> &SegwitCache {
         self.segwit_cache.get_or_insert_with(|| {
             let common_cache = Self::common_cache_minimal_borrow(common_cache, tx);
             SegwitCache {
-                prevouts: common_cache.prevouts.hash_again(),
-                sequences: common_cache.sequences.hash_again(),
-                outputs: common_cache.outputs.hash_again(),
+                prevouts: sha256d::Hash::from_inner(
+                    sha256::Hash::hash(&common_cache.prevouts).into_inner(),
+                ),
+                sequences: sha256d::Hash::from_inner(
+                    sha256::Hash::hash(&common_cache.sequences).into_inner(),
+                ),
+                outputs: sha256d::Hash::from_inner(
+                    sha256::Hash::hash(&common_cache.outputs).into_inner(),
+                ),
             }
         })
     }
diff --git a/bitcoin/src/taproot.rs b/bitcoin/src/taproot.rs
index 62f9c348..02ef8efe 100644
--- a/bitcoin/src/taproot.rs
+++ b/bitcoin/src/taproot.rs
@@ -79,7 +79,7 @@ pub fn from_key_and_tweak(
         // always hash the key
         eng.input(&internal_key.serialize());
         if let Some(h) = merkle_root {
-            eng.input(h.as_ref());
+            eng.input(&h);
         } else {
             // nothing to hash
         }
@@ -116,11 +116,11 @@ impl TapBranchHash {
     pub fn from_node_hashes(a: sha256::Hash, b: sha256::Hash) -> TapBranchHash {
         let mut eng = TapBranchHash::engine();
         if a < b {
-            eng.input(a.as_ref());
-            eng.input(b.as_ref());
+            eng.input(&a);
+            eng.input(&b);
         } else {
-            eng.input(b.as_ref());
-            eng.input(a.as_ref());
+            eng.input(&b);
+            eng.input(&a);
         };
         TapBranchHash::from_engine(eng)
     }
@@ -673,7 +673,7 @@ fn from_collection<T: AsRef<[sha256::Hash]> + Into<Vec<sha256::Hash>>>(
     /// The number of bytes written to the writer.
     pub fn encode<Write: io::Write>(&self, mut writer: Write) -> io::Result<usize> {
         for hash in self.0.iter() {
-            writer.write_all(hash.as_ref())?;
+            writer.write_all(hash)?;
         }
         Ok(self.0.len() * sha256::Hash::LEN)
     }
@@ -1101,8 +1101,8 @@ mod test {
     fn tag_engine(tag_name: &str) -> sha256::HashEngine {
         let mut engine = sha256::Hash::engine();
         let tag_hash = sha256::Hash::hash(tag_name.as_bytes());
-        engine.input(tag_hash.as_ref());
-        engine.input(tag_hash.as_ref());
+        engine.input(&tag_hash[..]);
+        engine.input(&tag_hash[..]);
         engine
     }
 


Test patch:
diff --git a/bitcoin/tests/psbt.rs b/bitcoin/tests/psbt.rs
index d14bea47..389f1727 100644
--- a/bitcoin/tests/psbt.rs
+++ b/bitcoin/tests/psbt.rs
@@ -26,7 +26,7 @@ macro_rules! hex_script {
 
 macro_rules! hex_psbt {
     ($s:expr) => {
-        deserialize::<Psbt>(&<Vec<u8> as FromHex>::from_hex($s).unwrap())
+        Psbt::deserialize(&<Vec<u8> as FromHex>::from_hex($s).unwrap())
     };
 }
 
@@ -397,10 +397,10 @@ fn combine_lexicographically() {
     let expected_psbt = hex_psbt!(expected_psbt_hex).unwrap();
 
     let v = Vec::from_hex(psbt_1_hex).unwrap();
-    let mut psbt_1: Psbt = deserialize(&v).expect("failed to deserialize psbt 1");
+    let mut psbt_1 = Psbt::deserialize(&v).expect("failed to deserialize psbt 1");
 
     let v = Vec::from_hex(psbt_2_hex).unwrap();
-    let psbt_2: Psbt = deserialize(&v).expect("failed to deserialize psbt 2");
+    let psbt_2 = Psbt::deserialize(&v).expect("failed to deserialize psbt 2");
 
     psbt_1.combine(psbt_2).expect("failed to combine PSBTs");
 
@@ -417,16 +417,14 @@ fn sign(mut psbt: Psbt, keys: BTreeMap<bitcoin::PublicKey, PrivateKey>) -> Psbt
 /// Finalizes a PSBT accord to the Input Finalizer role described in BIP 174.
 /// This is just a test. For a production-ready PSBT Finalizer, use [rust-miniscript](https://docs.rs/miniscript/latest/miniscript/psbt/trait.PsbtExt.html#tymethod.finalize)
 fn finalize_psbt(mut psbt: Psbt) -> Psbt {
-    use bitcoin::psbt::serialize::Serialize;
-
     // Input 0: legacy UTXO
 
     let sigs: Vec<_> = psbt.inputs[0].partial_sigs.values().collect();
     let script_sig = script::Builder::new()
         .push_opcode(OP_0) // OP_CHECKMULTISIG bug pops +1 value when evaluating so push OP_0.
-        .push_slice(&sigs[0].serialize())
-        .push_slice(&sigs[1].serialize())
-        .push_slice(&psbt.inputs[0].redeem_script.clone().unwrap().serialize())
+        .push_slice(&sigs[0].to_vec())
+        .push_slice(&sigs[1].to_vec())
+        .push_slice(psbt.inputs[0].redeem_script.clone().unwrap().as_bytes())
         .into_script();
 
     psbt.inputs[0].final_script_sig = Some(script_sig);
@@ -439,7 +437,7 @@ fn finalize_psbt(mut psbt: Psbt) -> Psbt {
     // Input 1: SegWit UTXO
 
     let script_sig = script::Builder::new()
-        .push_slice(&psbt.inputs[1].redeem_script.clone().unwrap().serialize())
+        .push_slice(psbt.inputs[1].redeem_script.clone().unwrap().as_bytes())
         .into_script();
 
     psbt.inputs[1].final_script_sig = Some(script_sig);
@@ -448,9 +446,9 @@ fn finalize_psbt(mut psbt: Psbt) -> Psbt {
         let sigs: Vec<_> = psbt.inputs[1].partial_sigs.values().collect();
         let mut script_witness = Witness::new();
         script_witness.push([]); // Push 0x00 to the stack.
-        script_witness.push(&sigs[1].serialize());
-        script_witness.push(&sigs[0].serialize());
-        script_witness.push(&psbt.inputs[1].witness_script.clone().unwrap().serialize());
+        script_witness.push(&sigs[1].to_vec());
+        script_witness.push(&sigs[0].to_vec());
+        script_witness.push(psbt.inputs[1].witness_script.clone().unwrap().as_bytes());
 
         script_witness
     };
diff --git a/hashes/contrib/test.sh b/hashes/contrib/test.sh
index bb7fe2e8..6cf1e764 100755
--- a/hashes/contrib/test.sh
+++ b/hashes/contrib/test.sh
@@ -22,11 +22,6 @@ export CARGO_TERM_VERBOSE=true
 cargo build --all
 cargo test --all
 
-if [ "$DO_LINT" = true ]
-then
-    cargo clippy --all-features --all-targets -- -D warnings
-fi
-
 if [ "$DO_FEATURE_MATRIX" = true ]; then
     cargo build --all --no-default-features
     cargo test --all --no-default-features