Skip to main content

psbt_v2/v2/miniscript/
finalize.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Implementation of the Finalizer role as defined in [BIP-174].
4//!
5//! [BIP-174]: <https://github.com/bitcoin/bips/blob/master/bip-0174.media wiki>
6
7use alloc::collections::BTreeMap;
8use core::fmt;
9
10use bitcoin::hashes::hash160;
11use bitcoin::secp256k1::{Secp256k1, Verification};
12use bitcoin::taproot::LeafVersion;
13use bitcoin::{sighash, Address, Network, Script, ScriptBuf, Txid, Witness, XOnlyPublicKey};
14use miniscript::{
15    interpreter, BareCtx, Descriptor, ExtParams, Legacy, Miniscript, Satisfier, Segwitv0, SigType,
16    Tap, ToPublicKey,
17};
18
19use crate::error::{write_err, FundingUtxoError};
20use crate::prelude::*;
21use crate::v2::map::input::{self, Input};
22use crate::v2::miniscript::satisfy::InputSatisfier;
23use crate::v2::miniscript::InterpreterCheckError;
24use crate::v2::{DetermineLockTimeError, PartialSigsSighashTypeError, Psbt};
25
26/// Implements the BIP-370 Finalized role.
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub struct Finalizer(Psbt);
30
31impl Finalizer {
32    /// Creates an `Finalizer`.
33    ///
34    /// A finalizer can only be created if all inputs have a funding UTXO.
35    pub fn new(psbt: Psbt) -> Result<Self, Error> {
36        // TODO: Consider doing this with combinators.
37        for input in psbt.inputs.iter() {
38            let _ = input.funding_utxo()?;
39        }
40        let _ = psbt.determine_lock_time()?;
41        psbt.check_partial_sigs_sighash_type()?;
42
43        Ok(Self(psbt))
44    }
45
46    /// Returns this PSBT's unique identification.
47    pub fn id(&self) -> Txid {
48        self.0.id().expect("Finalizer guarantees lock time can be determined")
49    }
50
51    /// Finalize the PSBT.
52    ///
53    /// # Returns
54    ///
55    /// Returns the finalized PSBT without modifying the original.
56    #[must_use = "returns the finalized PSBT without modifying the original"]
57    pub fn finalize<C: Verification>(&self, secp: &Secp256k1<C>) -> Result<Psbt, FinalizeError> {
58        let mut inputs = vec![];
59        for (input_index, input) in self.0.inputs.iter().enumerate() {
60            match self.finalize_input(input) {
61                Ok(input) => inputs.push(input),
62                // TODO: Do we want to continue loop and return a vector of errors?
63                Err(error) => return Err(FinalizeError::FinalizeInput { input_index, error }),
64            }
65        }
66
67        let finalized =
68            Psbt { global: self.0.global.clone(), inputs, outputs: self.0.outputs.to_vec() };
69
70        finalized.interpreter_check(secp)?;
71        Ok(finalized)
72    }
73
74    // `index` must be the input index of `input` which references an input in `self` - this gets rid of out-of-bounds error path.
75    fn finalize_input(&self, input: &Input) -> Result<Input, FinalizeInputError> {
76        let allow_mall = true; // TODO: Add mall and no-mall versions.
77        let (script_sig, witness) = self.final_script_sig_and_witness(input, allow_mall)?;
78
79        Ok(input.finalize(script_sig, witness)?.clone())
80    }
81
82    /// Returns the final script_sig and final witness for this input.
83    // TODO: Think harder about this.
84    //
85    // Input finalizer should only set script sig and witness iff one is required
86    //
87    // > The Input Finalizer must only accept a PSBT. For each input, the Input Finalizer determines
88    // > if the input has enough data to pass validation. If it does, it must construct the 0x07
89    // > Finalized scriptSig and 0x08 Finalized scriptWitness and place them into the input key-value
90    // > map. If scriptSig is empty for an input, 0x07 should remain unset rather than assigned an
91    // > empty array. Likewise, if no scriptWitness exists for an input, 0x08 should remain unset
92    // > rather than assigned an empty array.
93    //
94    // However a finalized input _must_ have them both set.
95    //
96    // > It checks whether all inputs have complete scriptSigs and scriptWitnesses by checking for
97    // > the presence of 0x07 Finalized scriptSig and 0x08 Finalized scriptWitness typed records. If
98    // > they do, the Transaction Extractor should ...
99    //
100    // TODO: Check that we are doing the right thing at the right time between finalization and extraction.
101    fn final_script_sig_and_witness(
102        &self,
103        input: &Input,
104        allow_mall: bool,
105    ) -> Result<(ScriptBuf, Witness), InputError> {
106        let (witness, script_sig) = {
107            let spk =
108                &input.funding_utxo().expect("guaranteed by Finalizer invariant").script_pubkey;
109            let sat = InputSatisfier { input };
110
111            if spk.is_p2tr() {
112                // Deal with taproot case separately, we cannot infer the full descriptor for taproot.
113                let wit = construct_tap_witness(spk, sat, allow_mall)?;
114                (wit, ScriptBuf::new())
115            } else {
116                // Get a descriptor for this input.
117                let desc = self.get_descriptor(input)?;
118
119                // Generate the satisfaction witness and scriptsig.
120                if !allow_mall {
121                    desc.get_satisfaction(sat)?
122                } else {
123                    desc.get_satisfaction_mall(sat)?
124                }
125            }
126        };
127
128        let witness = Witness::from_slice(&witness);
129        Ok((script_sig, witness))
130    }
131
132    /// Creates a descriptor from an unfinalized PSBT input.
133    ///
134    /// Panics on out of bound input index for psbt Also sanity checks that the witness script and
135    /// redeem script are consistent with the script pubkey. Does *not* check signatures We parse
136    /// the insane version while satisfying because we want to move the script is probably already
137    /// created and we want to satisfy it in any way possible.
138    fn get_descriptor(&self, input: &Input) -> Result<Descriptor<bitcoin::PublicKey>, InputError> {
139        let mut map: BTreeMap<hash160::Hash, bitcoin::PublicKey> = BTreeMap::new();
140
141        // TODO(Tobin): Understand why we use keys from all inputs?
142        let psbt_inputs = &self.0.inputs;
143        for psbt_input in psbt_inputs {
144            // Use BIP32 Derviation to get set of all possible keys.
145            let public_keys = psbt_input.bip32_derivations.keys();
146            for key in public_keys {
147                let bitcoin_key = *key;
148                let hash = bitcoin_key.pubkey_hash().to_raw_hash();
149                map.insert(hash, bitcoin_key);
150            }
151        }
152
153        // Figure out Scriptpubkey
154        let script_pubkey = &input.funding_utxo().expect("guaranteed by Finalizer").script_pubkey;
155        // 1. `PK`: creates a `Pk` descriptor(does not check if partial sig is given)
156        if script_pubkey.is_p2pk() {
157            let script_pubkey_len = script_pubkey.len();
158            let pk_bytes = &script_pubkey.to_bytes();
159            match bitcoin::PublicKey::from_slice(&pk_bytes[1..script_pubkey_len - 1]) {
160                Ok(pk) => Ok(Descriptor::new_pk(pk)),
161                Err(e) => Err(InputError::from(e)),
162            }
163        } else if script_pubkey.is_p2pkh() {
164            // 2. `Pkh`: creates a `PkH` descriptor if partial_sigs has the corresponding pk
165            let partial_sig_contains_pk = input.partial_sigs.iter().find(|&(&pk, _sig)| {
166                // Indirect way to check the equivalence of pubkey-hashes.
167                // Create a pubkey hash and check if they are the same.
168                // THIS IS A BUG AND *WILL* PRODUCE WRONG SATISFACTIONS FOR UNCOMPRESSED KEYS
169                // Partial sigs loses the compressed flag that is necessary
170                // TODO: See https://github.com/rust-bitcoin/rust-bitcoin/pull/836
171                // The type checker will fail again after we update to 0.28 and this can be removed
172                let addr = Address::p2pkh(pk, Network::Bitcoin);
173                *script_pubkey == addr.script_pubkey()
174            });
175            match partial_sig_contains_pk {
176                Some((pk, _sig)) => Descriptor::new_pkh(*pk).map_err(InputError::from),
177                None => Err(InputError::MissingPubkey),
178            }
179        } else if script_pubkey.is_p2wpkh() {
180            // 3. `Wpkh`: creates a `wpkh` descriptor if the partial sig has corresponding pk.
181            let partial_sig_contains_pk = input.partial_sigs.iter().find(|&(&pk, _sig)| {
182                match bitcoin::key::CompressedPublicKey::try_from(pk) {
183                    Ok(compressed) => {
184                        // Indirect way to check the equivalence of pubkey-hashes.
185                        // Create a pubkey hash and check if they are the same.
186                        let addr = bitcoin::Address::p2wpkh(&compressed, bitcoin::Network::Bitcoin);
187                        *script_pubkey == addr.script_pubkey()
188                    }
189                    Err(_) => false,
190                }
191            });
192            match partial_sig_contains_pk {
193                Some((pk, _sig)) => Ok(Descriptor::new_wpkh(*pk)?),
194                None => Err(InputError::MissingPubkey),
195            }
196        } else if script_pubkey.is_p2wsh() {
197            // 4. `Wsh`: creates a `Wsh` descriptor
198            if input.redeem_script.is_some() {
199                return Err(InputError::NonEmptyRedeemScript);
200            }
201            if let Some(ref witness_script) = input.witness_script {
202                if witness_script.to_p2wsh() != *script_pubkey {
203                    return Err(InputError::InvalidWitnessScript {
204                        witness_script: witness_script.clone(),
205                        p2wsh_expected: script_pubkey.clone(),
206                    });
207                }
208                let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_with_ext(
209                    witness_script,
210                    &ExtParams::allow_all(),
211                )?;
212                Ok(Descriptor::new_wsh(ms.substitute_raw_pkh(&map))?)
213            } else {
214                Err(InputError::MissingWitnessScript)
215            }
216        } else if script_pubkey.is_p2sh() {
217            match input.redeem_script {
218                None => Err(InputError::MissingRedeemScript),
219                Some(ref redeem_script) => {
220                    if redeem_script.to_p2sh() != *script_pubkey {
221                        return Err(InputError::InvalidRedeemScript {
222                            redeem: redeem_script.clone(),
223                            p2sh_expected: script_pubkey.clone(),
224                        });
225                    }
226                    if redeem_script.is_p2wsh() {
227                        // 5. `ShWsh` case
228                        if let Some(ref witness_script) = input.witness_script {
229                            if witness_script.to_p2wsh() != *redeem_script {
230                                return Err(InputError::InvalidWitnessScript {
231                                    witness_script: witness_script.clone(),
232                                    p2wsh_expected: redeem_script.clone(),
233                                });
234                            }
235                            let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_with_ext(
236                                witness_script,
237                                &ExtParams::allow_all(),
238                            )?;
239                            Ok(Descriptor::new_sh_wsh(ms.substitute_raw_pkh(&map))?)
240                        } else {
241                            Err(InputError::MissingWitnessScript)
242                        }
243                    } else if redeem_script.is_p2wpkh() {
244                        // 6. `ShWpkh` case
245                        let partial_sig_contains_pk =
246                            input.partial_sigs.iter().find(|&(&pk, _sig)| {
247                                match bitcoin::key::CompressedPublicKey::try_from(pk) {
248                                    Ok(compressed) => {
249                                        let addr = bitcoin::Address::p2wpkh(
250                                            &compressed,
251                                            bitcoin::Network::Bitcoin,
252                                        );
253                                        *redeem_script == addr.script_pubkey()
254                                    }
255                                    Err(_) => false,
256                                }
257                            });
258                        match partial_sig_contains_pk {
259                            Some((pk, _sig)) => Ok(Descriptor::new_sh_wpkh(*pk)?),
260                            None => Err(InputError::MissingPubkey),
261                        }
262                    } else {
263                        //7. regular p2sh
264                        if input.witness_script.is_some() {
265                            return Err(InputError::NonEmptyWitnessScript);
266                        }
267                        if let Some(ref redeem_script) = input.redeem_script {
268                            let ms = Miniscript::<bitcoin::PublicKey, Legacy>::parse_with_ext(
269                                redeem_script,
270                                &ExtParams::allow_all(),
271                            )?;
272                            Ok(Descriptor::new_sh(ms)?)
273                        } else {
274                            Err(InputError::MissingWitnessScript)
275                        }
276                    }
277                }
278            }
279        } else {
280            // 8. Bare case
281            if input.witness_script.is_some() {
282                return Err(InputError::NonEmptyWitnessScript);
283            }
284            if input.redeem_script.is_some() {
285                return Err(InputError::NonEmptyRedeemScript);
286            }
287            let ms = Miniscript::<bitcoin::PublicKey, BareCtx>::parse_with_ext(
288                script_pubkey,
289                &ExtParams::allow_all(),
290            )?;
291            Ok(Descriptor::new_bare(ms.substitute_raw_pkh(&map))?)
292        }
293    }
294}
295
296// Satisfy the taproot descriptor. It is not possible to infer the complete descriptor from psbt
297// because the information about all the scripts might not be present. Also, currently the spec does
298// not support hidden branches, so inferring a descriptor is not possible.
299fn construct_tap_witness(
300    spk: &Script,
301    sat: InputSatisfier,
302    allow_mall: bool,
303) -> Result<Vec<Vec<u8>>, InputError> {
304    assert!(spk.is_p2tr());
305    // When miniscript tries to finalize the PSBT, it doesn't have the full descriptor (which
306    // contained a pkh() fragment) and instead resorts to parsing the raw script sig, which is
307    // translated into a "expr_raw_pkh" internally.
308    let mut map: BTreeMap<hash160::Hash, XOnlyPublicKey> = BTreeMap::new();
309
310    // We need to satisfy or dissatisfy any given key. `tap_key_origin` is the only field of PSBT
311    // Input which consist of all the keys added on a descriptor and thus we get keys from it.
312    let public_keys = sat.input.tap_key_origins.keys();
313    for key in public_keys {
314        // TODO: How is this key converting to a miniscript::interpreter::BitcoinKey?
315        // let hash = key.to_pubkeyhash(SigType::Schnorr);
316        let bitcoin_key = *key;
317        let hash = bitcoin_key.to_pubkeyhash(SigType::Schnorr);
318
319        map.insert(hash, *key);
320    }
321
322    // try the key spend path first
323    if let Some(sig) = <InputSatisfier as Satisfier<XOnlyPublicKey>>::lookup_tap_key_spend_sig(&sat)
324    {
325        return Ok(vec![sig.to_vec()]);
326    }
327    // Next script spends
328    let (mut min_wit, mut min_wit_len) = (None, None);
329    if let Some(block_map) =
330        <InputSatisfier as Satisfier<XOnlyPublicKey>>::lookup_tap_control_block_map(&sat)
331    {
332        for (control_block, (script, ver)) in block_map {
333            if *ver != LeafVersion::TapScript {
334                // We don't know how to satisfy non default version scripts yet
335                continue;
336            }
337            let ms = match Miniscript::<XOnlyPublicKey, Tap>::parse_with_ext(
338                script,
339                &ExtParams::allow_all(),
340            ) {
341                Ok(ms) => ms.substitute_raw_pkh(&map),
342                Err(..) => continue, // try another script
343            };
344            let mut wit = if allow_mall {
345                match ms.satisfy_malleable(&sat) {
346                    Ok(ms) => ms,
347                    Err(..) => continue,
348                }
349            } else {
350                match ms.satisfy(&sat) {
351                    Ok(ms) => ms,
352                    Err(..) => continue,
353                }
354            };
355            wit.push(ms.encode().into_bytes());
356            wit.push(control_block.serialize());
357            let wit_len = Some(super::witness_size(&wit));
358            if min_wit_len.is_some() && wit_len > min_wit_len {
359                continue;
360            } else {
361                // store the minimum
362                min_wit = Some(wit);
363                min_wit_len = wit_len;
364            }
365        }
366        min_wit.ok_or(InputError::CouldNotSatisfyTr)
367    } else {
368        // No control blocks found
369        Err(InputError::CouldNotSatisfyTr)
370    }
371}
372
373/// Error constructing a [`Finalizer`].
374#[derive(Debug)]
375pub enum Error {
376    /// An input is missing its funding UTXO.
377    FundingUtxo(FundingUtxoError),
378    /// Finalizer must be able to determine the lock time.
379    DetermineLockTime(DetermineLockTimeError),
380    /// An input has incorrect sighash type for its partial sigs (ECDSA).
381    PartialSigsSighashType(PartialSigsSighashTypeError),
382}
383
384impl fmt::Display for Error {
385    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386        use Error::*;
387
388        match *self {
389            // TODO: Loads of error messages are capitalized, they should not be.
390            FundingUtxo(ref e) => write_err!(f, "Finalizer missing funding UTXO"; e),
391            DetermineLockTime(ref e) =>
392                write_err!(f, "finalizer must be able to determine the lock time"; e),
393            PartialSigsSighashType(ref e) => write_err!(f, "Finalizer sighash type error"; e),
394        }
395    }
396}
397
398#[cfg(feature = "std")]
399impl std::error::Error for Error {
400    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
401        use Error::*;
402
403        match *self {
404            FundingUtxo(ref e) => Some(e),
405            DetermineLockTime(ref e) => Some(e),
406            PartialSigsSighashType(ref e) => Some(e),
407        }
408    }
409}
410
411impl From<FundingUtxoError> for Error {
412    fn from(e: FundingUtxoError) -> Self { Self::FundingUtxo(e) }
413}
414
415impl From<DetermineLockTimeError> for Error {
416    fn from(e: DetermineLockTimeError) -> Self { Self::DetermineLockTime(e) }
417}
418
419impl From<PartialSigsSighashTypeError> for Error {
420    fn from(e: PartialSigsSighashTypeError) -> Self { Self::PartialSigsSighashType(e) }
421}
422
423/// Error finalizing an input.
424#[derive(Debug)]
425pub enum FinalizeError {
426    /// Error finalizing an input.
427    FinalizeInput {
428        /// The associated input index for `error`.
429        input_index: usize,
430        /// Error finalizing input.
431        error: FinalizeInputError,
432    },
433    /// Error running the interpreter checks.
434    InterpreterCheck(InterpreterCheckError),
435}
436
437impl fmt::Display for FinalizeError {
438    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439        use FinalizeError::*;
440
441        match *self {
442            FinalizeInput { input_index, ref error } =>
443                write_err!(f, "failed to finalize input at index {}", input_index; error),
444            InterpreterCheck(ref e) => write_err!(f, "error running the interpreter checks"; e),
445        }
446    }
447}
448
449#[cfg(feature = "std")]
450impl std::error::Error for FinalizeError {
451    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
452        use FinalizeError::*;
453
454        match *self {
455            FinalizeInput { input_index: _, ref error } => Some(error),
456            InterpreterCheck(ref error) => Some(error),
457        }
458    }
459}
460
461impl From<InterpreterCheckError> for FinalizeError {
462    fn from(e: InterpreterCheckError) -> Self { Self::InterpreterCheck(e) }
463}
464
465/// Error finalizing an input.
466#[derive(Debug)]
467pub enum FinalizeInputError {
468    /// Failed to get final script_sig and final witness.
469    Final(InputError),
470    /// Failed to create a finalized input from final fields.
471    Input(input::FinalizeError),
472}
473
474impl fmt::Display for FinalizeInputError {
475    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476        use FinalizeInputError::*;
477
478        match *self {
479            Final(ref e) => write_err!(f, "final"; e),
480            Input(ref e) => write_err!(f, "input"; e),
481        }
482    }
483}
484
485#[cfg(feature = "std")]
486impl std::error::Error for FinalizeInputError {
487    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
488        use FinalizeInputError::*;
489
490        match *self {
491            Final(ref e) => Some(e),
492            Input(ref e) => Some(e),
493        }
494    }
495}
496
497impl From<InputError> for FinalizeInputError {
498    fn from(e: InputError) -> Self { Self::Final(e) }
499}
500
501impl From<input::FinalizeError> for FinalizeInputError {
502    fn from(e: input::FinalizeError) -> Self { Self::Input(e) }
503}
504
505/// Error type for Pbst Input
506#[derive(Debug)]
507pub enum InputError {
508    /// Get the secp Errors directly
509    SecpErr(bitcoin::secp256k1::Error),
510    /// Key errors
511    KeyErr(bitcoin::key::FromSliceError),
512    /// Could not satisfy taproot descriptor
513    /// This error is returned when both script path and key paths could not be
514    /// satisfied. We cannot return a detailed error because we try all miniscripts
515    /// in script spend path, we cannot know which miniscript failed.
516    CouldNotSatisfyTr,
517    /// Error doing an interpreter-check on a finalized psbt
518    Interpreter(interpreter::Error),
519    /// Redeem script does not match the p2sh hash
520    InvalidRedeemScript {
521        /// Redeem script
522        redeem: ScriptBuf,
523        /// Expected p2sh Script
524        p2sh_expected: ScriptBuf,
525    },
526    /// Witness script does not match the p2wsh hash
527    InvalidWitnessScript {
528        /// Witness Script
529        witness_script: ScriptBuf,
530        /// Expected p2wsh script
531        p2wsh_expected: ScriptBuf,
532    },
533    /// Invalid sig
534    InvalidSignature {
535        /// The bitcoin public key
536        pubkey: bitcoin::PublicKey,
537        /// The (incorrect) signature
538        sig: Vec<u8>,
539    },
540    /// Pass through the underlying errors in miniscript
541    MiniscriptError(miniscript::Error),
542    /// Missing redeem script for p2sh
543    MissingRedeemScript,
544    /// Missing witness
545    MissingWitness,
546    /// used for public key corresponding to pkh/wpkh
547    MissingPubkey,
548    /// Missing witness script for segwit descriptors
549    MissingWitnessScript,
550    ///Missing both the witness and non-witness utxo
551    MissingUtxo,
552    /// Non empty Witness script for p2sh
553    NonEmptyWitnessScript,
554    /// Non empty Redeem script
555    NonEmptyRedeemScript,
556    /// Non Standard sighash type
557    NonStandardSighashType(sighash::NonStandardSighashTypeError),
558    /// Sighash did not match
559    WrongSighashFlag {
560        /// required sighash type
561        required: sighash::EcdsaSighashType,
562        /// the sighash type we got
563        got: sighash::EcdsaSighashType,
564        /// the corresponding publickey
565        pubkey: bitcoin::PublicKey,
566    },
567}
568
569#[cfg(feature = "std")]
570impl std::error::Error for InputError {
571    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
572        use self::InputError::*;
573
574        match self {
575            CouldNotSatisfyTr
576            | InvalidRedeemScript { .. }
577            | InvalidWitnessScript { .. }
578            | InvalidSignature { .. }
579            | MissingRedeemScript
580            | MissingWitness
581            | MissingPubkey
582            | MissingWitnessScript
583            | MissingUtxo
584            | NonEmptyWitnessScript
585            | NonEmptyRedeemScript
586            | NonStandardSighashType(_)
587            | WrongSighashFlag { .. } => None,
588            SecpErr(e) => Some(e),
589            KeyErr(e) => Some(e),
590            Interpreter(e) => Some(e),
591            MiniscriptError(e) => Some(e),
592        }
593    }
594}
595
596impl fmt::Display for InputError {
597    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598        match *self {
599            InputError::InvalidSignature { ref pubkey, ref sig } => {
600                write!(f, "PSBT: bad signature {} for key {:?}", pubkey, sig)
601            }
602            InputError::KeyErr(ref e) => write!(f, "Key Err: {}", e),
603            InputError::Interpreter(ref e) => write!(f, "Interpreter: {}", e),
604            InputError::SecpErr(ref e) => write!(f, "Secp Err: {}", e),
605            InputError::InvalidRedeemScript { ref redeem, ref p2sh_expected } => write!(
606                f,
607                "Redeem script {} does not match the p2sh script {}",
608                redeem, p2sh_expected
609            ),
610            InputError::InvalidWitnessScript { ref witness_script, ref p2wsh_expected } => write!(
611                f,
612                "Witness script {} does not match the p2wsh script {}",
613                witness_script, p2wsh_expected
614            ),
615            InputError::MiniscriptError(ref e) => write!(f, "Miniscript Error: {}", e),
616            InputError::MissingWitness => write!(f, "PSBT is missing witness"),
617            InputError::MissingRedeemScript => write!(f, "PSBT is Redeem script"),
618            InputError::MissingUtxo => {
619                write!(f, "PSBT is missing both witness and non-witness UTXO")
620            }
621            InputError::MissingWitnessScript => write!(f, "PSBT is missing witness script"),
622            InputError::MissingPubkey => write!(f, "Missing pubkey for a pkh/wpkh"),
623            InputError::NonEmptyRedeemScript => {
624                write!(f, "PSBT has non-empty redeem script at for legacy transactions")
625            }
626            InputError::NonEmptyWitnessScript => {
627                write!(f, "PSBT has non-empty witness script at for legacy input")
628            }
629            InputError::WrongSighashFlag { required, got, pubkey } => write!(
630                f,
631                "PSBT: signature with key {:?} had \
632                 sighashflag {:?} rather than required {:?}",
633                pubkey, got, required
634            ),
635            InputError::CouldNotSatisfyTr => write!(f, "Could not satisfy Tr descriptor"),
636            InputError::NonStandardSighashType(ref e) =>
637                write!(f, "Non-standard sighash type {}", e),
638        }
639    }
640}
641
642impl From<crate::miniscript::Error> for InputError {
643    fn from(e: crate::miniscript::Error) -> Self { Self::MiniscriptError(e) }
644}
645
646impl From<interpreter::Error> for InputError {
647    fn from(e: interpreter::Error) -> Self { Self::Interpreter(e) }
648}
649
650impl From<bitcoin::secp256k1::Error> for InputError {
651    fn from(e: bitcoin::secp256k1::Error) -> Self { Self::SecpErr(e) }
652}
653
654impl From<bitcoin::key::FromSliceError> for InputError {
655    fn from(e: bitcoin::key::FromSliceError) -> Self { Self::KeyErr(e) }
656}