miniscript_debug/psbt/
finalizer.rs

1// Miniscript
2// Written in 2020 by
3//     Sanket Kanjalkar <sanket1729@gmail.com>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! # Partially-Signed Bitcoin Transactions
16//!
17//! This module implements the Finalizer and Extractor roles defined in
18//! BIP 174, PSBT, described at
19//! `https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki`
20//!
21
22use bitcoin::blockdata::witness::Witness;
23use bitcoin::secp256k1::{self, Secp256k1};
24use bitcoin::util::key::XOnlyPublicKey;
25use bitcoin::util::sighash::Prevouts;
26use bitcoin::util::taproot::LeafVersion;
27use bitcoin::{self, PublicKey, Script, TxOut};
28
29use super::{sanity_check, Error, InputError, Psbt, PsbtInputSatisfier};
30use crate::prelude::*;
31use crate::util::witness_size;
32use crate::{
33    interpreter, BareCtx, Descriptor, ExtParams, Legacy, Miniscript, Satisfier, Segwitv0, Tap,
34};
35
36// Satisfy the taproot descriptor. It is not possible to infer the complete
37// descriptor from psbt because the information about all the scripts might not
38// be present. Also, currently the spec does not support hidden branches, so
39// inferring a descriptor is not possible
40fn construct_tap_witness(
41    spk: &Script,
42    sat: &PsbtInputSatisfier,
43    allow_mall: bool,
44) -> Result<Vec<Vec<u8>>, InputError> {
45    assert!(spk.is_v1_p2tr());
46
47    // try the key spend path first
48    if let Some(sig) =
49        <PsbtInputSatisfier as Satisfier<XOnlyPublicKey>>::lookup_tap_key_spend_sig(sat)
50    {
51        return Ok(vec![sig.to_vec()]);
52    }
53    // Next script spends
54    let (mut min_wit, mut min_wit_len) = (None, None);
55    if let Some(block_map) =
56        <PsbtInputSatisfier as Satisfier<XOnlyPublicKey>>::lookup_tap_control_block_map(sat)
57    {
58        for (control_block, (script, ver)) in block_map {
59            if *ver != LeafVersion::TapScript {
60                // We don't know how to satisfy non default version scripts yet
61                continue;
62            }
63            let ms = match Miniscript::<XOnlyPublicKey, Tap>::parse_with_ext(
64                script,
65                &ExtParams::allow_all(),
66            ) {
67                Ok(ms) => ms,
68                Err(..) => continue, // try another script
69            };
70            let mut wit = if allow_mall {
71                match ms.satisfy_malleable(sat) {
72                    Ok(ms) => ms,
73                    Err(..) => continue,
74                }
75            } else {
76                match ms.satisfy(sat) {
77                    Ok(ms) => ms,
78                    Err(..) => continue,
79                }
80            };
81            wit.push(ms.encode().into_bytes());
82            wit.push(control_block.serialize());
83            let wit_len = Some(witness_size(&wit));
84            if min_wit_len.is_some() && wit_len > min_wit_len {
85                continue;
86            } else {
87                // store the minimum
88                min_wit = Some(wit);
89                min_wit_len = wit_len;
90            }
91        }
92        min_wit.ok_or(InputError::CouldNotSatisfyTr)
93    } else {
94        // No control blocks found
95        Err(InputError::CouldNotSatisfyTr)
96    }
97}
98
99// Get the scriptpubkey for the psbt input
100pub(super) fn get_scriptpubkey(psbt: &Psbt, index: usize) -> Result<&Script, InputError> {
101    get_utxo(psbt, index).map(|utxo| &utxo.script_pubkey)
102}
103
104// Get the spending utxo for this psbt input
105pub(super) fn get_utxo(psbt: &Psbt, index: usize) -> Result<&bitcoin::TxOut, InputError> {
106    let inp = &psbt.inputs[index];
107    let utxo = if let Some(ref witness_utxo) = inp.witness_utxo {
108        witness_utxo
109    } else if let Some(ref non_witness_utxo) = inp.non_witness_utxo {
110        let vout = psbt.unsigned_tx.input[index].previous_output.vout;
111        &non_witness_utxo.output[vout as usize]
112    } else {
113        return Err(InputError::MissingUtxo);
114    };
115    Ok(utxo)
116}
117
118/// Get the Prevouts for the psbt
119pub(super) fn prevouts(psbt: &Psbt) -> Result<Vec<&bitcoin::TxOut>, super::Error> {
120    let mut utxos = vec![];
121    for i in 0..psbt.inputs.len() {
122        let utxo_ref = get_utxo(psbt, i).map_err(|e| Error::InputError(e, i))?;
123        utxos.push(utxo_ref);
124    }
125    Ok(utxos)
126}
127
128// Create a descriptor from unfinalized PSBT input.
129// Panics on out of bound input index for psbt
130// Also sanity checks that the witness script and
131// redeem script are consistent with the script pubkey.
132// Does *not* check signatures
133// We parse the insane version while satisfying because
134// we want to move the script is probably already created
135// and we want to satisfy it in any way possible.
136fn get_descriptor(psbt: &Psbt, index: usize) -> Result<Descriptor<PublicKey>, InputError> {
137    // Figure out Scriptpubkey
138    let script_pubkey = get_scriptpubkey(psbt, index)?;
139    let inp = &psbt.inputs[index];
140    // 1. `PK`: creates a `Pk` descriptor(does not check if partial sig is given)
141    if script_pubkey.is_p2pk() {
142        let script_pubkey_len = script_pubkey.len();
143        let pk_bytes = &script_pubkey.to_bytes();
144        match bitcoin::PublicKey::from_slice(&pk_bytes[1..script_pubkey_len - 1]) {
145            Ok(pk) => Ok(Descriptor::new_pk(pk)),
146            Err(e) => Err(InputError::from(e)),
147        }
148    } else if script_pubkey.is_p2pkh() {
149        // 2. `Pkh`: creates a `PkH` descriptor if partial_sigs has the corresponding pk
150        let partial_sig_contains_pk = inp.partial_sigs.iter().find(|&(&pk, _sig)| {
151            // Indirect way to check the equivalence of pubkey-hashes.
152            // Create a pubkey hash and check if they are the same.
153            // THIS IS A BUG AND *WILL* PRODUCE WRONG SATISFACTIONS FOR UNCOMPRESSED KEYS
154            // Partial sigs loses the compressed flag that is necessary
155            // TODO: See https://github.com/rust-bitcoin/rust-bitcoin/pull/836
156            // The type checker will fail again after we update to 0.28 and this can be removed
157            let addr = bitcoin::Address::p2pkh(&pk, bitcoin::Network::Bitcoin);
158            *script_pubkey == addr.script_pubkey()
159        });
160        match partial_sig_contains_pk {
161            Some((pk, _sig)) => Ok(Descriptor::new_pkh(*pk)),
162            None => Err(InputError::MissingPubkey),
163        }
164    } else if script_pubkey.is_v0_p2wpkh() {
165        // 3. `Wpkh`: creates a `wpkh` descriptor if the partial sig has corresponding pk.
166        let partial_sig_contains_pk = inp.partial_sigs.iter().find(|&(&pk, _sig)| {
167            // Indirect way to check the equivalence of pubkey-hashes.
168            // Create a pubkey hash and check if they are the same.
169            let addr = bitcoin::Address::p2wpkh(&pk, bitcoin::Network::Bitcoin)
170                .expect("Address corresponding to valid pubkey");
171            *script_pubkey == addr.script_pubkey()
172        });
173        match partial_sig_contains_pk {
174            Some((pk, _sig)) => Ok(Descriptor::new_wpkh(*pk)?),
175            None => Err(InputError::MissingPubkey),
176        }
177    } else if script_pubkey.is_v0_p2wsh() {
178        // 4. `Wsh`: creates a `Wsh` descriptor
179        if inp.redeem_script.is_some() {
180            return Err(InputError::NonEmptyRedeemScript);
181        }
182        if let Some(ref witness_script) = inp.witness_script {
183            if witness_script.to_v0_p2wsh() != *script_pubkey {
184                return Err(InputError::InvalidWitnessScript {
185                    witness_script: witness_script.clone(),
186                    p2wsh_expected: script_pubkey.clone(),
187                });
188            }
189            let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_with_ext(
190                witness_script,
191                &ExtParams::allow_all(),
192            )?;
193            Ok(Descriptor::new_wsh(ms)?)
194        } else {
195            Err(InputError::MissingWitnessScript)
196        }
197    } else if script_pubkey.is_p2sh() {
198        match inp.redeem_script {
199            None => Err(InputError::MissingRedeemScript),
200            Some(ref redeem_script) => {
201                if redeem_script.to_p2sh() != *script_pubkey {
202                    return Err(InputError::InvalidRedeemScript {
203                        redeem: redeem_script.clone(),
204                        p2sh_expected: script_pubkey.clone(),
205                    });
206                }
207                if redeem_script.is_v0_p2wsh() {
208                    // 5. `ShWsh` case
209                    if let Some(ref witness_script) = inp.witness_script {
210                        if witness_script.to_v0_p2wsh() != *redeem_script {
211                            return Err(InputError::InvalidWitnessScript {
212                                witness_script: witness_script.clone(),
213                                p2wsh_expected: redeem_script.clone(),
214                            });
215                        }
216                        let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_with_ext(
217                            witness_script,
218                            &ExtParams::allow_all(),
219                        )?;
220                        Ok(Descriptor::new_sh_wsh(ms)?)
221                    } else {
222                        Err(InputError::MissingWitnessScript)
223                    }
224                } else if redeem_script.is_v0_p2wpkh() {
225                    // 6. `ShWpkh` case
226                    let partial_sig_contains_pk = inp.partial_sigs.iter().find(|&(&pk, _sig)| {
227                        let addr = bitcoin::Address::p2wpkh(&pk, bitcoin::Network::Bitcoin)
228                            .expect("Address corresponding to valid pubkey");
229                        *redeem_script == addr.script_pubkey()
230                    });
231                    match partial_sig_contains_pk {
232                        Some((pk, _sig)) => Ok(Descriptor::new_sh_wpkh(*pk)?),
233                        None => Err(InputError::MissingPubkey),
234                    }
235                } else {
236                    //7. regular p2sh
237                    if inp.witness_script.is_some() {
238                        return Err(InputError::NonEmptyWitnessScript);
239                    }
240                    if let Some(ref redeem_script) = inp.redeem_script {
241                        let ms = Miniscript::<bitcoin::PublicKey, Legacy>::parse_with_ext(
242                            redeem_script,
243                            &ExtParams::allow_all(),
244                        )?;
245                        Ok(Descriptor::new_sh(ms)?)
246                    } else {
247                        Err(InputError::MissingWitnessScript)
248                    }
249                }
250            }
251        }
252    } else {
253        // 8. Bare case
254        if inp.witness_script.is_some() {
255            return Err(InputError::NonEmptyWitnessScript);
256        }
257        if inp.redeem_script.is_some() {
258            return Err(InputError::NonEmptyRedeemScript);
259        }
260        let ms = Miniscript::<bitcoin::PublicKey, BareCtx>::parse_with_ext(
261            script_pubkey,
262            &ExtParams::allow_all(),
263        )?;
264        Ok(Descriptor::new_bare(ms)?)
265    }
266}
267
268/// Interprets all psbt inputs and checks whether the
269/// script is correctly interpreted according to the context
270/// The psbt must have included final script sig and final witness.
271/// In other words, this checks whether the finalized psbt interprets
272/// correctly
273pub fn interpreter_check<C: secp256k1::Verification>(
274    psbt: &Psbt,
275    secp: &Secp256k1<C>,
276) -> Result<(), Error> {
277    let utxos = prevouts(psbt)?;
278    let utxos = &Prevouts::All(&utxos);
279    for (index, input) in psbt.inputs.iter().enumerate() {
280        let empty_script_sig = Script::new();
281        let empty_witness = Witness::default();
282        let script_sig = input.final_script_sig.as_ref().unwrap_or(&empty_script_sig);
283        let witness = input
284            .final_script_witness
285            .as_ref()
286            .map(|wit_slice| Witness::from_vec(wit_slice.to_vec())) // TODO: Update rust-bitcoin psbt API to use witness
287            .unwrap_or(empty_witness);
288
289        interpreter_inp_check(psbt, secp, index, utxos, &witness, script_sig)?;
290    }
291    Ok(())
292}
293
294// Run the miniscript interpreter on a single psbt input
295fn interpreter_inp_check<C: secp256k1::Verification, T: Borrow<TxOut>>(
296    psbt: &Psbt,
297    secp: &Secp256k1<C>,
298    index: usize,
299    utxos: &Prevouts<T>,
300    witness: &Witness,
301    script_sig: &Script,
302) -> Result<(), Error> {
303    let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
304
305    // Now look at all the satisfied constraints. If everything is filled in
306    // corrected, there should be no errors
307    // Interpreter check
308    {
309        let cltv = psbt.unsigned_tx.lock_time;
310        let csv = psbt.unsigned_tx.input[index].sequence;
311        let interpreter =
312            interpreter::Interpreter::from_txdata(spk, script_sig, witness, csv, cltv.into())
313                .map_err(|e| Error::InputError(InputError::Interpreter(e), index))?;
314        let iter = interpreter.iter(secp, &psbt.unsigned_tx, index, utxos);
315        if let Some(error) = iter.filter_map(Result::err).next() {
316            return Err(Error::InputError(InputError::Interpreter(error), index));
317        };
318    }
319    Ok(())
320}
321
322/// Finalize the psbt. This function takes in a mutable reference to psbt
323/// and populates the final_witness and final_scriptsig
324/// of the psbt assuming all of the inputs are miniscript as per BIP174.
325/// If any of the inputs is not miniscript, this returns a parsing error
326/// For satisfaction of individual inputs, use the satisfy API.
327/// This function also performs a sanity interpreter check on the
328/// finalized psbt which involves checking the signatures/ preimages/timelocks.
329/// The functions fails it is not possible to satisfy any of the inputs non-malleably
330/// See [finalize_mall] if you want to allow malleable satisfactions
331#[deprecated(since = "7.0.0", note = "Please use PsbtExt::finalize instead")]
332pub fn finalize<C: secp256k1::Verification>(
333    psbt: &mut Psbt,
334    secp: &Secp256k1<C>,
335) -> Result<(), super::Error> {
336    finalize_helper(psbt, secp, false)
337}
338
339/// Same as [finalize], but allows for malleable satisfactions
340pub fn finalize_mall<C: secp256k1::Verification>(
341    psbt: &mut Psbt,
342    secp: &Secp256k1<C>,
343) -> Result<(), super::Error> {
344    finalize_helper(psbt, secp, true)
345}
346
347pub fn finalize_helper<C: secp256k1::Verification>(
348    psbt: &mut Psbt,
349    secp: &Secp256k1<C>,
350    allow_mall: bool,
351) -> Result<(), super::Error> {
352    sanity_check(psbt)?;
353
354    // Actually construct the witnesses
355    for index in 0..psbt.inputs.len() {
356        finalize_input(psbt, index, secp, allow_mall)?;
357    }
358    // Interpreter is already run inside finalize_input for each input
359    Ok(())
360}
361
362// Helper function to obtain psbt final_witness/final_script_sig.
363// Does not add fields to the psbt, only returns the values.
364fn finalize_input_helper<C: secp256k1::Verification>(
365    psbt: &Psbt,
366    index: usize,
367    secp: &Secp256k1<C>,
368    allow_mall: bool,
369) -> Result<(Witness, Script), super::Error> {
370    let (witness, script_sig) = {
371        let spk = get_scriptpubkey(psbt, index).map_err(|e| Error::InputError(e, index))?;
372        let sat = PsbtInputSatisfier::new(psbt, index);
373
374        if spk.is_v1_p2tr() {
375            // Deal with tr case separately, unfortunately we cannot infer the full descriptor for Tr
376            let wit = construct_tap_witness(spk, &sat, allow_mall)
377                .map_err(|e| Error::InputError(e, index))?;
378            (wit, Script::new())
379        } else {
380            // Get a descriptor for this input.
381            let desc = get_descriptor(psbt, index).map_err(|e| Error::InputError(e, index))?;
382
383            //generate the satisfaction witness and scriptsig
384            let sat = PsbtInputSatisfier::new(psbt, index);
385            if !allow_mall {
386                desc.get_satisfaction(sat)
387            } else {
388                desc.get_satisfaction_mall(sat)
389            }
390            .map_err(|e| Error::InputError(InputError::MiniscriptError(e), index))?
391        }
392    };
393
394    let witness = bitcoin::Witness::from_vec(witness);
395    let utxos = prevouts(psbt)?;
396    let utxos = &Prevouts::All(&utxos);
397    interpreter_inp_check(psbt, secp, index, utxos, &witness, &script_sig)?;
398
399    Ok((witness, script_sig))
400}
401
402pub(super) fn finalize_input<C: secp256k1::Verification>(
403    psbt: &mut Psbt,
404    index: usize,
405    secp: &Secp256k1<C>,
406    allow_mall: bool,
407) -> Result<(), super::Error> {
408    let (witness, script_sig) = finalize_input_helper(psbt, index, secp, allow_mall)?;
409
410    // Now mutate the psbt input. Note that we cannot error after this point.
411    // If the input is mutated, it means that the finalization succeeded.
412    {
413        let input = &mut psbt.inputs[index];
414        //Fill in the satisfactions
415        input.final_script_sig = if script_sig.is_empty() {
416            None
417        } else {
418            Some(script_sig)
419        };
420        input.final_script_witness = if witness.is_empty() {
421            None
422        } else {
423            Some(witness)
424        };
425        //reset everything
426        input.partial_sigs.clear(); // 0x02
427        input.sighash_type = None; // 0x03
428        input.redeem_script = None; // 0x04
429        input.witness_script = None; // 0x05
430        input.bip32_derivation.clear(); // 0x05
431                                        // finalized witness 0x06 and 0x07 are not clear
432                                        // 0x09 Proof of reserves not yet supported
433        input.ripemd160_preimages.clear(); // 0x0a
434        input.sha256_preimages.clear(); // 0x0b
435        input.hash160_preimages.clear(); // 0x0c
436        input.hash256_preimages.clear(); // 0x0d
437                                         // psbt v2 fields till 0x012 not supported
438        input.tap_key_sig = None; // 0x013
439        input.tap_script_sigs.clear(); // 0x014
440        input.tap_scripts.clear(); // 0x015
441        input.tap_key_origins.clear(); // 0x16
442        input.tap_internal_key = None; // x017
443        input.tap_merkle_root = None; // 0x018
444    }
445
446    Ok(())
447}
448
449#[cfg(test)]
450mod tests {
451    use bitcoin::consensus::encode::deserialize;
452    use bitcoin::hashes::hex::FromHex;
453
454    use super::*;
455    use crate::psbt::PsbtExt;
456
457    #[test]
458    fn tests_from_bip174() {
459        let mut psbt: bitcoin::util::psbt::PartiallySignedTransaction = deserialize(&Vec::<u8>::from_hex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000002202029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01220202dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d7483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01010304010000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e887220203089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f012202023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e73473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d2010103040100000001042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000").unwrap()).unwrap();
460
461        let secp = Secp256k1::verification_only();
462        psbt.finalize_mut(&secp).unwrap();
463
464        let expected: bitcoin::util::psbt::PartiallySignedTransaction = deserialize(&Vec::<u8>::from_hex("70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000107da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae0001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8870107232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b20289030108da0400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000").unwrap()).unwrap();
465        assert_eq!(psbt, expected);
466    }
467}