rawtx_rs/
tx.rs

1//! Information about Bitcoin transactions.
2
3use crate::{input, output};
4use bitcoin::blockdata::locktime::absolute::LockTime;
5use bitcoin::hash_types::Txid;
6use bitcoin::hashes::Hash;
7use bitcoin::script;
8use bitcoin::{Amount, Transaction, TxIn, TxOut};
9use input::{InputInfo, InputSigops};
10use output::{OutputInfo, OutputSigops};
11use std::collections::HashMap;
12use std::{error, fmt};
13
14#[cfg(feature = "counterparty")]
15use crate::input::ScriptHashInput;
16#[cfg(feature = "counterparty")]
17use crate::output::OutputTypeDetection;
18#[cfg(feature = "counterparty")]
19use crate::script::{instructions_as_vec, Multisig};
20#[cfg(feature = "counterparty")]
21use bitcoin::blockdata::script::Instruction;
22#[cfg(feature = "counterparty")]
23use rc4::{consts::U32, Key, KeyInit, Rc4, StreamCipher};
24
25#[derive(Clone, Debug)]
26pub enum TxInfoError {
27    Input(input::InputError),
28    Output(output::OutputError),
29    SigOps(script::Error),
30}
31
32impl fmt::Display for TxInfoError {
33    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
34        match self {
35            TxInfoError::Input(e) => write!(f, "Transaction input error: {}", e),
36            TxInfoError::Output(e) => write!(f, "Transaction output error: {}", e),
37            TxInfoError::SigOps(e) => write!(f, "Transaction sigops error: {}", e),
38        }
39    }
40}
41
42impl error::Error for TxInfoError {
43    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
44        match *self {
45            TxInfoError::Input(ref e) => Some(e),
46            TxInfoError::Output(ref e) => Some(e),
47            TxInfoError::SigOps(ref e) => Some(e),
48        }
49    }
50}
51
52impl From<input::InputError> for TxInfoError {
53    fn from(e: input::InputError) -> Self {
54        TxInfoError::Input(e)
55    }
56}
57
58impl From<output::OutputError> for TxInfoError {
59    fn from(e: output::OutputError) -> Self {
60        TxInfoError::Output(e)
61    }
62}
63
64#[derive(Debug)]
65pub struct TxInfo {
66    pub txid: Txid,
67    pub version: i32,
68    pub vsize: u64,
69    pub weight: u64,
70    /// Information about the transactions absolute time-lock.
71    pub locktime: LockTime,
72    /// Information about the transaction inputs.
73    pub input_infos: Vec<InputInfo>,
74    /// Information about the transaction outputs.
75    pub output_infos: Vec<OutputInfo>,
76    // is_coinbase struct field is not yet used.
77    #[allow(dead_code)]
78    is_coinbase: bool,
79    is_bip69_compliant: bool,
80}
81
82impl TxInfo {
83    /// Creates an new [TxInfo] from a [Transaction].
84    /// Can return a TxInfoError if the transaction and it's scripts can't be parsed.
85    pub fn new(tx: &Transaction) -> Result<TxInfo, TxInfoError> {
86        let mut input_infos = Vec::with_capacity(tx.input.len());
87        for input in tx.input.iter() {
88            input_infos.push(InputInfo::new(input)?);
89        }
90
91        let mut output_infos = Vec::with_capacity(tx.output.len());
92        for output in tx.output.iter() {
93            output_infos.push(OutputInfo::new(output)?);
94        }
95
96        Ok(TxInfo {
97            txid: tx.compute_txid(),
98            version: tx.version.0,
99            vsize: tx.vsize() as u64,
100            weight: tx.weight().to_wu(),
101            is_coinbase: tx.is_coinbase(),
102            is_bip69_compliant: is_bip69_compliant(&tx.input, &tx.output),
103            locktime: tx.lock_time,
104            input_infos,
105            output_infos,
106        })
107    }
108
109    /// Number of non-OP_RETURN outputs minus one change output if there is more than one
110    /// non-OP_RETURN output. This should approximate the number of real-world "payments"
111    /// happening in this transaction. However, we don't actually know if a certain transaction
112    /// really made X payments. It's more a guess-timate and helpful when looking at many
113    /// transactions and blocks over time to observe changes in a payment trends.
114    pub fn payments(&self) -> u32 {
115        let non_opreturn_outputs = self
116            .output_infos
117            .iter()
118            .filter(|o| !o.is_opreturn())
119            .count();
120        match non_opreturn_outputs {
121            0 => 0,
122            1 => 1u32,
123            _ => (non_opreturn_outputs - 1) as u32,
124        }
125    }
126
127    /// Returns true if the transaction signals explicit RBF replicability by
128    /// having all sequences of the inputs set to a value lower than 0xFFFF_FFFE.
129    pub fn is_signaling_explicit_rbf_replicability(&self) -> bool {
130        self.input_infos.iter().all(|i| i.sequence.is_rbf())
131    }
132
133    /// Returns true if at least one input spends either nested or native SegWit.
134    pub fn is_spending_segwit(&self) -> bool {
135        self.input_infos.iter().any(|i| i.is_spending_segwit())
136    }
137
138    /// Returns true if at least one input spends a Taproot output.
139    pub fn is_spending_taproot(&self) -> bool {
140        self.input_infos.iter().any(|i| i.is_spending_taproot())
141    }
142
143    /// Returns true if at least one input spends nested SegWit.
144    pub fn is_spending_nested_segwit(&self) -> bool {
145        self.input_infos
146            .iter()
147            .any(|i| i.is_spending_nested_segwit())
148    }
149
150    /// Returns true if at least one input spends native SegWit.
151    pub fn is_spending_native_segwit(&self) -> bool {
152        self.input_infos
153            .iter()
154            .any(|i| i.is_spending_native_segwit())
155    }
156
157    /// Returns true if all inputs spend SegWit outputs.
158    pub fn is_only_spending_segwit(&self) -> bool {
159        self.input_infos.iter().all(|i| i.is_spending_segwit())
160    }
161
162    /// Returns true if all inputs spend legacy outputs.
163    pub fn is_only_spending_legacy(&self) -> bool {
164        self.input_infos.iter().all(|i| i.is_spending_legacy())
165    }
166
167    /// Returns true if all inputs spend taproot outputs.
168    pub fn is_only_spending_taproot(&self) -> bool {
169        self.input_infos.iter().all(|i| i.is_spending_taproot())
170    }
171
172    /// Returns true if the inputs spend legacy and SegWit outputs.
173    pub fn is_spending_segwit_and_legacy(&self) -> bool {
174        let mut legacy = false;
175        let mut segwit = false;
176        for i in self.input_infos.iter() {
177            legacy |= i.is_spending_legacy();
178            segwit |= i.is_spending_segwit();
179            if legacy && segwit {
180                return true;
181            }
182        }
183        legacy && segwit
184    }
185
186    /// Returns true if all inputs spend nested SegWit.
187    pub fn is_only_spending_nested_segwit(&self) -> bool {
188        self.input_infos
189            .iter()
190            .all(|i| i.is_spending_nested_segwit())
191    }
192
193    /// Returns true if all inputs spend native SegWit.
194    pub fn is_only_spending_native_segwit(&self) -> bool {
195        self.input_infos
196            .iter()
197            .all(|i| i.is_spending_native_segwit())
198    }
199
200    /// Returns true if at least one input spends native SegWit.
201    pub fn is_spending_multisig(&self) -> bool {
202        self.input_infos.iter().any(|i| i.is_spending_multisig())
203    }
204
205    /// Returns true if at the inputs and outputs are sorted according to BIP-69.
206    pub fn is_bip69_compliant(&self) -> bool {
207        self.is_bip69_compliant
208    }
209
210    /// Returns true if at least one output is an OP_RETURN output.
211    pub fn has_opreturn_output(&self) -> bool {
212        self.output_infos.iter().any(|o| o.is_opreturn())
213    }
214
215    /// Returns true if the transaction could be a equal-output-value coinjoin.
216    /// Coinjoins require at least two inputs and two equal-value-outputs.
217    /// Furthermore, we check if at least one third of the outputs has the same
218    /// output-value.
219    pub fn potentially_coinjoin(&self) -> bool {
220        if !(self.input_infos.len() < 2 || self.output_infos.len() < 2) {
221            let mut a: HashMap<Amount, usize> = HashMap::new();
222            for amount in self.output_infos.iter().map(|o| o.value) {
223                if let Some(count) = a.clone().get(&amount) {
224                    a.insert(amount, *count + 1);
225                } else {
226                    a.insert(amount, 1);
227                }
228            }
229
230            // a third of the outputs must have an equal-output-value
231            let max_count = *a.values().max().unwrap();
232            if max_count >= self.output_infos.len() / 3 && max_count > 2 {
233                return true;
234            }
235        }
236        false
237    }
238
239    pub fn potentially_consolidation(&self) -> bool {
240        // Consolidations here are defined to have at least 10 inputs and up to two outputs.
241        self.input_infos.len() >= 10 && self.output_infos.len() <= 2
242    }
243
244    /// Returns true if the transaction has a non-opreturn output with a value smaller than `value`.
245    pub fn has_non_opretrun_output_smaller_than(&self, value: Amount) -> bool {
246        self.output_infos
247            .iter()
248            .any(|o| o.value < value && !o.is_opreturn())
249    }
250
251    /// Returns true if the transaction has an output with a value larger than `value`.
252    pub fn has_output_larger_than(&self, value: Amount) -> bool {
253        self.output_infos.iter().any(|o| o.value > value)
254    }
255
256    /// Returns the sum of all output values
257    pub fn output_value_sum(&self) -> Amount {
258        self.output_infos
259            .iter()
260            .fold(Amount::from_sat(0), |acc, o| acc + o.value)
261    }
262}
263pub trait TransactionSigops {
264    fn sigops(&self) -> Result<usize, TxInfoError>;
265}
266
267impl TransactionSigops for Transaction {
268    fn sigops(&self) -> Result<usize, TxInfoError> {
269        let mut sigops: usize = 0;
270        for input in self.input.iter() {
271            sigops += input.sigops()?
272        }
273        for output in self.output.iter() {
274            sigops += output.sigops()
275        }
276        Ok(sigops)
277    }
278}
279
280fn is_bip69_compliant(inputs: &[TxIn], outputs: &[TxOut]) -> bool {
281    let inputs_sorted = if inputs.len() == 1 {
282        true
283    } else {
284        let mut to_be_sorted_inputs = inputs.to_vec();
285        to_be_sorted_inputs.sort_by(|a, b| {
286            let mut a_outpoint_txid_reversed = a.previous_output.txid.to_byte_array();
287            a_outpoint_txid_reversed.reverse();
288            let mut b_outpoint_txid_reversed = b.previous_output.txid.to_byte_array();
289            b_outpoint_txid_reversed.reverse();
290
291            a_outpoint_txid_reversed
292                .cmp(&b_outpoint_txid_reversed)
293                .then_with(|| a.previous_output.vout.cmp(&b.previous_output.vout))
294        });
295
296        inputs.to_vec() == to_be_sorted_inputs
297    };
298
299    let outputs_sorted = if outputs.len() == 1 {
300        true
301    } else {
302        let mut to_be_sorted_outputs = outputs.to_vec();
303        to_be_sorted_outputs.sort_by(|a, b| {
304            a.value
305                .cmp(&b.value)
306                .then_with(|| a.script_pubkey.cmp(&b.script_pubkey))
307        });
308        outputs.to_vec() == to_be_sorted_outputs
309    };
310    inputs_sorted && outputs_sorted
311}
312
313#[cfg(feature = "counterparty")]
314/// Returns true if the transaction is an OP_RETURN CounterParty transaction.
315pub fn is_opreturn_counterparty(tx: &Transaction) -> bool {
316    if tx.is_coinbase() {
317        return false;
318    }
319
320    // find OP_RETURN output
321    for output in tx.output.iter() {
322        if output.script_pubkey.is_op_return() {
323            // check if OP_RETRUN message is long enough
324            if output.script_pubkey.len() > 1 + 1 + 8 {
325                // OP_RETURN + length + CNTRPRTY prefix
326                let first_input = match tx.input.first() {
327                    Some(input) => input,
328                    None => return false,
329                };
330
331                // CounterParty uses the human readable hex (block-explorer) representation
332                // of the txid for encryption
333                let mut first_outpoint_txid = first_input.previous_output.txid.to_byte_array();
334                first_outpoint_txid.reverse();
335                let key = Key::<U32>::from_slice(&first_outpoint_txid);
336
337                // expected: OP_RETURN PUSH_DATA <payload>
338                // drop the OP_RETURN and PUSH_DATA here
339                let mut payload = output.script_pubkey.clone().as_bytes()[2..].to_vec();
340
341                // decrypt the payload with the txid as key
342                let mut rc4 = Rc4::new(key);
343                rc4.apply_keystream(&mut payload);
344                return payload.starts_with(&[0x43, 0x4e, 0x54, 0x52, 0x50, 0x52, 0x54, 0x59]);
345            }
346        }
347    }
348    false
349}
350
351#[cfg(feature = "counterparty")]
352/// Returns true if the transaction is a multisig (P2MS) CounterParty transaction.
353pub fn is_p2ms_counterparty(tx: &Transaction) -> bool {
354    if tx.is_coinbase() {
355        return false;
356    }
357
358    // find P2MS output
359    for output in tx.output.iter() {
360        if output.is_p2ms() {
361            if let Ok(Some(n_of_m)) = output.script_pubkey.get_opcheckmultisig_n_m() {
362                // for CounterParty, n == 1 and m == 3
363                if n_of_m.0 == 1 && n_of_m.1 == 3 {
364                    if let Ok(instructions) = instructions_as_vec(&output.script_pubkey) {
365                        // expected: OP_PUSHNUM_1 PUSH(<pk1>) PUSH(<pk2>) PUSH(<pk3>) OP_PUSHNUM_3 OP_CHECKMULTISIG
366                        if instructions.len() != 6 {
367                            return false;
368                        }
369
370                        let first_pubkey = match instructions[1] {
371                            Instruction::PushBytes(x) => x,
372                            Instruction::Op(_) => return false,
373                        };
374
375                        let first_input = match tx.input.first() {
376                            Some(input) => input,
377                            None => return false,
378                        };
379
380                        // CounterParty uses the human readable hex (block-explorer) representation
381                        // of the txid for encryption
382                        let mut first_outpoint_txid =
383                            first_input.previous_output.txid.to_byte_array();
384                        first_outpoint_txid.reverse();
385                        let key = Key::<U32>::from_slice(&first_outpoint_txid);
386
387                        // expected: PUBKEYMARKER (02 or 03) and payload
388                        // drop the PUBKEYMARKER
389                        let mut payload: Vec<u8> = first_pubkey.as_bytes()[1..].to_vec();
390
391                        // decrypt the payload with the txid as key
392                        let mut rc4 = Rc4::new(key);
393                        rc4.apply_keystream(&mut payload);
394
395                        // expected: PAYLOAD_LENGTH + PAYLOAD
396                        // drop PAYLOAD_LENGTH
397                        return payload[1..]
398                            .starts_with(&[0x43, 0x4e, 0x54, 0x52, 0x50, 0x52, 0x54, 0x59]);
399                    } else {
400                        return false;
401                    }
402                }
403            }
404        }
405    }
406    false
407}
408
409#[cfg(feature = "counterparty")]
410/// Returns true if the transaction is a P2SH CounterParty transaction.
411pub fn is_p2sh_counterparty(tx: &Transaction) -> bool {
412    if tx.is_coinbase() {
413        return false;
414    }
415
416    // find P2SH input
417    for input in tx.input.iter() {
418        let redeem_script = match input.redeem_script() {
419            Ok(script) => match script {
420                Some(script) => script,
421                None => continue,
422            },
423            Err(_) => continue,
424        };
425
426        if let Ok(instructions) = instructions_as_vec(&redeem_script) {
427            if instructions.len() < 8 {
428                continue;
429            }
430            let payload = match instructions[0] {
431                Instruction::PushBytes(x) => x.as_bytes(),
432                Instruction::Op(_) => continue,
433            };
434            return payload.starts_with(&[0x43, 0x4e, 0x54, 0x52, 0x50, 0x52, 0x54, 0x59]);
435        }
436    }
437    false
438}
439
440#[cfg(test)]
441mod tests {
442    use super::TransactionSigops;
443    use super::TxInfo;
444    use bitcoin::Transaction;
445
446    #[cfg(feature = "counterparty")]
447    use crate::tx::is_opreturn_counterparty;
448    #[cfg(feature = "counterparty")]
449    use crate::tx::is_p2ms_counterparty;
450    #[cfg(feature = "counterparty")]
451    use crate::tx::is_p2sh_counterparty;
452
453    #[test]
454    fn short_input_script() {
455        // mainnet 0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae
456        let raw_tx = hex::decode("0100000003d4dfa41ffe9825af0aad023f084b4dd4a599d6cb8d083e58e3a53e8ad682a6ae010000000401030103ffffffffe2274e1294e1708f344e7cd5156648750bc60d3bf3705130cdcba66675e3439b010000000401030103fffffffff41d101368d124c5b10bf130bceece07623d87767a2c950a35d1b7b6217a2329000000000401030103ffffffff0170032d00000000001976a91429c9743283afd76eed5811788b20b23f9eece00788ac00000000").unwrap();
457        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
458        TxInfo::new(&tx).unwrap();
459    }
460
461    #[test]
462    fn bip69_compliance_1in_1out() {
463        // mainnet 3d896cc02c6be867c1619bfcce8c96284f2fd17b34f070ab4b307b56edcf1a12
464        let raw_tx = hex::decode("02000000000101f795ab70b2c98c1b97fdbd1e98a238bdbc4336362099ea2d07fd7b5db7c48aa72500000000ffffffff01236500000000000017a914f73d4190dba76f89573d8ecca6cd49c7ca9e852b870247304402202f95f204b7663a61f8d8a8b6691562d23f6774b3ed64b3d993233fa1ed6c6c98022005723fe5c98182aca858dfab50ea4e68103ddfd83ca10480c825169897fff42901210201fe8351e1908fc82cb314ca4a532d7a9d81726d9fd60df0487ff7dd4235c4e300000000").unwrap();
465        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
466        assert!(TxInfo::new(&tx).unwrap().is_bip69_compliant());
467    }
468
469    #[test]
470    fn bip69_compliance_3in_4out_compliant() {
471        // mainnet 95ce62ff4b64ea9358edb02d695559cb90461ec6f695ff5f207005e52cd5ba92
472        let raw_tx = hex::decode("0200000003947511e452abbcfdb0cb56c05acfef41c22f68d2f54415b25cff7672a718a91e000000006a4730440220620a98a40e18e799f8adba3204acc776ae8fb6a9861ab739e346a26e3067e29202207bad9bbcf4278c48dac23061e3bb97821712f4ea0b8d42a8367fbb1f79018cf2012103241af1d91902d8ff26ccec89ff33664bb328b3f980f8e98e67187e931d8c4565fdffffffd75a3121036692055c44782dd6908bb346997659561a923bc96bc70b54f230ae000000006a47304402203389c2c3b51bdcb7c7c2bad02bb4ca78866ce202bc0bedf693d15b872a840f3f022029311b399c9f583cb746045aba2343e31bda147c88334bb93f0b8b9526aba0ed012103583031de58a37d8c0d5840b9db5d823f2aac071f9347c56824f1b926cff559fdfdffffffb0840efafc1fb1595c684225e10cb59e1440ee8698fad3be3399d97ae78cc4e6030000006a47304402202e46e5d1b5230225deed4fc395c05a1c163602e030feaa135de2e2498fb3979902200cb4c72782d1cc6167cb97a670a1cdb6bfb361bf104350d95b6cadb3c85d39d401210242191a9a0b2398032fba0b9503846555a07feebcb5822d60e50fb565dceeebfafdffffff040c2e0000000000001976a914c5a0ae47fec174bb1788cb2f379bfc49f59f751088ac3f430000000000001976a91425434bd302f418914bd8643cc6d814a27fb97a2288ac73660000000000001976a914434d62974d0263bbbd5accd4bf3222987059101888ac1d2c0e000000000017a914345eaf1ba7d68077f411ae3222ea840ebcb3f81487785c0a00").unwrap();
473        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
474        assert!(TxInfo::new(&tx).unwrap().is_bip69_compliant());
475    }
476
477    #[test]
478    fn bip69_compliance_3in_3out_not_compliant() {
479        // mainnet 8e8ab032d6df899610c3eb5d7d14d09eaf0e54e5da15dfd94c1472984a5a7087
480        let raw_tx = hex::decode("02000000000103edfe97964f954656db279834f3fb810cf2868c26f1fe5fd2fd3bb6d293da80500100000017160014517f3e10051df116600af7f322ebad1eb0834676ffffffff6a5d444fe88ef03df7d8062c429850286c434cb9ee77947b6e73dc34e200ef040000000017160014c2af9fdf9f2e8dbe406163e1b3c22c4a98c22bccffffffff1df2a57be954c93f002d4d272616ebbe7952ec100fd77e8c9118a4df88ae694a0000000017160014fc89a4451ba56e5511aac20a634206391f267120ffffffff03a13f05000000000017a914acb331118b1b1d233dc5ee3972d8b0eb10a389648748d104000000000017a914e416571b39ca465e70ca21c4401925a2d53c9cd687823b9e000000000017a9144ff5174680c12f362699f4186e822e64c0f741ca8702473044022017f06d1a4baf4783566f5a751c3f5239d896728e26a34b484d5b804a83571b31022038e76fa397eae5644651a0c7c1fa8b4533f4df061d315ac4794669958e7afb3e0121032adeb16dc182591e03bbd57f68bbee23d1d2b95315103008ee7b2afc8151d8a00247304402200fadb196510acc654ddbd3b3be87051e0db8bf58a003f07628be44a666a2bc2102200e8010a7abdf6c635f5a3ec7ab06c2d4e7ce520e99a617724349025b588b52630121027c9b7d4b9789bdc5fdd4e170d593619bd185b376d53eaca3463f3925ca8047af024730440220486e51ace259a49ff14fd069b3c2ae4890da290651a9f364dbe352b67aee10d402203dc2f37182ed5bae92ebed04d531d7e34c6e1dc17e8ecaa60f83b054f967827401210295dec103680805c7934cde983acdbb3224119bfa770814808f98c651655d764a00000000").unwrap();
481        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
482        assert!(!TxInfo::new(&tx).unwrap().is_bip69_compliant());
483    }
484
485    #[test]
486    fn tx_payments() {
487        let testcases = vec![
488            // mainnet df4a45b6fea86b144a95c80aa9cef12319a34f592b82398e3779056ed08c1f7d
489            // A two input and four output transaction
490            (3, "02000000000102069ed58b5561484f7e4a7a7c350b185299bbdba802456b0d80e6ec1cbdbbbc9a0200000000ffffffffd58368763ad102d594859403cf7cf71dc92782f6fb607fb8aafda0b525c70d0c0000000000ffffffff04a644010000000000160014990c5256fd8581a2123131491cf757352fc92f79b0ad0100000000001976a9143842229d25f208e1354595087da59977a7de340a88aceeca090000000000160014f71cd3db25d346ead787b40a0555893bb06861ccf7b2050000000000160014e0145df166b20db925a3f7492889a5eb5ff6d363024730440220422332ad8e7606092423ced37f2b093058081fadeee6194ac8da52d6ac179eff022053c3751f14be917abf67e30035a017eec96453b4ba00525ceb124de742a804540121029c73c9de8db31bd1e6c27b8e0f83f7caf385f4d83b37912b8aa8ab23e047d9cb0247304402200fea5229da4aee2fc111c680d3ecd9b9bb9bd412d74cbc6967c9c4143ee4a06b02205fac85cb479f889f9f7c30965c59ba93c8ef72ae97bf39cfcd14535b6a2abf0501210306cd8c46641987aec3a0d36876135ad3fb27c3acba2dba752d319f029d5fe1f800000000"),
491            // mainnet 55248b67e86f44db36b86a32c51c90163a62a160ed569530f54b1e52a1b73094
492            // A one input and one output transaction
493            (1, "020000000001017d5edb2c1483064b20e02e9506b45811aa1782be487b258df0059771e354b4db0000000000fdffffff012a634702000000001600141d7310914c7190bfa5d5f989c17f58a7ec870edb024830450221008640353600ec553a06cf047193076f6fb344150776d21c0afdd37393bb6387cd022060a019011c1991437e1f81030ebd1856ae986b0791487dd65c5f953d44206be80121028a96fb1aae84d63edcbb8d31c0878fda4d11ccf4b05d13e863ca0f3d2c24fa7d00000000"),
494            // mainnet 1226fa65993778032d9b940211980da6596f894cbd4f9e8c6d68ae458d9d866a
495            // A one input and two output transaction
496            (1, "01000000000101af734457ce3599296126306b4be48931336d7074b26079b02bea5caa612e3b760100000000ffffffff0210980200000000001600147e79fdadeff8b0b5d1f455377611df393810a252407a2b00000000001600143776a8dd42704d357c0a1f4531dc1ee83ecce0910248304502210087d244b808e0205ce1acc77d15d46a53fb9574103e74821e6fc532dfb58cf2180220459c4ad9729486dcaedacfc483488427e7edfaa220d6332e84dcddf5e9e6e861012102c9ed734050521fe27747d6a17cf919f34b707686db6ce50d0a96c990bd754ffa00000000"),
497            // mainnet 1d1eebc1f4d25ba217aafa050e6db487edeb1e11b476539a31e8502b1d2f2a08
498            // A one input and three output transaction where one output is an OP_RETURN output
499            (1, "0200000000010196c86fc2817bd20ce957e9ceb9ac7b908e44d56f3c16301e33c9f8ea7d2732280200000000ffffffff032202000000000000225120f16b4c81b96cf6baad1ba955156ca947d76df997d0829b26eef8c764f8ecbd600000000000000000116a5d0eff7f818cec82d08bc0a88281d215764a00000000000016001431a845e68288a5046cb02dc509bd982a4ea94f2d024730440220190f3124ff5bd0df7fc9b5568016fcfc646251ee428b3565c5b14a70f6bd13f6022067627da4d26a07679d74b6774435ab01515832e3596b4c798aee889be65abf7d012102e3f72c926b604e2591441412d7eee863aa2308bf0076b6e6efbe55705ac476d000000000"),
500            // mainnet 6f660e9f1bbfcc8435593eb8ff70a501275ad6cdbaa536747bd9d55bdbeda65a (Libre; Relay)
501            // A one input and three output transaction where two outputs are an OP_RETURN output
502            // from https://b10c.me/observations/09-non-standard-transactions/#non-mandatory-script-verify-flag
503            (1, "0200000000010196c86fc2817bd20ce957e9ceb9ac7b908e44d56f3c16301e33c9f8ea7d2732280200000000ffffffff032202000000000000225120f16b4c81b96cf6baad1ba955156ca947d76df997d0829b26eef8c764f8ecbd600000000000000000116a5d0eff7f818cec82d08bc0a88281d215764a00000000000016001431a845e68288a5046cb02dc509bd982a4ea94f2d024730440220190f3124ff5bd0df7fc9b5568016fcfc646251ee428b3565c5b14a70f6bd13f6022067627da4d26a07679d74b6774435ab01515832e3596b4c798aee889be65abf7d012102e3f72c926b604e2591441412d7eee863aa2308bf0076b6e6efbe55705ac476d000000000"),
504            // mainnet cb22f12d777e95e426fd31840171af453c600390276d29d307ef34dbaeda4387 (never; gonna; give; you; up)
505            // A one input and six output transaction where five outputs are an OP_RETURN output
506            // from https://b10c.me/observations/09-non-standard-transactions/#non-mandatory-script-verify-flag
507            (1, "0200000000010179bf07f0c130de21cbaa2daccf1fd9052eefb89f37a7f0fd7cb0c598f7bf8bee0100000000fdffffff060000000000000000076a056e657665720000000000000000076a05676f6e6e610000000000000000066a04676976650000000000000000056a03796f750000000000000000046a027570546901000000000016001472a0c5560127b92dc8649862f38094b28ff3b2ef0247304402200f0c6d3b1f661b7d76cc9c0566c1c6159e288d671b327cdc7989c7b853e67adc022079819f87c5c51e70b4e2b56dfe6868bc742c9486071754bde1936034f4e1b755012102fb7d5362c637767ffadf734fa279070f0d4a0d0caae1784193c6fe592f20ac8700000000"),
508        ];
509        for c in testcases.iter() {
510            let rawtx = hex::decode(&c.1).unwrap();
511            let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
512            assert_eq!(TxInfo::new(&tx).unwrap().payments(), c.0);
513        }
514    }
515
516    #[test]
517    fn test_transaction_sigops() {
518        let tx_sigops_pairs = [
519            (6usize, "010000000001015b26c9550875279971632eafde56bd76d3c25fc544c250de8819c53cf30466c30000000000feffffff11b7640000000000001600143a7f1bba431ca8c37d4cd1cde573ca95b9ce33e57ccf000000000000160014c20781c62f767c44149cbc2faa131c24d1b596b68513010000000000160014056b0b030aa5bb5736273a3037c2e1b22fc951d08a3a01000000000016001474403ba47b091c6a72488b19b2acc207aee681641142010000000000160014d89710afe6e62b45284e50aab6697a13f754b6945c470100000000001600149118018676be9cde9d01e4e5cf9696649d01b5390768010000000000160014d49466813dbe618230ddfe033f991de4b1de9ddc72770100000000001976a91496070daa16d6226220a27632826eb262212b45f088ac8fcf010000000000160014df52cb8f365679fe7650ac89c18f39094a8289defbd30100000000001600148c6991d57e341187e42f3cde5635394eb88cef5ca2da01000000000016001496941f95afd6149ab45c5cc109fa6c7a184f5bc804ea010000000000160014c0fbd69ede930029485e0ef5f0fe90f9450d4cab020b02000000000016001466de96b53d55749d55e544b4c5c8788486e0e0ac14a8020000000000160014601c6732e6a22c3a4d87e97e18f205770131cf9249040300000000001600146ce2d6dac9c8aa6862ecc045b9b5114bcbfe0dcc1927030000000000160014f66758a0748c715d167b9c45bfc23af35587d478754503000000000016001418b3e52251c640a241de407b960ceeb6bc696722040047304402205194a738a89b8d5d38a9da65953effdabae7c2640482b89d44ffecc67b4b1f36022008e3ff0af2dd4b734288ab9e1c00f63e7fc654fe7664d885a91ba1f97d3d183b0147304402202a9bcb6d492583f7045cfffa375a772247ed50e7144c8acb58c658d6740646af02202e9bd9e63712d70b0d9d57af8ec6e9f51bd66e4fcf131f624684642f09a892e6014752210256abd6a1f3b25e64ce575e06c434ac49a48041c66a0fcc8a5f15a08c7fb2940c21031e14e051793339d04552004e0c6417c70d05aae1948962bed2188616623516ba52ae6ef90b00"),
520            (29usize, "02000000000101cbdf7b986004a715fdc26fa43c539535a2ace9920d2f73721dc86270ed7086a40300000000fdffffff09134a1600000000001976a91424f7e9c66ba2a9e92aad42228dd6d5fd72c6258588ac4a8e0300000000001976a914b9676ad121e0b2c0a186f159d6942b4cc764699588acbb7c1e00000000001976a914b89b10ecd0c130f041ce2dff823b6518431c736c88ac4f111200000000001600147f3abf331ec3d7710a945faf498a68438bb4a1d1960a1b00000000001976a914bfc9663e29e246ae180eb176f1b5587ca6c287d788acf3f20900000000001976a914630302f6d3a63d9e4977f95f39d9a5edd2628da388ac40393200000000001976a914ab05fb0bcd16e6c1ca8f8d0b200b6a643b9d858788ac97741400000000001976a9148031b161c8441d11accb3933cc35aee5e7c7559488ac62f7af0f0000000016001424e5e542b61d260e7b46f29dc887f83b231f3d1d02473044022007b6e8f3391d6222baf53695659460be8f93996e9aadb7b21b807aeaa64f413802201e17be7fd7a4d05a2fd167a16e141151d7747d7bfb9b52d9abbf64c13ee8b794012103b4118dad6f16a2e60542ebc883583277172de605ccbba23dbb5899e401bc69638bf90b00"),
521            (9usize, "02000000000101369e3987ed900be5360982501c59ec8f54763fff6a52e75ab00c66d78bddbec80100000000fdffffff0320300500000000001976a914b59c3a811b3480befcffd31253374bb27fabd77388ac6f0a0900000000001976a914573c227fde2d5dadc3aa33ce78f499e2ceb358e088ac1d0a594300000000160014f60834ef165253c571b11ce9fa74e46692fc5ec10247304402204e666de27d7117902b15d072e861c480cd06ac2b1bddd61349ad6f08db607dd202205f9f9ca6e56d57f03c88c9cfc24e4e4b2e655d9b365dca8aba29b0477f268bfd0121026e5628506ecd33242e5ceb5fdafe4d3066b5c0f159b3c05a621ef65f177ea28600000000"),
522            (86usize, "01000000000102d962edaf1463fcbd3f7e5262e9013f1a665d18bb0593f02d9957d0123c67af294c00000000fffffffffe115c6ad5dd7622364198ab7ddbcb3a241d2c1595a4a0820e34fb60a14b8efa01000000232200204dabe51a312c54ff621373fd1bdd99c0ecc433fd78d4d8b5d4a1ec09c3b46ef3ffffffff353f960000000000001600145f8cff66afae811b8694c608caaaf0f0b035898fc057010000000000160014d583589a104a29c222e4f56b48ed494ee14e068c888a0100000000001976a914c19f638d64aecd593fcc695fcf19c0a1ec663c2588ac708e0100000000001976a9140133799122cb3697f93d74ebe448917afb805a9188acb0ad01000000000017a9142531c305d85ebed7d895ec0240536b20d228a5258748e80100000000001976a91434a741db089157ed4740cc8451a353433287913c88ac785d0200000000001600148b3c9eaabb004ba90954599e41c62e4b6aa3e333e8740200000000001600146736875ed53b2df91c6f7f6fe1a581cf9597929ad07802000000000016001490d370478e087bd2bb037b9b90b7ed1c478cabd0588c0200000000001600147a679175f97ce1964710c41f9fe085bb5251bc85409002000000000016001446fbb01007d57e6eaa30beab4e5b994035d1f311409002000000000016001488b9d2ba8f0263f5885ea8abdbf6a876c3ae3c24d8ca02000000000016001420cc1a5ceee8a1d4833dc3e80dc1126e5bc197bcd8ca020000000000160014b0489b275b03f20ed5282e0789af4f9c2d287c5c58090300000000001600140b7411a6646820a63394d37571ee17c193f9eadf400d03000000000017a914aef8ebc7394f746221d2db093c58ca4938f7ff3687101503000000000016001497f1b15ba312f2cf5b93d436ee9a1bb8fef4b699905303000000000017a91487757770c42cb5a1031474264a13258b1a0637a387c89d0300000000001976a9148ee571b7594b5673cccb8a2eef6e36bb27ba0a8688acc8970400000000001976a91495f4252e7a20bb06ec249694f4769f483ec385f788ac60d20400000000001976a914601ef774603d99bf413553a10b56a89c489dda6d88ac18de040000000000160014abf0e0755f36cf1026ef3dcb701e11a0bebb1a72e8e504000000000017a914d510c93e87e566e7e53b3a3ef25892897495f4678740010500000000001976a914b0736cafbf6a19048b414101e9c207eee170840888ac27450500000000001976a914c859652b6644181080123b6d6beeb01353eed4b588ac28ff0500000000001976a914bcc18a564b461a64c9513d5b8531d56ec828449588ace85c0600000000001976a914cb0f853abd9e8929129748b88a4a4c4ce10954bd88accb200800000000001600148e54c8d731d9f91f09dac6add1b22ef9daa643ba717e0a00000000001976a91429dac391762253b12b728ad4a192b3bb136f8e7188acd89a0a0000000000160014d9bbd1690ff0f34a66db5ff6b1e29693ec1947f300be0a00000000001976a9148c070b57613a81d734a9fd30675460f9ab6fe59588ac3cb60b00000000001600143a3297dbed6ea17e4cb3a762807e9054b0a4aa1ae8bb0b00000000001976a914496b455d3a29d2cbddc435e9029651955cea63ae88ac20060c000000000017a91473c4c8a65ecd3f75416c3535afd403efeca4f09f876f630c000000000017a9145aea48748e8aff443294696bdc5d1e51e250f5cb8790170d00000000001600141b947b0c659778eae66a87543bab81e395b59441d0360d00000000001600144d2f05456e694e6b33fdd93f0270ee70a31d8ddc20770e00000000001976a9148ec7a5ab3ca5392377ac18160e0cc682f29d4d3288aca0b50e00000000001976a914749cf68d7855cee5333198618bc11e36fc14f2af88aca8840f00000000001600140d2a31b46bc58a28ac57155819cb75f0cad89968a8f51100000000001600143095582bcfeb21a7e3b58340560dce47fa7c0733f83b1200000000001600147e276285bc7437c30480c14bfbb0666e00db2632203b180000000000160014a7f4c809680bfd517626226fa5739089d64c0a56109118000000000017a914d82e45767ce025298667a9e64f6feb6b312afc5b8718dd1900000000001976a91482e85ca18d50eca438fb6f07d38c80cdba2c983688acccb91a00000000001976a914d0d8ea777426c0b34850de0f3c2656d58ea226b688ac90c31a00000000001976a914e6a1f6543f64256574859b69e21086ff83dcc27d88acd041200000000000160014b4afc34958c0a0d33b7c4b43d895b58be6d3fca2e83c3500000000001600144d2f05456e694e6b33fdd93f0270ee70a31d8ddcf0446b00000000001976a914ba6d7f42fc439e3c1db99bda1086f2ea65171f9a88acb8ac7700000000001976a9148b4b2f5f7a508abe657d71199c3121f15d9a6ede88acc0eb83020000000017a9146c18cbd8b8d8db62f3f89bfa7929481cf460390687af86f102000000002200209ffa349e21dc16bf62e51f903947890f080d0becc6c145fef4db4fd6f56935fe0400483045022100b2a28e3877ec59b886f7a7d4ccf13600382520d724333dba67e1aa1fdf9c389f0220189a17c369d26896088f3b3ca59187b44a1a420aba62e5e510a748bd03c5588a01473044022015701affe64eca107942e18cb468338a17512a0a5faf160b2e6576c9acdbae21022001afbdfc8c1d6577103842a4f78b35fb8f3e01795fdd9b22bb3015189fe82c630169522102e0119b324f22f357b4d1736b25f51468e8080e821445662aadf6f268770e25fd210354b7fb16256afbcffcac8d6a63e65e585985a2ca2d3f0813b24ec27628ec1abc2103fe2c82f09defdf1f458b66b05642ab4299c241718d3a7cec3518c8cec188dced53ae0400473044022061080519531e7cd52fc36b163fd85fe466dd3c97e97438608dcf0eaaf67bb45b0220286721037311b638628e8bbeb26430c0dd7038cbe9f5a27df6b75782063ff5490147304402203b173dbb0fa08c3cf41de914de7488e7002bb5eed018e8f5298077289bb955150220780077fecb7d54a41a857419e60d1ea538ce1c487d0202300f7271d1fed1bcaf0169522102db43e4cde7cc070a45b6aa4a46d6d2e0bc615c31dcc27e2b97cd0b8287377d2a2102a8da544e96bf9f2ee56035a20420abe95ab19fe24026a553603fe73113ada43d2103a7d02955bc72bddf5d6c0893489b98cdce698964e94d1657f894eb8732d3f77653ae00000000"),
523            (5, "02000000000101b557ad25033e2e7323d1ea26035bd8d84c0529a8d23c6d243002fb4d277d5ba10100000017160014d28e1be3503020526295715f41b4c9e0d291c5b90100000001c0d40100000000001976a914ea9911ef55b869720c76846bba0babf6f2748db388ac02483045022100c586d4e2ff16a8354fd4fbadcdfc1dc66782c66ba83f7be87e7692e90cc5105e0220759f96d12dce5acd7eb515a312ec9112b8a9eb8b220ef120d5f34ed35465b2e30121030955dbd8b34862058e6edaef080243cdd1b0b907d626990c1f404614d40be18700000000"),
524            // coinbase with two P2PKH outputs (from F2Pool; splitting uncommon ordinals)
525            (8, "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff6403ac9b0c2cfabe6d6d654b01255ebb409c165395395b3f3c2301f032f53df45cba5ed5d266dc2c786010000000f09f909f092f4632506f6f6c2f7300000000000000000000000000000000000000000000000000000000000000000000000500ae970800000000000422020000000000001976a914c6740a12d0a7d556f89782bf5faf0e12cf25a63988ac1ebc4025000000001976a914c825a1ecf2a6830c4401620c3a16f1995057c2ab88ac00000000000000002f6a2d434f524501a21cbd3caa4fe89bccd1d716c92ce4533e4d4733bdb2a04b4ccf74792cc6753c27c5fd5f1d6458bf00000000000000002c6a4c2952534b424c4f434b3acd2e3ba1354794d09aabccd650c2155ae16cd9830cc9b0d57aecd423005ba3a64940a53f"),
526            // failed the input check with EarlyEndOfScript, as we didn't skip the sigops counting for witness coinbase scripts
527            (0, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5803ad9b0c1b4d696e656420627920416e74506f6f6c3936312900e50185de0c73fabe6d6d4045cd649213cd20a5b9fbc8e8d110413043505dd479aa610953d960b83bc22e10000000000000000000ca8d0370000000000000ffffffff05220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade787821dea280000000017a9144b09d828dfc8baaba5d04ee77397e04b1050cc73870000000000000000266a24aa21a9ed7d3074d041ce338acb516547585351a0d058c476cf2b91141fa94db4096d3aea00000000000000002f6a2d434f524501a37cf4faa0758b26dca666f3e36d42fa15cc01065997be5a09d05bb9bac27ec60419d0b373f32b2000000000000000002b6a2952534b424c4f434b3a6b102098d342b6868cb55909ae57bec35e74ded30cc9b0d57aecd423005ba3ab0120000000000000000000000000000000000000000000000000000000000000000000000000"),
528            // failed the output check with EarlyEndOfScript, as we failed on outputs pushing past the end
529            (0, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0363740c0474d64e652f7a7a616d78632f76649b3c094f135bf4b83108c14ea85f1226746ff200b4020000ffffffffffffffff03a1defc2900000000160014b6f3cfc20084e3b9f0d12b0e6f9da8fcbcf5a2d90000000000000000266a24aa21a9ed22e7492ea82d70262d12e74db7b7813d93365a2bf528aa803b191a209272a65f00000000000000002cfabe6d6d14c8eef25658e2e29cf3580d3c0cfe6cf5ece1bfba5949e1b44de824cc5c4be7010000000000000001200000000000000000000000000000000000000000000000000000000000000000a1233f55"),
530            // mainnet 44c857ef596332bbdcec28d2337a3c7eda10c23da0dc1769325b40c9220c4816, non-standard rsk tx
531            (84, "0200000001e5af92d188010aa34910c71de4a55d6f142c0989a62af388b10bd8a33b54e86c00000000fd3803004830450221008b42e9089fd73cd1367d7963664314fd7affc434766b97ef35e57f10c764eca802204bdf578f5339448053cc077b410050ca95f54f0773d1be37e4c8f7028b5b630d014730440220599fa798537dbca65defc998088b56df77993914f7b1f6d262ae210c49caf00a022013f11bc2c85befdcf36aac357e0d1cd9cd1954ccbbe6c90c56575b5e68dbb17401473044022040cf3f17bb5639e9a64c89566b21a13339e362ef27f02ac3bdd09a79b5564a4402203556c8a5ef82e99c4148ef1b9626d663fc8aabefee8eecbb016439d0dc255b6c01483045022100cc20e9db6c4ae335eb6a9af4f9f915b8dc9e6e130af4266a3c720464796d68b3022010203f84d6f9cc20fba81d2971fcb276595c6c5ed6f009f99da5cea93ff5dfdc01483045022100e39c1896c909b87f2d860ba23af7e94873dfeccee33657d28041335ca708bb36022002b70cb3050bf418645e4703958252706e3008ac7a940a7248747f5651b0ea0a01004dc801645521020ace50bab1230f8002a0bfe619482af74b338cc9e4c956add228df47e6adae1c210231a395e332dde8688800a0025cccc5771ea1aa874a633b8ab6e5c89d300c7c3621025093f439fb8006fd29ab56605ffec9cdc840d16d2361004e1337a2f86d8bd2db21026b472f7d59d201ff1f540f111b6eb329e071c30a9d23e3d2bcd128fe73dc254c2103250c11be0561b1d7ae168b1f59e39cbc1fd1ba3cf4d2140c1a365b2723a2bf93210357f7ed4c118e581f49cd3b4d9dd1edb4295f4def49d6dcf2faaaaac87a1a0a422103ae72827d25030818c4947a800187b1fbcc33ae751e248ae60094cc989fb880f62103e05bf6002b62651378b1954820539c36ca405cbb778c225395dd9ebff67802992103ecd8af1e93c57a1b8c7f917bd9980af798adeb0205e9687865673353eb041e8d59670350cd00b275532102370a9838e4d15708ad14a104ee5606b36caaaaf739d833e67770ce9fd9b3ec80210257c293086c4d4fe8943deda5f890a37d11bebd140e220faa76258a41d077b4d42103c2660a46aa73078ee6016dee953488566426cf55fc8011edd0085634d75395f92103cd3e383ec6e12719a6c69515e5559bcbe037d0aa24c187e1e26ce932e22ad7b35468aeffffffff026cd03c00000000001976a9143a267e4435e590d9e711d04954d8c8ef1003658588aca59f1d020000000017a91485aaffdabb34e8f7403291b3eff574129cc2486d8700000000"),
532        ];
533
534        for (sigops, rawtx) in tx_sigops_pairs.iter() {
535            let tx: Transaction =
536                bitcoin::consensus::deserialize(&hex::decode(rawtx).unwrap()).unwrap();
537            assert_eq!(*sigops, tx.sigops().unwrap());
538        }
539    }
540
541    #[test]
542    #[cfg(feature = "counterparty")]
543    fn test_is_opreturn_counterparty_tx() {
544        // mainnet e08c3d808317731ef6040799646de2f567590ff890c8fe920a12e36502d8ceb0
545        let raw_tx = hex::decode("0100000001c05b048e0964c3db4a87c33e00503ccdc3d7bff75892639029ba311ce7829745010000006b483045022100df78463aa570274bb2be52ee7410265f5165c0f9f00fb67285582a3e365711ed022074eb1c368569164bb1ad4bca9833b8398e1bb90409e58ac92a98d5ecd117e7e4012103a6e6e5baa76d34499b6b895bccf5a7b5f0d6b264637dd1cddad72cea2c4a1499ffffffff0200000000000000004b6a49f7032ee8d7f20e860d177068a124158cebaa066a209a916e2e6fa26637468bfa7742f6c58724fb1d36aa209aa394245b94afbe385df237e9c4821c6e9d8b9c234f8de5c68760561d03ebd0eb01000000001976a9142e9943921a473dee1e04a579c1762ff6e9ac34e488ac00000000").unwrap();
546        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
547        assert!(is_opreturn_counterparty(&tx));
548
549        // mainnet 229028368caadfbab2654f888e919062f117e24da5b3a1974ea9796162191a56
550        let raw_tx = hex::decode("01000000011378419519eebd4034e6eae696e98eee4f247f2103394740844a1cc5462fa465010000006b483045022100f592b2df2e8b9dcf6bc77bd0e8cc1064b74c6119cfb3482f96d03d1442d60f82022040372b55fd7aba6d7474ddce7e6d6a7b9219018e925e31379d06f4216104e51e0121021f6dc395418fed2bce5eb47485b2945e3681ba0a690c57779b09f4b15cb66afbffffffff020000000000000000386a36a61966fd345606c378b4ccd3596ab28200b3cb90398c0babbd1ef635a6dfd106e89794feae9a58d7700e6d3b41782e2181118a94eb9117390000000000001976a914bcb7dadb45b78b33653d38789ac14d54dff6de1388ac00000000").unwrap();
551        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
552        assert!(is_opreturn_counterparty(&tx));
553    }
554
555    #[test]
556    #[cfg(feature = "counterparty")]
557    fn test_is_p2ms_counterparty_tx() {
558        // mainnet cf01557d4e6b0eec45d07d871e37253285e33672a3a9c0708a593c096ea98e16
559        let raw_tx = hex::decode("0100000001b731c05ddc859c44326689fb588e81e054def1baff4cdc368a038abc26e9b197010000008b483045022100926a2470ccee567d806fd9697dbcb8b2ae5525888403132ab6da2bc66b36e8500220170d6ae7b5ea57946eb9f0479a83da96c1622057551059ffd645df8f628410a00141046e2298d86527d08589cf81eefb0857f5dda1167b49d99cf03f3fb2b01098e1e3cedcbb6da3c7dbdc893f15bdef80d51f9b2e3099949e7d7c9bbb2ec1c19aef4effffffff03781e000000000000895121021fab2c6657cd39baaf6fe10d9cb6731a6f221f57c5d65e6c34f69e62ed6aa9d92103fc9831f32e44cd7f8e88af4815d0be5aed5af278f3cc055031ceb16075c7709541046e2298d86527d08589cf81eefb0857f5dda1167b49d99cf03f3fb2b01098e1e3cedcbb6da3c7dbdc893f15bdef80d51f9b2e3099949e7d7c9bbb2ec1c19aef4e53ae781e0000000000008951210306ab2c6657cd39baaf546fe1af06444c763947788b97152d72dfd523a025fd552102b3b65b804142b90bfefb95673ab7cc349c28860addaf6a3d1eafc21310b3038541046e2298d86527d08589cf81eefb0857f5dda1167b49d99cf03f3fb2b01098e1e3cedcbb6da3c7dbdc893f15bdef80d51f9b2e3099949e7d7c9bbb2ec1c19aef4e53ae5bd14200000000001976a914a925bca955a11b6c07c2767acfb3f1dabce7ae5688ac00000000").unwrap();
560        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
561        assert!(is_p2ms_counterparty(&tx));
562
563        // mainnet 229028368caadfbab2654f888e919062f117e24da5b3a1974ea9796162191a56
564        // OP_RETURN CounterParty tx
565        let raw_tx = hex::decode("01000000011378419519eebd4034e6eae696e98eee4f247f2103394740844a1cc5462fa465010000006b483045022100f592b2df2e8b9dcf6bc77bd0e8cc1064b74c6119cfb3482f96d03d1442d60f82022040372b55fd7aba6d7474ddce7e6d6a7b9219018e925e31379d06f4216104e51e0121021f6dc395418fed2bce5eb47485b2945e3681ba0a690c57779b09f4b15cb66afbffffffff020000000000000000386a36a61966fd345606c378b4ccd3596ab28200b3cb90398c0babbd1ef635a6dfd106e89794feae9a58d7700e6d3b41782e2181118a94eb9117390000000000001976a914bcb7dadb45b78b33653d38789ac14d54dff6de1388ac00000000").unwrap();
566        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
567        assert!(!is_p2ms_counterparty(&tx));
568    }
569
570    #[test]
571    #[cfg(feature = "counterparty")]
572    fn test_is_p2sh_counterparty_tx() {
573        // testnet 66ca3ba2df81d9c1da8ef4c253fab2ab5eda1db699ab028050c4d261d3716181
574        let raw_tx = hex::decode("010000000187e88fa930f1843d690db12fdb98b12efc9c52cc27b160aa4d128d82d4e8824500000000fd6201483045022100985cf3628b082c3af81509c048b13bf747251e869aee22133fc7a73dcfdfcc6a0220074fa0d9d1e0151e6f5cf84bccaf83f6f42c51aabec65c123bf327bdc13214a3014d16014ceb434e5452505254590300046f4a2fdff4db96b99f33972f11b9b32b545a824d086f5e1872b887654a3c44e5a3bd811e0ed8e911f58e6f78a50bfaf0e6a8dab23b0c802e7200d5a0d905c96fd8a4bff843c2ecd12b82b12b77e7151cd7928ef940000000584bf050f80000000000000007028000000000000000184746573744000000000000000619d989e5d195cf00000000000000018773656e64696e678000000000000000f0000000003253aac4c2a32b9ba1038bab4ba329030903637b7339039ba3934b7338000000000dc617b106b2cae6800000000533a84c4124ba60000000014f64d7104d2e60752102b1a624aadeb689e79e53c66cb282cbaaf9b381be4436ddcf48f99b5e403f2679ad0075740087ffffffff0100000000000000000e6a0cb18961d65a9207474701a54e00000000").unwrap();
575        let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
576        assert!(is_p2sh_counterparty(&tx));
577    }
578}