Skip to main content

psbt_v2/
serialize.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! PSBT serialization.
4//!
5//! Traits to serialize PSBT values to and from raw bytes
6//! according to the BIP-174 specification.
7
8use core::convert::{TryFrom, TryInto};
9use core::fmt;
10
11use bitcoin::bip32::{ChildNumber, Fingerprint, KeySource};
12use bitcoin::consensus::{self, Decodable, Encodable};
13use bitcoin::hashes::{self, hash160, ripemd160, sha256, sha256d, Hash};
14use bitcoin::key::PublicKey;
15use bitcoin::secp256k1::{self, XOnlyPublicKey};
16use bitcoin::taproot::{
17    ControlBlock, LeafVersion, TapLeafHash, TapNodeHash, TapTree, TaprootBuilder,
18};
19use bitcoin::{
20    absolute, ecdsa, taproot, transaction, Amount, ScriptBuf, Sequence, Transaction, TxOut, Txid,
21    VarInt, Witness,
22};
23
24use crate::error::write_err;
25use crate::prelude::*;
26use crate::sighash_type::PsbtSighashType;
27#[cfg(feature = "silent-payments")]
28use crate::v2::dleq;
29use crate::version;
30
31/// A trait for serializing a value as raw data for insertion into PSBT
32/// key-value maps.
33pub(crate) trait Serialize {
34    /// Serialize a value as raw data.
35    fn serialize(&self) -> Vec<u8>;
36}
37
38/// A trait for deserializing a value from raw data in PSBT key-value maps.
39pub(crate) trait Deserialize: Sized {
40    /// Deserialize a value from raw data.
41    fn deserialize(bytes: &[u8]) -> Result<Self, Error>;
42}
43
44// Strictly speaking these do not need the prefix because the v0 versions are
45// unused but we want to leave thoes in the code so the the files are close as
46// possible to the original from bitcoin/miniscript repos.
47v2_impl_psbt_de_serialize!(absolute::LockTime);
48v2_impl_psbt_de_serialize!(Amount);
49v2_impl_psbt_de_serialize!(Transaction);
50v2_impl_psbt_de_serialize!(transaction::Version);
51v2_impl_psbt_de_serialize!(TxOut);
52v2_impl_psbt_de_serialize!(Witness);
53v2_impl_psbt_de_serialize!(VarInt);
54v2_impl_psbt_hash_de_serialize!(ripemd160::Hash);
55v2_impl_psbt_hash_de_serialize!(sha256::Hash);
56v2_impl_psbt_hash_de_serialize!(TapLeafHash);
57v2_impl_psbt_hash_de_serialize!(TapNodeHash);
58v2_impl_psbt_hash_de_serialize!(Txid);
59v2_impl_psbt_hash_de_serialize!(hash160::Hash);
60v2_impl_psbt_hash_de_serialize!(sha256d::Hash);
61
62// taproot
63v2_impl_psbt_de_serialize!(Vec<TapLeafHash>);
64
65impl Serialize for ScriptBuf {
66    fn serialize(&self) -> Vec<u8> { self.to_bytes() }
67}
68
69impl Deserialize for ScriptBuf {
70    fn deserialize(bytes: &[u8]) -> Result<Self, Error> { Ok(Self::from(bytes.to_vec())) }
71}
72
73impl Serialize for PublicKey {
74    fn serialize(&self) -> Vec<u8> {
75        let mut buf = Vec::new();
76        self.write_into(&mut buf).expect("vecs don't error");
77        buf
78    }
79}
80
81impl Deserialize for PublicKey {
82    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
83        PublicKey::from_slice(bytes).map_err(Error::InvalidPublicKey)
84    }
85}
86
87impl Serialize for secp256k1::PublicKey {
88    fn serialize(&self) -> Vec<u8> { self.serialize().to_vec() }
89}
90
91impl Deserialize for secp256k1::PublicKey {
92    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
93        secp256k1::PublicKey::from_slice(bytes).map_err(Error::InvalidSecp256k1PublicKey)
94    }
95}
96
97impl Serialize for ecdsa::Signature {
98    fn serialize(&self) -> Vec<u8> { self.to_vec() }
99}
100
101impl Deserialize for ecdsa::Signature {
102    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
103        // NB: Since BIP-174 says "the signature as would be pushed to the stack from
104        // a scriptSig or witness" we should ideally use a consensus deserialization and do
105        // not error on a non-standard values. However,
106        //
107        // 1) the current implementation of from_u32_consensus(`flag`) does not preserve
108        // the sighash byte `flag` mapping all unknown values to EcdsaSighashType::All or
109        // EcdsaSighashType::AllPlusAnyOneCanPay. Therefore, break the invariant
110        // EcdsaSig::from_slice(&sl[..]).to_vec = sl.
111        //
112        // 2) This would cause to have invalid signatures because the sighash message
113        // also has a field sighash_u32 (See BIP141). For example, when signing with non-standard
114        // 0x05, the sighash message would have the last field as 0x05u32 while, the verification
115        // would use check the signature assuming sighash_u32 as `0x01`.
116        ecdsa::Signature::from_slice(bytes).map_err(|e| match e {
117            ecdsa::Error::EmptySignature => Error::InvalidEcdsaSignature(e),
118            ecdsa::Error::SighashType(err) => Error::NonStandardSighashType(err.0),
119            ecdsa::Error::Secp256k1(..) => Error::InvalidEcdsaSignature(e),
120            ecdsa::Error::Hex(..) => unreachable!("Decoding from slice, not hex"),
121            _ => panic!("TODO: Handle non_exhaustive error"),
122        })
123    }
124}
125
126impl Serialize for KeySource {
127    fn serialize(&self) -> Vec<u8> {
128        let mut rv: Vec<u8> = Vec::with_capacity(key_source_len(self));
129
130        rv.append(&mut self.0.to_bytes().to_vec());
131
132        for cnum in self.1.into_iter() {
133            rv.append(&mut consensus::serialize(&u32::from(*cnum)))
134        }
135
136        rv
137    }
138}
139
140impl Deserialize for KeySource {
141    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
142        if bytes.len() < 4 {
143            return Err(Error::NotEnoughData);
144        }
145
146        let fprint: Fingerprint = bytes[0..4].try_into().expect("4 is the fingerprint length");
147        let mut dpath: Vec<ChildNumber> = Default::default();
148
149        let mut d = &bytes[4..];
150        while !d.is_empty() {
151            match u32::consensus_decode(&mut d) {
152                Ok(index) => dpath.push(index.into()),
153                Err(e) => return Err(e.into()),
154            }
155        }
156
157        Ok((fprint, dpath.into()))
158    }
159}
160
161impl Serialize for u32 {
162    fn serialize(&self) -> Vec<u8> { consensus::serialize(&self) }
163}
164
165impl Deserialize for u32 {
166    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
167        let val: u32 = consensus::deserialize(bytes)?;
168        Ok(val)
169    }
170}
171
172impl Serialize for Sequence {
173    fn serialize(&self) -> Vec<u8> { consensus::serialize(&self) }
174}
175
176impl Deserialize for Sequence {
177    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
178        let n: Sequence = consensus::deserialize(bytes)?;
179        Ok(n)
180    }
181}
182
183impl Serialize for absolute::Height {
184    fn serialize(&self) -> Vec<u8> { consensus::serialize(&self.to_consensus_u32()) }
185}
186
187impl Deserialize for absolute::Height {
188    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
189        let n: u32 = consensus::deserialize(bytes)?;
190        let lock = absolute::Height::from_consensus(n)?;
191        Ok(lock)
192    }
193}
194
195impl Serialize for absolute::Time {
196    fn serialize(&self) -> Vec<u8> { consensus::serialize(&self.to_consensus_u32()) }
197}
198
199impl Deserialize for absolute::Time {
200    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
201        let n: u32 = consensus::deserialize(bytes)?;
202        let lock = absolute::Time::from_consensus(n)?;
203        Ok(lock)
204    }
205}
206
207// partial sigs
208impl Serialize for Vec<u8> {
209    fn serialize(&self) -> Vec<u8> { self.clone() }
210}
211
212impl Deserialize for Vec<u8> {
213    fn deserialize(bytes: &[u8]) -> Result<Self, Error> { Ok(bytes.to_vec()) }
214}
215
216impl Serialize for PsbtSighashType {
217    fn serialize(&self) -> Vec<u8> { consensus::serialize(&self.to_u32()) }
218}
219
220impl Deserialize for PsbtSighashType {
221    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
222        let raw: u32 = consensus::deserialize(bytes)?;
223        Ok(PsbtSighashType { inner: raw })
224    }
225}
226
227// Taproot related ser/deser
228impl Serialize for XOnlyPublicKey {
229    fn serialize(&self) -> Vec<u8> { XOnlyPublicKey::serialize(self).to_vec() }
230}
231
232impl Deserialize for XOnlyPublicKey {
233    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
234        XOnlyPublicKey::from_slice(bytes).map_err(|_| Error::InvalidXOnlyPublicKey)
235    }
236}
237
238impl Serialize for taproot::Signature {
239    fn serialize(&self) -> Vec<u8> { self.to_vec() }
240}
241
242impl Deserialize for taproot::Signature {
243    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
244        use taproot::SigFromSliceError::*;
245
246        taproot::Signature::from_slice(bytes).map_err(|e| match e {
247            SighashType(err) => Error::NonStandardSighashType(err.0),
248            InvalidSignatureSize(_) => Error::InvalidTaprootSignature(e),
249            Secp256k1(..) => Error::InvalidTaprootSignature(e),
250            _ => panic!("TODO: Handle non_exhaustive error"),
251        })
252    }
253}
254
255impl Serialize for (XOnlyPublicKey, TapLeafHash) {
256    fn serialize(&self) -> Vec<u8> {
257        let ser_pk = self.0.serialize();
258        let mut buf = Vec::with_capacity(ser_pk.len() + self.1.as_byte_array().len());
259        buf.extend(&ser_pk);
260        buf.extend(self.1.as_byte_array());
261        buf
262    }
263}
264
265impl Deserialize for (XOnlyPublicKey, TapLeafHash) {
266    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
267        if bytes.len() < 32 {
268            return Err(Error::NotEnoughData);
269        }
270        let a: XOnlyPublicKey = Deserialize::deserialize(&bytes[..32])?;
271        let b: TapLeafHash = Deserialize::deserialize(&bytes[32..])?;
272        Ok((a, b))
273    }
274}
275
276impl Serialize for ControlBlock {
277    fn serialize(&self) -> Vec<u8> { ControlBlock::serialize(self) }
278}
279
280impl Deserialize for ControlBlock {
281    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
282        Self::decode(bytes).map_err(|_| Error::InvalidControlBlock)
283    }
284}
285
286// Versioned ScriptBuf
287impl Serialize for (ScriptBuf, LeafVersion) {
288    fn serialize(&self) -> Vec<u8> {
289        let mut buf = Vec::with_capacity(self.0.len() + 1);
290        buf.extend(self.0.as_bytes());
291        buf.push(self.1.to_consensus());
292        buf
293    }
294}
295
296impl Deserialize for (ScriptBuf, LeafVersion) {
297    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
298        if bytes.is_empty() {
299            return Err(Error::NotEnoughData);
300        }
301        // The last byte is LeafVersion.
302        let script = ScriptBuf::deserialize(&bytes[..bytes.len() - 1])?;
303        let leaf_ver = LeafVersion::from_consensus(bytes[bytes.len() - 1])
304            .map_err(|_| Error::InvalidLeafVersion)?;
305        Ok((script, leaf_ver))
306    }
307}
308
309impl Serialize for (Vec<TapLeafHash>, KeySource) {
310    fn serialize(&self) -> Vec<u8> {
311        let mut buf = Vec::with_capacity(32 * self.0.len() + key_source_len(&self.1));
312        self.0.consensus_encode(&mut buf).expect("Vecs don't error allocation");
313        // TODO: Add support for writing into a writer for key-source
314        buf.extend(self.1.serialize());
315        buf
316    }
317}
318
319impl Deserialize for (Vec<TapLeafHash>, KeySource) {
320    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
321        let (leafhash_vec, consumed) = consensus::deserialize_partial::<Vec<TapLeafHash>>(bytes)?;
322        let key_source = KeySource::deserialize(&bytes[consumed..])?;
323        Ok((leafhash_vec, key_source))
324    }
325}
326
327impl Serialize for TapTree {
328    fn serialize(&self) -> Vec<u8> {
329        let capacity = self
330            .script_leaves()
331            .map(|l| {
332                l.script().len() + VarInt::from(l.script().len()).size() // script version
333            + 1 // merkle branch
334            + 1 // leaf version
335            })
336            .sum::<usize>();
337        let mut buf = Vec::with_capacity(capacity);
338        for leaf_info in self.script_leaves() {
339            // # Cast Safety:
340            //
341            // TaprootMerkleBranch can only have len atmost 128(TAPROOT_CONTROL_MAX_NODE_COUNT).
342            // safe to cast from usize to u8
343            buf.push(leaf_info.merkle_branch().len() as u8);
344            buf.push(leaf_info.version().to_consensus());
345            leaf_info.script().consensus_encode(&mut buf).expect("Vecs dont err");
346        }
347        buf
348    }
349}
350
351impl Deserialize for TapTree {
352    fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
353        let mut builder = TaprootBuilder::new();
354        let mut bytes_iter = bytes.iter();
355        while let Some(depth) = bytes_iter.next() {
356            let version = bytes_iter.next().ok_or(Error::Taproot("Invalid Taproot Builder"))?;
357            let (script, consumed) =
358                consensus::deserialize_partial::<ScriptBuf>(bytes_iter.as_slice())?;
359            if consumed > 0 {
360                bytes_iter.nth(consumed - 1);
361            }
362            let leaf_version =
363                LeafVersion::from_consensus(*version).map_err(|_| Error::InvalidLeafVersion)?;
364            builder = builder
365                .add_leaf_with_ver(*depth, script, leaf_version)
366                .map_err(|_| Error::Taproot("Tree not in DFS order"))?;
367        }
368        TapTree::try_from(builder).map_err(Error::TapTree)
369    }
370}
371
372// Helper function to compute key source len
373fn key_source_len(key_source: &KeySource) -> usize { 4 + 4 * (key_source.1).as_ref().len() }
374
375// TODO: This error is still too general but splitting it up is
376// non-trivial because it is returned by the Deserialize trait.
377/// Ways that deserializing a PSBT might fail.
378#[derive(Debug)]
379#[non_exhaustive]
380pub enum Error {
381    /// Not enough data to deserialize object.
382    NotEnoughData,
383    /// Non-proprietary key type found when proprietary key was expected
384    InvalidProprietaryKey,
385    /// Signals that there are no more key-value pairs in a key-value map.
386    NoMorePairs,
387    /// Unable to parse as a standard sighash type.
388    NonStandardSighashType(u32),
389    /// Invalid hash when parsing slice.
390    InvalidHash(hashes::FromSliceError),
391    /// Serialization error in bitcoin consensus-encoded structures
392    ConsensusEncoding(consensus::encode::Error),
393    /// Parsing error indicating invalid public keys
394    InvalidPublicKey(bitcoin::key::FromSliceError),
395    /// Parsing error indicating invalid secp256k1 public keys
396    InvalidSecp256k1PublicKey(secp256k1::Error),
397    /// Parsing error indicating invalid xonly public keys
398    InvalidXOnlyPublicKey,
399    /// Parsing error indicating invalid ECDSA signatures
400    InvalidEcdsaSignature(bitcoin::ecdsa::Error),
401    /// Parsing error indicating invalid taproot signatures
402    InvalidTaprootSignature(bitcoin::taproot::SigFromSliceError),
403    /// Parsing error indicating invalid control block
404    InvalidControlBlock,
405    /// Parsing error indicating invalid leaf version
406    InvalidLeafVersion,
407    /// Parsing error indicating a taproot error
408    Taproot(&'static str),
409    /// Taproot tree deserilaization error
410    TapTree(taproot::IncompleteBuilderError),
411    /// Error related to PSBT version
412    /// PSBT data is not consumed entirely
413    PartialDataConsumption,
414    /// Couldn't converting parsed u32 to a lock time.
415    LockTime(absolute::ConversionError),
416    /// Unsupported PSBT version.
417    UnsupportedVersion(version::UnsupportedVersionError),
418    /// Invalid scan key for BIP-375 silent payments (expected 33 bytes).
419    InvalidScanKey {
420        /// The length that was provided.
421        got: usize,
422        /// The expected length.
423        expected: usize,
424    },
425    /// Invalid ECDH share for BIP-375 silent payments (expected 33 bytes).
426    InvalidEcdhShare {
427        /// The length that was provided.
428        got: usize,
429        /// The expected length.
430        expected: usize,
431    },
432    /// Invalid DLEQ proof for BIP-375 silent payments (expected 64 bytes).
433    #[cfg(feature = "silent-payments")]
434    InvalidDleqProof(dleq::InvalidLengthError),
435}
436
437impl fmt::Display for Error {
438    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
439        use Error::*;
440
441        match *self {
442            NotEnoughData => f.write_str("not enough data to deserialize object"),
443            InvalidProprietaryKey =>
444                write!(f, "non-proprietary key type found when proprietary key was expected"),
445            NoMorePairs => f.write_str("no more key-value pairs for this psbt map"),
446            NonStandardSighashType(ref sht) => write!(f, "non-standard sighash type: {}", sht),
447            InvalidHash(ref e) => write_err!(f, "invalid hash when parsing slice"; e),
448            ConsensusEncoding(ref e) => write_err!(f, "bitcoin consensus encoding error"; e),
449            InvalidPublicKey(ref e) => write_err!(f, "invalid public key"; e),
450            InvalidSecp256k1PublicKey(ref e) => write_err!(f, "invalid secp256k1 public key"; e),
451            InvalidXOnlyPublicKey => f.write_str("invalid xonly public key"),
452            InvalidEcdsaSignature(ref e) => write_err!(f, "invalid ECDSA signature"; e),
453            InvalidTaprootSignature(ref e) => write_err!(f, "invalid taproot signature"; e),
454            InvalidControlBlock => f.write_str("invalid control block"),
455            InvalidLeafVersion => f.write_str("invalid leaf version"),
456            Taproot(s) => write!(f, "taproot error -  {}", s),
457            TapTree(ref e) => write_err!(f, "taproot tree error"; e),
458            PartialDataConsumption =>
459                f.write_str("data not consumed entirely when explicitly deserializing"),
460            LockTime(ref e) => write_err!(f, "parsed locktime invalid"; e),
461            UnsupportedVersion(ref e) => write_err!(f, "unsupported version"; e),
462            InvalidScanKey { got, expected } => {
463                write!(f, "invalid scan key: got {} bytes, expected {}", got, expected)
464            }
465            InvalidEcdhShare { got, expected } => {
466                write!(f, "invalid ECDH share: got {} bytes, expected {}", got, expected)
467            }
468            #[cfg(feature = "silent-payments")]
469            InvalidDleqProof(e) => {
470                write!(f, "invalid DLEQ proof: got {} bytes, expected {}", e.got, e.expected)
471            }
472        }
473    }
474}
475
476#[cfg(feature = "std")]
477impl std::error::Error for Error {
478    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
479        use Error::*;
480
481        match *self {
482            InvalidHash(ref e) => Some(e),
483            ConsensusEncoding(ref e) => Some(e),
484            LockTime(ref e) => Some(e),
485            UnsupportedVersion(ref e) => Some(e),
486            NotEnoughData
487            | InvalidProprietaryKey
488            | NoMorePairs
489            | NonStandardSighashType(_)
490            | InvalidPublicKey(_)
491            | InvalidSecp256k1PublicKey(_)
492            | InvalidXOnlyPublicKey
493            | InvalidEcdsaSignature(_)
494            | InvalidTaprootSignature(_)
495            | InvalidControlBlock
496            | InvalidLeafVersion
497            | Taproot(_)
498            | TapTree(_)
499            | PartialDataConsumption
500            | InvalidScanKey { .. }
501            | InvalidEcdhShare { .. } => None,
502            #[cfg(feature = "silent-payments")]
503            InvalidDleqProof { .. } => None,
504        }
505    }
506}
507
508impl From<hashes::FromSliceError> for Error {
509    fn from(e: hashes::FromSliceError) -> Self { Self::InvalidHash(e) }
510}
511
512impl From<consensus::encode::Error> for Error {
513    fn from(e: consensus::encode::Error) -> Self { Self::ConsensusEncoding(e) }
514}
515
516impl From<absolute::ConversionError> for Error {
517    fn from(e: absolute::ConversionError) -> Self { Self::LockTime(e) }
518}
519
520impl From<version::UnsupportedVersionError> for Error {
521    fn from(e: version::UnsupportedVersionError) -> Self { Self::UnsupportedVersion(e) }
522}
523
524#[cfg(test)]
525mod tests {
526    use core::convert::TryFrom;
527
528    use super::*;
529
530    // Composes tree matching a given depth map, filled with dumb script leafs,
531    // each of which consists of a single push-int op code, with int value
532    // increased for each consecutive leaf.
533    pub fn compose_taproot_builder<'map>(
534        opcode: u8,
535        depth_map: impl IntoIterator<Item = &'map u8>,
536    ) -> TaprootBuilder {
537        let mut val = opcode;
538        let mut builder = TaprootBuilder::new();
539        for depth in depth_map {
540            let script = ScriptBuf::from_hex(&format!("{:02x}", val)).unwrap();
541            builder = builder.add_leaf(*depth, script).unwrap();
542            let (new_val, _) = val.overflowing_add(1);
543            val = new_val;
544        }
545        builder
546    }
547
548    #[test]
549    fn taptree_hidden() {
550        let mut builder = compose_taproot_builder(0x51, &[2, 2, 2]);
551        builder = builder
552            .add_leaf_with_ver(
553                3,
554                ScriptBuf::from_hex("b9").unwrap(),
555                LeafVersion::from_consensus(0xC2).unwrap(),
556            )
557            .unwrap();
558        builder = builder.add_hidden_node(3, TapNodeHash::all_zeros()).unwrap();
559        assert!(TapTree::try_from(builder).is_err());
560    }
561
562    #[test]
563    fn taptree_roundtrip() {
564        let mut builder = compose_taproot_builder(0x51, &[2, 2, 2, 3]);
565        builder = builder
566            .add_leaf_with_ver(
567                3,
568                ScriptBuf::from_hex("b9").unwrap(),
569                LeafVersion::from_consensus(0xC2).unwrap(),
570            )
571            .unwrap();
572        let tree = TapTree::try_from(builder).unwrap();
573        let tree_prime = TapTree::deserialize(&tree.serialize()).unwrap();
574        assert_eq!(tree, tree_prime);
575    }
576
577    #[test]
578    fn can_deserialize_non_standard_psbt_sighash_type() {
579        let non_standard_sighash = [222u8, 0u8, 0u8, 0u8]; // 32 byte value.
580        let sighash = PsbtSighashType::deserialize(&non_standard_sighash);
581        assert!(sighash.is_ok())
582    }
583
584    #[test]
585    #[should_panic(expected = "InvalidMagic")]
586    fn invalid_vector_1() {
587        let hex_psbt = b"0200000001268171371edff285e937adeea4b37b78000c0566cbb3ad64641713ca42171bf6000000006a473044022070b2245123e6bf474d60c5b50c043d4c691a5d2435f09a34a7662a9dc251790a022001329ca9dacf280bdf30740ec0390422422c81cb45839457aeb76fc12edd95b3012102657d118d3357b8e0f4c2cd46db7b39f6d9c38d9a70abcb9b2de5dc8dbfe4ce31feffffff02d3dff505000000001976a914d0c59903c5bac2868760e90fd521a4665aa7652088ac00e1f5050000000017a9143545e6e33b832c47050f24d3eeb93c9c03948bc787b32e1300";
588        crate::v0::Psbt::deserialize(hex_psbt).unwrap();
589    }
590}