rawtx_rs/
input.rs

1//! Information about Bitcoin transaction inputs.
2
3use bitcoin::blockdata::opcodes::all as opcodes;
4use bitcoin::blockdata::script;
5use bitcoin::script::Instruction;
6use bitcoin::{Sequence, TxIn};
7use std::{error, fmt};
8
9use crate::script::{
10    instructions_as_vec, Multisig, PubKeyInfo, PublicKey, Signature, SignatureInfo,
11};
12
13pub const TAPROOT_ANNEX_INDICATOR: u8 = 0x50;
14pub const TAPROOT_LEAF_TAPSCRIPT: u8 = 0xc0;
15pub const TAPROOT_LEAF_MASK: u8 = 0xfe;
16pub const ORDINALS_INSCRIPTION_MARKER: [u8; 3] = [0x6f, 0x72, 0x64]; // ASCII "ord"
17
18#[derive(Clone, Debug)]
19pub enum InputError {
20    TypeInfo(script::Error),
21    MultisigInfo(script::Error),
22    SignatureInfo(script::Error),
23    PubkeyInfo(script::Error),
24    SigOpsInfo(script::Error),
25    ScriptHashInfo(script::Error),
26}
27
28impl fmt::Display for InputError {
29    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30        match self {
31            InputError::TypeInfo(e) => write!(f, "Could not determine type of input: {}", e),
32            InputError::MultisigInfo(e) => {
33                write!(f, "Could not extract multisig infos from input: {}", e)
34            }
35            InputError::SignatureInfo(e) => {
36                write!(f, "Could not extract signature infos from input: {}", e)
37            }
38            InputError::PubkeyInfo(e) => {
39                write!(f, "Could not extract pubkey infos from input: {}", e)
40            }
41            InputError::SigOpsInfo(e) => {
42                write!(f, "Could not extract sigops infos from input: {}", e)
43            }
44            InputError::ScriptHashInfo(e) => {
45                write!(f, "Could not extract sighash infos from input: {}", e)
46            }
47        }
48    }
49}
50
51impl error::Error for InputError {
52    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
53        match *self {
54            InputError::TypeInfo(ref e) => Some(e),
55            InputError::MultisigInfo(ref e) => Some(e),
56            InputError::SignatureInfo(ref e) => Some(e),
57            InputError::PubkeyInfo(ref e) => Some(e),
58            InputError::SigOpsInfo(ref e) => Some(e),
59            InputError::ScriptHashInfo(ref e) => Some(e),
60        }
61    }
62}
63
64#[derive(Debug)]
65pub struct InputInfo {
66    pub in_type: InputType,
67    pub sequence: Sequence,
68    pub multisig_info: Option<MultisigInputInfo>,
69    pub signature_info: Vec<SignatureInfo>,
70    pub pubkey_stats: Vec<PubKeyInfo>,
71    // TODO: OpCodes vec?
72    // TODO: is_ln_unilateral_closing: bool,
73}
74
75impl InputInfo {
76    pub fn new(input: &TxIn) -> Result<InputInfo, InputError> {
77        Ok(InputInfo {
78            sequence: input.sequence,
79            in_type: input.get_type()?,
80            multisig_info: input.multisig_info()?,
81            signature_info: SignatureInfo::all_from(input)?,
82            pubkey_stats: PubKeyInfo::from_input(input)?,
83        })
84    }
85
86    /// Returns true if the input spends a SegWit output
87    pub fn is_spending_segwit(&self) -> bool {
88        match self.in_type {
89            InputType::P2shP2wpkh
90            | InputType::P2shP2wsh
91            | InputType::P2wpkh
92            | InputType::P2wsh
93            | InputType::P2trkp
94            | InputType::P2trsp
95            | InputType::P2a => true,
96            InputType::P2ms
97            | InputType::P2msLaxDer
98            | InputType::P2pk
99            | InputType::P2pkLaxDer
100            | InputType::P2pkh
101            | InputType::P2pkhLaxDer
102            | InputType::P2sh
103            | InputType::Unknown
104            | InputType::Coinbase
105            | InputType::CoinbaseWitness => false,
106        }
107    }
108
109    /// Returns true if the input spends Taproot either with a key-path or script-path spend.
110    pub fn is_spending_taproot(&self) -> bool {
111        match self.in_type {
112            InputType::P2trkp | InputType::P2trsp => true,
113            InputType::P2ms
114            | InputType::P2msLaxDer
115            | InputType::P2pk
116            | InputType::P2pkLaxDer
117            | InputType::P2pkh
118            | InputType::P2pkhLaxDer
119            | InputType::P2sh
120            | InputType::Unknown
121            | InputType::P2shP2wpkh
122            | InputType::P2shP2wsh
123            | InputType::P2wpkh
124            | InputType::P2wsh
125            | InputType::P2a
126            | InputType::Coinbase
127            | InputType::CoinbaseWitness => false,
128        }
129    }
130
131    /// Returns true if the input spends either a P2SH-nested-P2WPKH or a P2SH-nested-P2WSH input
132    pub fn is_spending_nested_segwit(&self) -> bool {
133        match self.in_type {
134            InputType::P2shP2wpkh | InputType::P2shP2wsh => true,
135            InputType::P2pk
136            | InputType::P2pkLaxDer
137            | InputType::P2pkh
138            | InputType::P2pkhLaxDer
139            | InputType::P2ms
140            | InputType::P2msLaxDer
141            | InputType::P2wpkh
142            | InputType::P2wsh
143            | InputType::P2trkp
144            | InputType::P2trsp
145            | InputType::P2sh
146            | InputType::P2a
147            | InputType::Coinbase
148            | InputType::Unknown
149            | InputType::CoinbaseWitness => false,
150        }
151    }
152
153    /// Returns true if the input spends either a native P2WPKH, a native P2WSH, a P2TR or P2A input
154    pub fn is_spending_native_segwit(&self) -> bool {
155        match self.in_type {
156            InputType::P2wpkh
157            | InputType::P2wsh
158            | InputType::P2trkp
159            | InputType::P2trsp
160            | InputType::P2a => true,
161            InputType::P2pk
162            | InputType::P2pkLaxDer
163            | InputType::P2pkh
164            | InputType::P2pkhLaxDer
165            | InputType::P2ms
166            | InputType::P2msLaxDer
167            | InputType::P2shP2wsh
168            | InputType::P2shP2wpkh
169            | InputType::P2sh
170            | InputType::Coinbase
171            | InputType::Unknown
172            | InputType::CoinbaseWitness => false,
173        }
174    }
175
176    /// Returns true if the input spends a legacy output.
177    pub fn is_spending_legacy(&self) -> bool {
178        match self.in_type {
179            InputType::P2ms
180            | InputType::P2msLaxDer
181            | InputType::P2pk
182            | InputType::P2pkLaxDer
183            | InputType::P2pkh
184            | InputType::P2pkhLaxDer
185            | InputType::P2sh
186            | InputType::Unknown => true,
187            InputType::P2wpkh
188            | InputType::P2wsh
189            | InputType::P2trkp
190            | InputType::P2trsp
191            | InputType::P2shP2wpkh
192            | InputType::P2shP2wsh
193            | InputType::P2a
194            | InputType::Coinbase
195            | InputType::CoinbaseWitness => false,
196        }
197    }
198
199    /// Returns true if the input spends a Multisig input.
200    pub fn is_spending_multisig(&self) -> bool {
201        self.multisig_info.is_some()
202    }
203}
204
205/// Contains information about a multi-signature construct used in an input.
206#[derive(PartialEq, Eq, Debug, Clone)]
207pub struct MultisigInputInfo {
208    /// Represents the number of needed signatures `m` from the possible
209    /// signatures `n`. Example: In a 2-of-3 (m = 2, n = 3) multisig there must
210    /// be signatures corresponding to two out of 3 possibly allowed Public Keys
211    /// supplied.
212    pub m_of_n: (u8, u8),
213    /// For P2MS inputs the n value (number of possible signatures) can not be
214    /// retrieved from the P2MS input. This is indicated by this boolean set to
215    /// `true`.
216    pub unknown_n: bool,
217}
218
219pub trait InputMultisigDetection {
220    fn multisig_info(&self) -> Result<Option<MultisigInputInfo>, InputError>;
221}
222
223impl InputMultisigDetection for TxIn {
224    /// Returns Some([MultisigInputInfo]) when the input detectably spends a
225    /// multisig, If the multisig spend is not detected, None() is returned.
226    fn multisig_info(&self) -> Result<Option<MultisigInputInfo>, InputError> {
227        if self.is_scripthash_input()? {
228            if let Ok(Some(redeemscript)) = self.redeem_script() {
229                if let Ok(Some(multisig)) = redeemscript.get_opcheckmultisig_n_m() {
230                    return Ok(Some(MultisigInputInfo {
231                        m_of_n: multisig,
232                        unknown_n: false,
233                    }));
234                }
235            }
236        } else if self.get_type()? == InputType::P2ms {
237            if let Ok(instructions) = crate::script::instructions_as_vec(&self.script_sig) {
238                // P2MS sigscripts consist of an OP_0 followed by up to 3 ECDSA signatures.
239                let instructions_count = instructions.len();
240                assert!(instructions_count <= 4);
241                return Ok(Some(MultisigInputInfo {
242                    m_of_n: ((instructions_count - 1) as u8, 0),
243                    unknown_n: true,
244                }));
245            }
246        }
247        Ok(None)
248    }
249}
250
251pub trait InputSigops {
252    fn sigops(&self) -> Result<usize, InputError>;
253}
254
255impl InputSigops for TxIn {
256    fn sigops(&self) -> Result<usize, InputError> {
257        const SIGOPS_SCALE_FACTOR: usize = 4;
258        let mut sigops: usize = 0;
259
260        // in P2TR and P2A scripts and coinbase inputs, no sigops are counted
261        if self.is_p2a()
262            || self.is_p2trkp()
263            || self.is_p2trsp()
264            || self.is_coinbase()
265            || self.is_coinbase_witness()
266        {
267            return Ok(0);
268        }
269
270        // While very very seldom, there can be sigops in the inputs script_sig
271        sigops += SIGOPS_SCALE_FACTOR * self.script_sig.count_sigops_legacy();
272
273        match self.get_type()? {
274            // sigops in P2SH redeem scripts (pre SegWit) are scaled by 4
275            InputType::P2sh => {
276                if let Some(redeem_script) = self.redeem_script()? {
277                    sigops += SIGOPS_SCALE_FACTOR * redeem_script.count_sigops();
278                }
279            }
280            InputType::P2shP2wsh | InputType::P2wsh => {
281                if let Some(redeem_script) = self.redeem_script()? {
282                    sigops += redeem_script.count_sigops();
283                }
284            }
285            // P2SH-P2WPKH and P2WPKH always have one sigop
286            InputType::P2shP2wpkh | InputType::P2wpkh => {
287                sigops += 1;
288            }
289            _ => (),
290        };
291
292        Ok(sigops)
293    }
294}
295
296pub trait ScriptHashInput {
297    fn redeem_script(&self) -> Result<Option<bitcoin::ScriptBuf>, InputError>;
298}
299
300impl ScriptHashInput for TxIn {
301    /// Returns the redeem script of the input. The caller must make sure the
302    /// input is script hash based, otherwise None is returned.
303    fn redeem_script(&self) -> Result<Option<bitcoin::ScriptBuf>, InputError> {
304        if !self.is_scripthash_input()? {
305            return Ok(None);
306        }
307
308        match self.get_type()? {
309            InputType::P2sh => {
310                // redeem script is the last element of the script sig
311                if let Some(instruction_result) = self.script_sig.instructions().last() {
312                    let instruction = match instruction_result {
313                        Ok(ins) => ins,
314                        Err(e) => return Err(InputError::ScriptHashInfo(e)),
315                    };
316                    if let script::Instruction::PushBytes(push_bytes) = instruction {
317                        return Ok(Some(bitcoin::ScriptBuf::from(
318                            push_bytes.as_bytes().to_vec(),
319                        )));
320                    }
321                }
322                Ok(None)
323            }
324            InputType::P2shP2wsh => {
325                // redeem script is the last element of the witness
326                if let Some(bytes) = self.witness.last() {
327                    return Ok(Some(bitcoin::ScriptBuf::from(bytes.to_vec())));
328                }
329                Ok(None)
330            }
331            InputType::P2wsh => {
332                // redeem script is the last element of the witness
333                if let Some(bytes) = self.witness.last() {
334                    return Ok(Some(bitcoin::ScriptBuf::from(bytes.to_vec())));
335                }
336                Ok(None)
337            }
338            _ => Ok(None),
339        }
340    }
341}
342
343pub trait PubkeyInput {
344    fn get_pubkey(&self) -> bitcoin::Script;
345    fn get_signature(&self) -> bitcoin::Script;
346}
347
348pub trait InputTypeDetection {
349    fn get_type(&self) -> Result<InputType, InputError>;
350    fn has_witness(&self) -> bool;
351
352    fn is_scripthash_input(&self) -> Result<bool, InputError>;
353
354    // detection:
355    fn is_p2ms(&self, strict_der_sig: bool) -> Result<bool, InputError>;
356    fn is_p2pk(&self, strict_der_sig: bool) -> Result<bool, InputError>;
357    fn is_p2pkh(&self, strict_der_sig: bool) -> Result<bool, InputError>;
358    fn is_p2sh(&self) -> Result<bool, InputError>;
359    fn is_nested_p2wpkh(&self) -> bool;
360    fn is_nested_p2wsh(&self) -> bool;
361    fn is_p2wpkh(&self) -> bool;
362    fn is_p2wsh(&self) -> bool;
363    fn is_p2trkp(&self) -> bool;
364    fn is_p2trsp(&self) -> bool;
365    fn is_p2a(&self) -> bool;
366    fn is_coinbase(&self) -> bool;
367    fn is_coinbase_witness(&self) -> bool;
368}
369
370#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
371pub enum InputType {
372    /// Pay-to-Public-Key input
373    P2pk,
374    /// Pay-to-Public-Key input when parsing the signature with non-strict DER encoding rules.
375    /// This should only appear in transactions created before [BIP-66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki)
376    /// activation in 2015.
377    P2pkLaxDer,
378    /// Pay-to-Public-Key-Hash input
379    P2pkh,
380    /// Pay-to-Public-Key-Hash input when parsing the signature with non-strict DER encoding rules.
381    /// This should only appear in transactions created before [BIP-66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki)
382    /// activation in 2015.
383    P2pkhLaxDer,
384    /// Pay-to-Script-Hash wrapped Pay-to-Witness-Public-Key-Hash input
385    P2shP2wpkh,
386    /// Pay-to-Witness-Public-Key-Hash input
387    P2wpkh,
388    /// Pay-to-Multisig input
389    P2ms,
390    /// Pay-to-Multisig input when parsing the signature with non-strict DER encoding rules.
391    /// This should only appear in transactions created before [BIP-66](https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki)
392    /// activation in 2015.
393    P2msLaxDer,
394    /// Pay-to-Script-Hash input
395    P2sh,
396    /// Pay-to-Script-Hash wrapped Pay-to-Witness-Script-Hash input
397    P2shP2wsh,
398    /// Pay-to-Witness-Script-Hash input
399    P2wsh,
400    /// Pay-to-Taproot key path input
401    P2trkp,
402    /// Pay-to-Taproot script path input
403    P2trsp,
404    /// Pay-to-Anchor input
405    P2a,
406    /// Coinbase transaction input
407    Coinbase,
408    /// Coinbase transaction input with a witness
409    CoinbaseWitness,
410    /// Unknown or unhandled input
411    Unknown,
412}
413
414impl fmt::Display for InputType {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        match self {
417            InputType::P2pk => write!(f, "P2PK"),
418            InputType::P2pkLaxDer => write!(f, "P2PK (lax DER)"),
419            InputType::P2pkh => write!(f, "P2PKH"),
420            InputType::P2pkhLaxDer => write!(f, "P2PKH (lax DER)"),
421            InputType::P2shP2wpkh => write!(f, "P2SH-P2WPKH"),
422            InputType::P2wpkh => write!(f, "P2WPKH"),
423            InputType::P2msLaxDer => write!(f, "P2MS (lax DER)"),
424            InputType::P2ms => write!(f, "P2MS"),
425            InputType::P2sh => write!(f, "P2SH"),
426            InputType::P2shP2wsh => write!(f, "P2SH-P2WSH"),
427            InputType::P2wsh => write!(f, "P2WSH"),
428            InputType::P2trkp => write!(f, "P2TR key-path"),
429            InputType::P2trsp => write!(f, "P2TR script-path"),
430            InputType::P2a => write!(f, "P2A"),
431            InputType::Coinbase => write!(f, "Coinbase"),
432            InputType::CoinbaseWitness => write!(f, "Coinbase with Witness"),
433            InputType::Unknown => write!(f, "UNKNOWN"),
434        }
435    }
436}
437
438impl InputTypeDetection for TxIn {
439    fn get_type(&self) -> Result<InputType, InputError> {
440        if self.has_witness() {
441            // check for coinbase_wittness first as coinbases can have weird
442            // input scripts which might cause an EarlyEndOfScript error in the
443            // other checks.
444            if self.is_coinbase_witness() {
445                return Ok(InputType::CoinbaseWitness);
446            } else if self.is_nested_p2wpkh() {
447                return Ok(InputType::P2shP2wpkh);
448            } else if self.is_p2wpkh() {
449                return Ok(InputType::P2wpkh);
450            } else if self.is_nested_p2wsh() {
451                return Ok(InputType::P2shP2wsh);
452            } else if self.is_p2wsh() {
453                return Ok(InputType::P2wsh);
454            } else if self.is_p2trkp() {
455                return Ok(InputType::P2trkp);
456            } else if self.is_p2trsp() {
457                return Ok(InputType::P2trsp);
458            }
459        // check for coinbase first as coinbases can have weird input scripts
460        // which might cause an EarlyEndOfScript error in the other checks.
461        } else if self.is_coinbase() {
462            return Ok(InputType::Coinbase);
463        } else if self.is_p2pkh(/* strict DER. */ true)? {
464            return Ok(InputType::P2pkh);
465        } else if self.is_p2pkh(/* strict DER. */ false)? {
466            return Ok(InputType::P2pkhLaxDer);
467        } else if self.is_p2sh()? {
468            return Ok(InputType::P2sh);
469        } else if self.is_p2pk(/* strict DER. */ true)? {
470            return Ok(InputType::P2pk);
471        } else if self.is_p2pk(/* strict DER. */ false)? {
472            return Ok(InputType::P2pkLaxDer);
473        } else if self.is_p2ms(/* strict DER. */ true)? {
474            return Ok(InputType::P2ms);
475        } else if self.is_p2ms(/* strict DER. */ false)? {
476            return Ok(InputType::P2msLaxDer);
477        } else if self.is_p2a() {
478            return Ok(InputType::P2a);
479        }
480        Ok(InputType::Unknown)
481    }
482
483    /// Indicates if the witness contains data.
484    fn has_witness(&self) -> bool {
485        !self.witness.is_empty()
486    }
487
488    /// Indicates if the input is script hash based.
489    fn is_scripthash_input(&self) -> Result<bool, InputError> {
490        match self.get_type()? {
491            InputType::P2sh | InputType::P2shP2wsh | InputType::P2wsh => Ok(true),
492            _ => Ok(false),
493        }
494    }
495
496    /// Checks if an input spends a P2PK output.
497    ///
498    /// The caller can decide if the signature must be strictly DER encoded.
499    /// All transactions present in the blockchain after July 4th, 2015 have to
500    /// be strictly DER encoded as per [BIP-66]. Setting this to `false` only
501    /// makes sense when working with historical data.
502    ///
503    /// [BIP-66]: https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki).
504    ///
505    /// A P2PK spend has only a signature in the script_sig and no witness.
506    /// `script_sig: [ <ECDSA Signature> ]`
507    /// `witness: [ ]`
508    ///
509    /// # Errors
510    ///
511    /// Returns a [`script::Error`] if the script_sig can't be parsed.
512    fn is_p2pk(&self, strict_der_sig: bool) -> Result<bool, InputError> {
513        if self.has_witness() || self.script_sig.is_empty() {
514            return Ok(false);
515        }
516
517        let instructions = match crate::script::instructions_as_vec(&self.script_sig) {
518            Ok(ins) => ins,
519            Err(e) => return Err(InputError::TypeInfo(e)),
520        };
521        if instructions.len() != 1 || !instructions[0].is_ecdsa_signature(strict_der_sig) {
522            return Ok(false);
523        }
524
525        Ok(true)
526    }
527
528    /// Checks if an input spends an P2MS output.
529    ///
530    /// The caller can decide if the signature must be strictly DER encoded.
531    /// All transactions present in the blockchain after July 4th, 2015 have to
532    /// be strictly DER encoded as per [BIP-66]. Setting this to `false` only
533    /// makes sense when working with historical data.
534    ///
535    /// [BIP-66]: https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki).
536    ///
537    /// A P2MS spend has a OP_0 followed by one to three signatures in the script_sig.
538    /// It doesn't have a witness.
539    /// `script_sig: [ OP_0 <ECDSA Signature> (<ECDSA Signature>) (<ECDSA Signature>) ]`
540    /// `witness: [ ]`
541    fn is_p2ms(&self, strict_der_sig: bool) -> Result<bool, InputError> {
542        if self.has_witness() {
543            return Ok(false);
544        }
545
546        let instructions = match crate::script::instructions_as_vec(&self.script_sig) {
547            Ok(ins) => ins,
548            Err(e) => return Err(InputError::TypeInfo(e)),
549        };
550
551        if instructions.len() < 2 || instructions.len() > 4 {
552            return Ok(false);
553        }
554
555        for (i, instruction) in instructions.iter().enumerate() {
556            match i {
557                0 => {
558                    // checks that the first instruction is a OP_0
559                    if let script::Instruction::PushBytes(bytes) = instruction {
560                        if !bytes.is_empty() {
561                            return Ok(false);
562                        };
563                    } else {
564                        return Ok(false);
565                    };
566                }
567                1..=3 => {
568                    // and all following are ECDSA Signatures
569                    if !instruction.is_ecdsa_signature(strict_der_sig) {
570                        return Ok(false);
571                    }
572                }
573                _ => return Ok(false),
574            }
575        }
576
577        Ok(true)
578    }
579
580    /// Checks if an input spends a P2PKH output.
581    ///
582    /// The caller can decide if the signature must be strictly DER encoded.
583    /// All transactions present in the blockchain after July 4th, 2015 have to
584    /// be strictly DER encoded as per [BIP-66]. Setting this to `false` only
585    /// makes sense when working with historical data.
586    ///
587    /// [BIP-66]: https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki).
588    ///
589    /// A P2PKH spend has a public key and a signature in the script_sig. It
590    /// doesn't have a witness.
591    /// `script_sig: [ <ECDSA Signature> <PublicKey> ]`
592    /// `witness: [ ]`
593    ///
594    /// # Errors
595    ///
596    /// Returns a [`script::Error`] if the script_sig can't be parsed.
597    fn is_p2pkh(&self, strict_der_sig: bool) -> Result<bool, InputError> {
598        if self.has_witness() {
599            return Ok(false);
600        }
601
602        let instructions = match crate::script::instructions_as_vec(&self.script_sig) {
603            Ok(ins) => ins,
604            Err(e) => return Err(InputError::TypeInfo(e)),
605        };
606        if instructions.len() != 2
607            || !instructions[0].is_ecdsa_signature(strict_der_sig)
608            || !instructions[1].is_pubkey()
609        {
610            return Ok(false);
611        }
612
613        Ok(true)
614    }
615
616    /// Checks if an input spends a P2SH output.
617    ///
618    /// A P2SH output has at least an redeem script as last script_sig element.
619    /// We can test this by making sure The witness is empty.
620    /// `script_sig: [ .. <redeem script> ]`
621    /// `witness: [  ]`
622    ///
623    /// # Errors
624    ///
625    /// Returns a [`script::Error`] if the script can't be parsed.
626    fn is_p2sh(&self) -> Result<bool, InputError> {
627        if self.has_witness()
628            || self.is_p2pkh(false)?
629            || self.is_p2pk(false)?
630            || self.is_p2ms(false)?
631            || self.is_p2pkh(true)?
632            || self.is_p2pk(true)?
633            || self.is_p2ms(true)?
634            || self.is_coinbase()
635            || self.script_sig.is_empty()
636        {
637            return Ok(false);
638        }
639
640        Ok(true)
641    }
642
643    /// Checks if an input spends a Nested P2WPKH output.
644    ///
645    /// A nested P2WPKH output has a OP_PUSHBYTES_22 in the script_sig. The
646    /// pushed data contains an OP_0 and an OP_PUSHBYTES_20 pushing a 20 byte
647    /// hash. The witness contains an ECDSA signature and a public key. The
648    /// signature must be strictly DER encoded.
649    /// `script_sig: [ <OP_PUSHBYTES_22 [<OP_0 OP_PUSHBYTES_20 [20-byte hash]>]>]`
650    /// `witness: [ <ECDSA Signature> <PublicKey> ]`
651    fn is_nested_p2wpkh(&self) -> bool {
652        if self.script_sig.len() != 23 || self.witness.len() != 2 {
653            return false;
654        }
655
656        let script_sig = self.script_sig.as_bytes();
657        if script_sig[0] == opcodes::OP_PUSHBYTES_22.to_u8()
658            && script_sig[1] == opcodes::OP_PUSHBYTES_0.to_u8()
659            && script_sig[2] == opcodes::OP_PUSHBYTES_20.to_u8()
660            && self.witness.to_vec()[0].is_ecdsa_signature(/* strict DER */ true)
661            && self.witness.to_vec()[1].is_pubkey()
662        {
663            return true;
664        }
665
666        false
667    }
668
669    /// Checks if an input spends a nested P2WSH output.
670    ///
671    /// A nested P2WSH input has a single PUSH_BYTE_34 instruction which pushes
672    /// a nested script containing two instructions: a OP_0 and a PUSH_BYTES_32.
673    /// The witness contains at least the redeem script as last element.
674    /// `script_sig: [ <OP_PUSH_BYTE_34> [OP_0 PUSH_BYTES_32 <32 byte hash>] ]`
675    /// `witness: [ .. <redeem script> ]`
676    ///
677    /// Returns a [`script::Error`] if the script can't be parsed.
678    fn is_nested_p2wsh(&self) -> bool {
679        if self.script_sig.len() != 35 || self.witness.is_empty() {
680            return false;
681        }
682
683        let script_sig = self.script_sig.as_bytes();
684        if script_sig[0] == opcodes::OP_PUSHBYTES_34.to_u8()
685            && script_sig[1] == opcodes::OP_PUSHBYTES_0.to_u8()
686            && script_sig[2] == opcodes::OP_PUSHBYTES_32.to_u8()
687        {
688            return true;
689        }
690        false
691    }
692
693    /// Checks if an input spends a P2WPKH output.
694    ///
695    /// A P2WPKH output has an empty script_sig. The witness contains an ECDSA
696    /// signature and a public key. The signature must be strictly DER encoded.
697    /// `script_sig: [ ]`
698    /// `witness: [ <ECDSA Signature> <PublicKey> ]`
699    fn is_p2wpkh(&self) -> bool {
700        if !self.script_sig.is_empty() || self.witness.len() != 2 {
701            return false;
702        }
703
704        if self.witness.to_vec()[0].is_ecdsa_signature(/* strict DER */ true)
705            && self.witness.to_vec()[1].is_pubkey()
706        {
707            return true;
708        }
709
710        false
711    }
712
713    /// Checks if an input spends a P2WSH output.
714    ///
715    /// A P2WSH output has an empty script_sig. The data is contained in the witness.
716    /// `script_sig: [ ]`
717    /// `witness: [ .. ]`
718    fn is_p2wsh(&self) -> bool {
719        if !self.script_sig.is_empty()
720            || !self.has_witness()
721            || self.is_p2wpkh()
722            || self.is_p2trkp()
723            || self.is_p2trsp()
724        {
725            return false;
726        }
727
728        true
729    }
730
731    /// Checks if an input spends a P2TR-keypath output.
732    ///
733    /// A P2TR output has an empty script_sig. The witness contains a Schnorr signature
734    /// and optionally an annex.
735    /// `script_sig: [ ]`
736    /// `witness: [ <schnorr signature> (<annex>) ]`
737    fn is_p2trkp(&self) -> bool {
738        if !self.script_sig.is_empty() || !self.has_witness() || self.witness.len() > 2 {
739            return false;
740        }
741        if self.witness.len() == 1 {
742            // without annex
743            return self.witness.to_vec()[0].is_schnorr_signature();
744        } else if self.witness.len() == 2 {
745            // with annex
746            if !self.witness.to_vec()[1].is_empty()
747                && self.witness.to_vec()[1][0] == TAPROOT_ANNEX_INDICATOR
748            {
749                return self.witness.to_vec()[0].is_schnorr_signature();
750            }
751        }
752        false
753    }
754
755    /// Checks if an input spends a P2TR-scriptpath output.
756    ///
757    /// A P2TR output has an empty script_sig. The witness script-input-data (zero-to-many),
758    /// a script, a control block, and optionally an annex.
759    /// `script_sig: [ ]`
760    /// `witness: [ (<script input data>, <script input data>, ...) <script> <control block> (<annex>) ]`
761    fn is_p2trsp(&self) -> bool {
762        if !self.script_sig.is_empty() || !self.has_witness() || self.witness.len() < 2 {
763            return false;
764        }
765
766        let last_witness_element_index = self.witness.len() - 1;
767        let mut control_block_index = last_witness_element_index;
768        let witness_vec = self.witness.to_vec();
769
770        // check for annex
771        if !witness_vec[last_witness_element_index].is_empty()
772            && witness_vec[last_witness_element_index][0] == TAPROOT_ANNEX_INDICATOR
773        {
774            control_block_index -= 1;
775        }
776
777        // check for control block
778        let control_block = &witness_vec[control_block_index];
779        if control_block.len() < 1 + 32 || (control_block.len() - 1) % 32 != 0 {
780            return false;
781        }
782
783        if control_block[0] & TAPROOT_LEAF_MASK == TAPROOT_LEAF_TAPSCRIPT {
784            return true;
785        }
786
787        false
788    }
789
790    /// Checks if an input spends a P2A output.
791    ///
792    /// A P2A output has an empty script_sig and an empty witness.
793    fn is_p2a(&self) -> bool {
794        self.script_sig.is_empty() && !self.has_witness()
795    }
796
797    /// Checks if an input is a coinbase without witness data.
798    ///
799    /// A coinbase has a an Outpoint with an all zero txid and an output index
800    /// of 0xffffffff. The witness is empty.
801    fn is_coinbase(&self) -> bool {
802        !self.has_witness()
803            && self.previous_output.vout == 0xffffffff
804            && self.previous_output.is_null()
805    }
806
807    /// Checks if an input is a coinbase with witness data. On mainnet, this
808    /// input type is expected after SegWit activation at height 481824
809    /// (24 August 2017).
810    ///
811    /// A coinbase has a an Outpoint with an all zero txid and an output index
812    /// of 0xffffffff. The witness is not empty.
813    fn is_coinbase_witness(&self) -> bool {
814        self.has_witness()
815            && self.previous_output.vout == 0xffffffff
816            && self.previous_output.is_null()
817    }
818}
819
820pub trait InputInscriptionDetection {
821    fn reveals_inscription(&self) -> Result<bool, script::Error>;
822}
823
824impl InputInscriptionDetection for TxIn {
825    fn reveals_inscription(&self) -> Result<bool, script::Error> {
826        if !self.is_p2trsp() {
827            return Ok(false);
828        }
829        // Inscription reveals can be identified by inspecting the tapscript
830        if let Some(tapscript) = self.witness.tapscript() {
831            if let Ok(instructions) = instructions_as_vec(tapscript) {
832                let mut instruction_iter = instructions.iter();
833                while let Some(instruction) = instruction_iter.next() {
834                    if matches!(instruction, Instruction::PushBytes(bytes) if bytes.is_empty())
835                        && matches!(instruction_iter.next(), Some(Instruction::Op(op)) if op == &opcodes::OP_IF)
836                        && matches!(instruction_iter.next(), Some(Instruction::PushBytes(bytes)) if bytes.as_bytes() == ORDINALS_INSCRIPTION_MARKER.to_vec())
837                    {
838                        return Ok(true);
839                    }
840                }
841            }
842        }
843        Ok(false)
844    }
845}
846
847#[cfg(test)]
848mod tests {
849    use super::{
850        InputInfo, InputInscriptionDetection, InputMultisigDetection, InputSigops, InputType,
851        InputTypeDetection, MultisigInputInfo,
852    };
853    use bitcoin::Transaction;
854
855    #[test]
856    fn reveals_inscription() {
857        // mainnet ba4f42037f92c2782ee3dd8c75ce0ce80d8a04b8d36e6ed6a36452f512e66dfd
858        let rawtx = hex::decode("02000000000101da5159649742d35e069f74724bb72cbb116415d72e11771a17420e2201d4ecf30000000000fdffffff0122020000000000002251201e6960b35d5da5c6c9ce0a18d989518bf546b4968a4ad046d9d664f5585f50440340ad42d4bec479e22866603b11048a64a3e5d366046d494278336798a51fd5fe5166221949e3025b25b505433ed871e20399fd82ffc867b70de8acef8e86f91f857e20318de3d918b6ca5ce115f5b00b3ed0b9c85ca7ebe8f0ccbe9de0e05e45de9293ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d3800387b2270223a226272632d3230222c226f70223a227472616e73666572222c227469636b223a224d4d5353222c22616d74223a22323030227d6821c1318de3d918b6ca5ce115f5b00b3ed0b9c85ca7ebe8f0ccbe9de0e05e45de929300000000").unwrap();
859        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
860        let in0 = &tx.input[0];
861        assert!(in0.is_p2trsp());
862        assert!(in0.reveals_inscription().unwrap());
863
864        // mainnet 8cf0e9c282233d72bae9b90a8f5b656d5decaa75c4a8ad54f457e45cd087d295
865        let rawtx = hex::decode("02000000000101db7427b32d5755c1ea1fad429d87e6a641a2682e53c0c7cfaf3bec578c349b680000000000fdffffff022202000000000000225120f97a587234b7e22e7b5de2d61b1861e0c8774eedb9b48190fabe41b083d24926db06000000000000160014d02d8c6d6b2307d96bc2bf99e89acab7fb3a923003402817599a681318c55b335f5fb29e6755a4f23e345a8dc130cb1e478f2dec76f748378d279f5b82ca70587d91caff2f906146df28785931b09f186489f317c2add020287b98bfb98dd21a639ee15a8414e70f4bb5f8747961f4c2ef1596519d465db1ac0063036f7264010118746578742f706c61696e3b636861727365743d7574662d38004c897b2270223a2022746170222c20226f70223a2022646d742d6d696e74222c2022646570223a2022303161393235393934656563313435313261653935386466313761353231666437663835616636613731633132373061656332303432323866613661613336346930222c20227469636b223a20226e6174222c2022626c6b223a202231363934227d6821c1f7f702f2e97e53bd22500cc1207871e88abba8871df5495ad16f3c73a5d7460e00000000").unwrap();
866        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
867        let in0 = &tx.input[0];
868        assert!(in0.is_p2trsp());
869        assert!(in0.reveals_inscription().unwrap());
870    }
871
872    #[test]
873    fn input_type_detection_p2pk() {
874        // mainnet ea44e97271691990157559d0bdd9959e02790c34db6c006d779e82fa5aee708e
875        let rawtx = hex::decode("0100000001169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f400000000484730440220576497b7e6f9b553c0aba0d8929432550e092db9c130aae37b84b545e7f4a36c022066cb982ed80608372c139d7bb9af335423d5280350fe3e06bd510e695480914f01ffffffff0100ca9a3b000000001976a914340cfcffe029e6935f4e4e5839a2ff5f29c7a57188ac00000000").unwrap();
876        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
877        let in0 = &tx.input[0];
878        assert!(in0.is_p2pk(true).unwrap());
879        assert_eq!(in0.get_type().unwrap(), InputType::P2pk);
880    }
881
882    #[test]
883    fn input_type_detection_p2pkh() {
884        // mainnet ab8358ee2d4573d975c5b693fcd349bd09a089327f5c12ca8a4abd350f18e670
885        let rawtx = hex::decode("01000000013e536ab65e20de9b57dd2859abd7289fd0452c3f5ac672b956d9c787d1933466230000006a47304402201b6e925baff25e8f9fda211f4319a0d9bc5add80d285db5609b882a80b4c50d002200e797a1435838df602634dd69e829bc884bc995aac77ea57ddf5c92e44dacc6f012103b773940906913e962d81b6c4d7c405212bf91ae736123eba7970859edefba84effffffff0188f40000000000001976a91408653d83a8bff4c66edd72921659326bd6ef04cc88ac00000000").unwrap();
886        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
887        let in0 = &tx.input[0];
888        assert!(in0.is_p2pkh(true).unwrap());
889        assert_eq!(in0.get_type().unwrap(), InputType::P2pkh);
890    }
891
892    #[test]
893    fn input_type_detection_nested_p2wpkh() {
894        // mainnet 9671f217d4d12b1717606403468fd0b1ed7362e33282f8834d4d38e67f85767b
895        let rawtx = hex::decode("020000000001017287da3eef7369c13bacb9fb92e7b1c3aabe04ead8d76b620c3966623fb1da510000000017160014ace9fb5f9aab3d34f272775424bbacd1132ff00effffffff01614804000000000017a91431ab116fe54444ef21c7656f3bb4bfe2520210fe8702473044022053c98f1e2f3dad4ad3e53c1f2e78a833a9e3bb0a47e592dc840312d053805c4602200bb65ecf01cb49d7506df37ff61141584c731c127973d4a651ab0f5cc5de04c60121029b8741cc06f098df1fad8f1fb920e1d610024b9f0190708b0dd777ba9cd1a55300000000").unwrap();
896        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
897        let in0 = &tx.input[0];
898        assert!(in0.is_nested_p2wpkh());
899        assert_eq!(in0.get_type().unwrap(), InputType::P2shP2wpkh);
900    }
901
902    #[test]
903    fn input_type_detection_p2wpkh() {
904        // mainnet 4aba5c7fe256a9997aa93cab91f1c0405074b30b34b6a5c0bbde425322d40ee7
905        let rawtx = hex::decode("01000000000101ce73651d1ef6e687a66a76adbaf16741a205e7230bff0ae7259d36c478ec1a339a00000000ffffffff018a399c0000000000160014a14296a183d6e4c2f1696713a9db833e66fcb2d50247304402204da5741dab6897b961d996c0bdd0fed51f33afa54459769a81019d04a35fb1a802206f47143811a512a73b9d1112f5f0202740c554bf853fe18e2c72e58381c5452b012103e37b04cf32aa417e3ef25c0fe19faefcdbb6e6b2eaca38837dabc4c146cdc95000000000").unwrap();
906        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
907        let in0 = &tx.input[0];
908        assert!(in0.is_p2wpkh());
909        assert_eq!(in0.get_type().unwrap(), InputType::P2wpkh);
910    }
911
912    #[test]
913    fn input_type_detection_p2sh() {
914        // mainnet 986e143fae33eb39bb2bdb2d5e0f6cb003ab8c6e638b4adf02b5a1ae8a81b4ec
915        let rawtx = hex::decode("0200000001b5d093fa119005dbb1e6efe4abae8352b9be31999dd498e2a4242d12a1bcac8800000000910047304402203c3e5cb8e6dd567804efb6f26523229ea67ed1f4529a35fd92a64407db790cce02201ba1f5173e792cc9e63769700e8f7673eae8ac705b58caa5931ec614f56f1acc014751210377622563e0110914888b7dc9364d6d341804aa0d7f09b3dea04f14a7dad104452103c71b40231260e990938f1be55bbf8c580832bcfdcb60a4e64139f12b313fe35552aefeffffff0258c70d00000000001976a914bab670335c428fe202157fb867dd06acca4ac25388ac5c2d53000000000017a914ad69347daeb1811224627597d5d8ebffa78c4c3e8700000000").unwrap();
916        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
917        let in0 = &tx.input[0];
918        assert!(in0.is_p2sh().unwrap());
919        assert_eq!(in0.get_type().unwrap(), InputType::P2sh);
920    }
921
922    #[test]
923    fn input_sigops_nonstandard_rsk_p2sh() {
924        // mainnet 44c857ef596332bbdcec28d2337a3c7eda10c23da0dc1769325b40c9220c4816, non-standard RSK input with more than MAX_P2SH_SIGOPS (15) sigops
925        let rawtx = hex::decode("0200000001e5af92d188010aa34910c71de4a55d6f142c0989a62af388b10bd8a33b54e86c00000000fd3803004830450221008b42e9089fd73cd1367d7963664314fd7affc434766b97ef35e57f10c764eca802204bdf578f5339448053cc077b410050ca95f54f0773d1be37e4c8f7028b5b630d014730440220599fa798537dbca65defc998088b56df77993914f7b1f6d262ae210c49caf00a022013f11bc2c85befdcf36aac357e0d1cd9cd1954ccbbe6c90c56575b5e68dbb17401473044022040cf3f17bb5639e9a64c89566b21a13339e362ef27f02ac3bdd09a79b5564a4402203556c8a5ef82e99c4148ef1b9626d663fc8aabefee8eecbb016439d0dc255b6c01483045022100cc20e9db6c4ae335eb6a9af4f9f915b8dc9e6e130af4266a3c720464796d68b3022010203f84d6f9cc20fba81d2971fcb276595c6c5ed6f009f99da5cea93ff5dfdc01483045022100e39c1896c909b87f2d860ba23af7e94873dfeccee33657d28041335ca708bb36022002b70cb3050bf418645e4703958252706e3008ac7a940a7248747f5651b0ea0a01004dc801645521020ace50bab1230f8002a0bfe619482af74b338cc9e4c956add228df47e6adae1c210231a395e332dde8688800a0025cccc5771ea1aa874a633b8ab6e5c89d300c7c3621025093f439fb8006fd29ab56605ffec9cdc840d16d2361004e1337a2f86d8bd2db21026b472f7d59d201ff1f540f111b6eb329e071c30a9d23e3d2bcd128fe73dc254c2103250c11be0561b1d7ae168b1f59e39cbc1fd1ba3cf4d2140c1a365b2723a2bf93210357f7ed4c118e581f49cd3b4d9dd1edb4295f4def49d6dcf2faaaaac87a1a0a422103ae72827d25030818c4947a800187b1fbcc33ae751e248ae60094cc989fb880f62103e05bf6002b62651378b1954820539c36ca405cbb778c225395dd9ebff67802992103ecd8af1e93c57a1b8c7f917bd9980af798adeb0205e9687865673353eb041e8d59670350cd00b275532102370a9838e4d15708ad14a104ee5606b36caaaaf739d833e67770ce9fd9b3ec80210257c293086c4d4fe8943deda5f890a37d11bebd140e220faa76258a41d077b4d42103c2660a46aa73078ee6016dee953488566426cf55fc8011edd0085634d75395f92103cd3e383ec6e12719a6c69515e5559bcbe037d0aa24c187e1e26ce932e22ad7b35468aeffffffff026cd03c00000000001976a9143a267e4435e590d9e711d04954d8c8ef1003658588aca59f1d020000000017a91485aaffdabb34e8f7403291b3eff574129cc2486d8700000000").unwrap();
926        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
927        let in0 = &tx.input[0];
928        assert!(in0.is_p2sh().unwrap());
929        assert_eq!(in0.get_type().unwrap(), InputType::P2sh);
930        let in0_sigops = in0.sigops().unwrap();
931        assert_eq!(in0_sigops, 80);
932    }
933
934    #[test]
935    fn input_type_detection_nested_p2wsh() {
936        // mainnet 716e81f6ac8cb5e11bcf7d850e6049fe2e8067a1650378b4045c8ad005cac76b
937        let rawtx = hex::decode("0100000000010158cffd9e03ff81e25367b93c78fe57c9a47dfd50e3365a0a210d7a75068333340100000023220020779b3b5851cbe7c95c7b21cd919aaf3df20b1f62f1dfc13bf93e734b3c88443ffdffffff01fca10000000000001976a914ea800dbd672b26181f9858bc0abedf40be747f3688ac0400483045022100d602eb075e28221a1b4622f24fa0a60242c90742033f032d40856ce0caed097c022061d066b92311b9492def6e0cc8bc0b76f73050b607a6d268f35d18d636ec7b940147304402203335fa91e31b8ec4e3ac5ddeb7fb2546c55a7aa6ee9548844337007dd29fcaac02204fbb78515a7085a9342c97c55b4f948681c3c1c80f9b2be5c9bd8e77a8cb6c240169522102a7b58e322616b2ac3752f59349b941edb5e9913f70864fc94a927b7592fe6d952103416995a2564b1fae34f521ad6745ffd04291bbda7075105e04720879ad728da52102523aa65c4a392a2ea8d81b8cd3bc19bba4b62a152f94a83f428c9b018dfc42d553ae00000000").unwrap();
938        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
939        let in0 = &tx.input[0];
940        assert!(in0.is_nested_p2wsh());
941        assert_eq!(in0.get_type().unwrap(), InputType::P2shP2wsh);
942    }
943
944    #[test]
945    fn input_type_detection_p2ms_1of2() {
946        // mainnet 99cb2139052dc88508f2fe35235c1c5685229a45bef3db70413c5ac43c41ca0a 1-of-2 P2MS input
947        let rawtx = hex::decode("01000000013de6aff69d5ebeca70a84d1dcef768bbcadbad210084012f8cda24233c8db278000000004b00493046022100a41a9015c847f404a14fcc81bf711ee2ce57583987948d54ebe540aafca97e0d022100d4e30d1ca42f77df8290b8975aa8fc0733d7c0cfdd5067ca516bac6c4012b47a01ffffffff01607d860500000000475121037953dbf08030f67352134992643d033417eaa6fcfb770c038f364ff40d7615882100dd28dfb81abe444429c466a1e3ab7c22365c48f234ef0f8d40397202969d4e9552ae00000000").unwrap();
948        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
949        let in0 = &tx.input[0];
950        assert!(in0.is_p2ms(true).unwrap());
951        assert_eq!(in0.get_type().unwrap(), InputType::P2ms);
952        assert_eq!(
953            in0.multisig_info().unwrap().unwrap(),
954            MultisigInputInfo {
955                m_of_n: (1, 0),
956                unknown_n: true
957            }
958        );
959    }
960
961    #[test]
962    fn input_type_detection_p2ms_2of3() {
963        // mainnet 949591ad468cef5c41656c0a502d9500671ee421fadb590fbc6373000039b693 2-of-3 P2MS input
964        let rawtx = hex::decode("010000000110a5fee9786a9d2d72c25525e52dd70cbd9035d5152fac83b62d3aa7e2301d58000000009300483045022100af204ef91b8dba5884df50f87219ccef22014c21dd05aa44470d4ed800b7f6e40220428fe058684db1bb2bfb6061bff67048592c574effc217f0d150daedcf36787601483045022100e8547aa2c2a2761a5a28806d3ae0d1bbf0aeff782f9081dfea67b86cacb321340220771a166929469c34959daf726a2ac0c253f9aff391e58a3c7cb46d8b7e0fdc4801ffffffff0180a21900000000001976a914971802edf585cdbc4e57017d6e5142515c1e502888ac00000000").unwrap();
965        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
966        let in0 = &tx.input[0];
967        assert!(in0.is_p2ms(true).unwrap());
968        assert_eq!(in0.get_type().unwrap(), InputType::P2ms);
969        assert_eq!(
970            in0.multisig_info().unwrap().unwrap(),
971            MultisigInputInfo {
972                m_of_n: (2, 0),
973                unknown_n: true
974            }
975        );
976    }
977
978    #[test]
979    fn input_type_detection_unknown() {
980        // mainnet 0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae
981        let rawtx = hex::decode("0100000003d4dfa41ffe9825af0aad023f084b4dd4a599d6cb8d083e58e3a53e8ad682a6ae010000000401030103ffffffffe2274e1294e1708f344e7cd5156648750bc60d3bf3705130cdcba66675e3439b010000000401030103fffffffff41d101368d124c5b10bf130bceece07623d87767a2c950a35d1b7b6217a2329000000000401030103ffffffff0170032d00000000001976a91429c9743283afd76eed5811788b20b23f9eece00788ac00000000").unwrap();
982        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
983        let in0 = &tx.input[0];
984        assert!(in0.is_p2sh().unwrap());
985        assert_eq!(in0.get_type().unwrap(), InputType::P2sh);
986    }
987
988    #[test]
989    fn input_type_detection_p2wsh_2of2() {
990        // mainnet f72d52eaae494da7c438a8456a9b20d2791fdf2b1c818825458f8f707d7b8011
991        let rawtx = hex::decode("01000000000101ec2ad0604a0e708963a39d16936118ab5fb36dcb5d5e07f3dc2ca149412500eb0100000000ffffffff0233cc08000000000017a9147f152249241d7320a80a013fd46907c1a1eff5a4879a3a5c15000000002200202ceaf557137c8f1f8ac2c8ee4a6656f9533c250c62b40006956ecb44e5c2357e040047304402201489d616c691fb1a4dd86caa495991463d88e049fdc6e316bd09877cb4bc17de02201ebba0c182a6e28024944e63638788f44b77acbd85fbea60b1225f180def8fc901483045022100c00674b0810fe3e5db048bdd231f7a50edc1cc2ffd75a68497d43fae7853277802200a9422b26290b7521574a5c161e2116ef3e906030f6ed08ca38b6ee6c5690ddb0147522103a2ea7e0b94c48fd799bf123c1f19b50fb6d15da310db8223fd7a6afd8b03e6932102eba627e6ea5bb7e0f4c981596872d0a97d800fb836b5b3a585c3f2b99c77a0e552ae00000000").unwrap();
992        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
993        let in0 = &tx.input[0];
994        assert!(in0.is_p2wsh());
995        assert_eq!(in0.get_type().unwrap(), InputType::P2wsh);
996        assert_eq!(
997            in0.multisig_info().unwrap().unwrap(),
998            MultisigInputInfo {
999                m_of_n: (2, 2),
1000                unknown_n: false
1001            }
1002        );
1003    }
1004
1005    #[test]
1006    fn input_type_detection_p2wsh_non_multisig() {
1007        // mainnet 41f18d932642a396b3469072860a10b193c53116d869dce9e2619b23555311cc
1008        let rawtx = hex::decode("02000000000101d29021fae716cbc98333fa1a88fa2a5cf6fd2db642a94ff33d7dfff2ad361fc30100000000ffffffff01ffdc0b00000000001600142c3a8a495c16839d5d975c1ca0ee504af825b52105483045022100d45ed56c316849b6ff15a568608aa887be0f5a09ad89d76f686949f320509d5902204e61db9bf6bb965b2de1ee0bb6d687a28579b0f725bde9d0e6c9a1c0a8cb6a0f0121020251be8c4c748fce671be54e247534f34d4c1b7f7784e3fc08ef41b3805a4af820535692b33b9f226fd70cd5571c68df7bfa5463d4a334a5ceb4d200fd0663f06a0101636382012088a820697ce4e1d91cdc96d53d1bc591367bd48855a076301972842aa5ffcb8fcb8b618876a9142c3a8a495c16839d5d975c1ca0ee504af825b52188ac6704c9191960b17576a9145a59f40f4ecb1f86efb13752b600ea3b8d4c633988ac6800000000").unwrap();
1009        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1010        let in0 = &tx.input[0];
1011        assert!(in0.is_p2wsh());
1012        assert_eq!(in0.get_type().unwrap(), InputType::P2wsh);
1013        assert_eq!(in0.multisig_info().unwrap(), None);
1014    }
1015
1016    #[test]
1017    fn coinbase_input_detection() {
1018        // mainnet b39fa6c39b99683ac8f456721b270786c627ecb246700888315991877024b983 coinbase @ 300000
1019        let rawtx = hex::decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4803e09304062f503253482f0403c86d53087ceca141295a00002e522cfabe6d6d7561cf262313da1144026c8f7a43e3899c44f6145f39a36507d36679a8b7006104000000000000000000000001c8704095000000001976a91480ad90d403581fa3bf46086a91b2d9d4125db6c188ac00000000").unwrap();
1020        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1021        let in0 = &tx.input[0];
1022        assert!(in0.is_coinbase());
1023        assert_eq!(in0.get_type().unwrap(), InputType::Coinbase);
1024    }
1025
1026    #[test]
1027    fn coinbase_input_detection2() {
1028        // mainnet 636339fdd992c5e0382e70523af25ef969c15c4d1697d293037079c72e3c87d4 coinbase @ 361582
1029        // This is one of many coinbase transactions that would error with
1030        // EarlyEndOfScript before.
1031        let rawtx = hex::decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff11036e8405045583b024acec8dee00000efbffffffff01553d3b95000000001976a914ca6ecc7d4d671d8c5c964a48dbb0bc194407a30688ac00000000").unwrap();
1032        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1033        let in0 = &tx.input[0];
1034        assert!(in0.is_coinbase());
1035        assert_eq!(in0.get_type().unwrap(), InputType::Coinbase);
1036    }
1037
1038    #[test]
1039    fn p2trkp_input_detection() {
1040        // signet 75a1a2488770ba0506b9899b1d03dc232f5b22f00dffc3ca3ea6640a53de8403
1041        let rawtx = hex::decode("0200000000010232b1c6063448c8089d4e7a500399555daec6bc1f3fef281016518c1de3d154cb0000000000feffffff32b1c6063448c8089d4e7a500399555daec6bc1f3fef281016518c1de3d154cb0100000000feffffff02301b0f00000000002251204b03959143386c56a1646c9d1002314c6acecd79ee0f0976fb9fa0f1f0837b1be31f0000000000001600140af8bb24c9504e8740076bb07b755237d4af6e67014159f6076cc04503a9bc72f137aa4af523ab05f5805b5fcb9e0b0b0f258a86afd30c7298e11203f6f27a1408385ec5b9fc1d16be738d628a4aac62eef1cfdac10b0102473044022044d39c6b67334c5e1aa0be056b2200221f8ec84e7319ba1b1cbe6df4dc2f6b1d02207289dd7e3d362355f22ff1df76965391ccd9c4d529a216947d2434f16dfd9b7901210338008b55bf51d06440c64129665a56c2eb828fdad50cca74191d29f92475e962b5680000").unwrap();
1042        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1043        let in0 = &tx.input[0];
1044        assert!(in0.is_p2trkp());
1045        assert_eq!(in0.get_type().unwrap(), InputType::P2trkp);
1046        assert!(InputInfo::new(in0).unwrap().is_spending_taproot());
1047        assert!(InputInfo::new(in0).unwrap().is_spending_segwit());
1048    }
1049
1050    #[test]
1051    fn p2trsp_input_detection() {
1052        // signet 692937bb7864cfcce9f7a5171d6af3646bf479204ffb9356a0d6ce8a4a7952f1
1053        let rawtx = hex::decode("02000000000101e1e91316b8780879bf5ac7559cbb3da5c65f19e57ed822615d832c53b2eeb5360000000000ffffffff01905f010000000000160014734e7298bfe985c5e0148a5a37179b66d9ad0b0804400d1e89bad817848056c3f32b4226f70946b84d358ff5a635b70f7ce40a43a94eba9b8ce213bc56d8ab6f9bb2f90d700cfed82fd93d91f41e7b3cf27c5b3ea77b20107661134f21fc7c02223d50ab9eb3600bc3ffc3712423a1e47bb1f9a9dbf55f45a8206c60f404f8167a38fc70eaf8aa17ac351023bef86bcb9d1086a19afe95bd533388204edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10ac41c1f30544d6009c8d8d94f5d030b2e844b1a3ca036255161c479db1cca5b374dd1cc81451874bd9ebd4b6fd4bba1f84cdfb533c532365d22a0a702205ff658b17c900000000").unwrap();
1054        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1055        let in0 = &tx.input[0];
1056        assert!(in0.is_p2trsp());
1057        assert_eq!(in0.get_type().unwrap(), InputType::P2trsp);
1058        assert!(InputInfo::new(in0).unwrap().is_spending_taproot());
1059        assert!(InputInfo::new(in0).unwrap().is_spending_segwit());
1060        assert!(!in0.reveals_inscription().unwrap());
1061    }
1062
1063    #[test]
1064    fn p2a_input_detection() {
1065        // mainnet d7da3b5ae755793ff486896e82553d841043df393f09593ec38b807dd2959c45
1066        let rawtx = hex::decode("020000000113c59a9590b576fbe82677888d9cea0639ffe077c6b42fb3599d3ad37cc6ef730000000000ffffffff010000000000000000056a032e2e2e00000000").unwrap();
1067        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1068        let in0 = &tx.input[0];
1069        assert!(in0.is_p2a());
1070        assert_eq!(in0.get_type().unwrap(), InputType::P2a);
1071        assert!(!InputInfo::new(in0).unwrap().is_spending_taproot());
1072        assert!(InputInfo::new(in0).unwrap().is_spending_segwit());
1073        assert!(!in0.reveals_inscription().unwrap());
1074    }
1075
1076    #[test]
1077    fn coinbase_witness_input_detection() {
1078        // mainnet d229b92da09f3abac4f22ceee35e0e55fc43375a62cc575f445431340d9e1732 coinbase @ 668692
1079        let rawtx = hex::decode("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5b0314340a41d806348bf412e341d806348bafbbed2f45324d2026204254432e544f502ffabe6d6d2c7a0f4d5f67376109bb4e6e78904381170ee6ee0715344da30602c49d869cf68000000000000000e60086988fb8000000000000ffffffff023e740b2c000000001976a9140b904a4a8590d0ccff680bb8adc4ae4fe49f890a88ac0000000000000000266a24aa21a9ed165dbbf9c556629ce3b8f2e748f7ca8372131ca6cf47e95736efd8f73dcea77d0120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
1080        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1081        let in0 = &tx.input[0];
1082        assert!(in0.is_coinbase_witness());
1083        assert_eq!(in0.get_type().unwrap(), InputType::CoinbaseWitness);
1084    }
1085
1086    #[test]
1087    fn non_der_sig_p2pkh_input_detection() {
1088        // mainnet fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d
1089        let rawtx = hex::decode("01000000012316aac445c13ff31af5f3d1e2cebcada83e54ba10d15e01f49ec28bddc285aa000000008e4b3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db5014104d0fe07ff74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9ce769d43e94bd58c5c7e331a188922b3fe9ca1f5affffffff01c0c62d00000000001976a9147a2a3b481ca80c4ba7939c54d9278e50189d94f988ac00000000").unwrap();
1090        let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
1091        let in0 = &tx.input[0];
1092        assert!(in0.is_p2pkh(false).unwrap());
1093        assert_eq!(in0.get_type().unwrap(), InputType::P2pkhLaxDer);
1094    }
1095}