1use crate::script::{Multisig, PubKeyInfo};
4use bitcoin::{blockdata::opcodes::all as opcodes, script, Amount, TxOut};
5use std::{error, fmt};
6
7#[derive(Debug, Clone)]
8pub enum OutputError {
9 PubkeyInfo(script::Error),
10}
11
12impl fmt::Display for OutputError {
13 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14 match self {
15 OutputError::PubkeyInfo(e) => {
16 write!(f, "Could not extract pubkey infos from input: {}", e)
17 }
18 }
19 }
20}
21
22impl error::Error for OutputError {
23 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
24 match *self {
25 OutputError::PubkeyInfo(ref e) => Some(e),
26 }
27 }
28}
29
30#[derive(PartialEq, Eq, Debug, Clone)]
31pub struct OutputInfo {
32 pub out_type: OutputType,
33 pub value: Amount,
34 pub pubkey_stats: Vec<PubKeyInfo>,
35}
36
37impl OutputInfo {
38 pub fn new(output: &TxOut) -> Result<OutputInfo, OutputError> {
39 Ok(OutputInfo {
40 out_type: output.get_type(),
41 value: Amount::from_sat(output.value.to_sat()),
42 pubkey_stats: PubKeyInfo::from_output(output)?,
43 })
44 }
45
46 pub fn is_opreturn(&self) -> bool {
48 matches!(self.out_type, OutputType::OpReturn(_))
49 }
50}
51
52#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
53pub enum OutputType {
54 P2pk,
55 P2pkh,
56 P2wpkhV0,
57 P2ms,
58 P2sh,
59 P2wshV0,
60 OpReturn(OpReturnFlavor),
61 P2tr,
62 P2a,
63 Unknown,
64}
65
66#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)]
67pub enum OpReturnFlavor {
68 Unspecified,
69 WitnessCommitment,
70 Omni,
71 StacksBlockCommit,
74 Len1Byte,
75 Len20Byte,
76 Len80Byte,
77 Bip47PaymentCode,
78 RSKBlock,
81 CoreDao,
84 ExSat,
87 HathorNetwork,
90 Runestone,
93}
94
95impl fmt::Display for OpReturnFlavor {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 OpReturnFlavor::Unspecified => write!(f, "OP_RETURN"),
99 OpReturnFlavor::WitnessCommitment => write!(f, "Witness Commitment"),
100 OpReturnFlavor::Omni => write!(f, "OP_RETURN (OmniLayer)"),
101 OpReturnFlavor::StacksBlockCommit => write!(f, "OP_RETURN (Stacks v2 blockcommit)"),
102 OpReturnFlavor::Len1Byte => write!(f, "OP_RETURN (0 byte)"),
103 OpReturnFlavor::Len20Byte => write!(f, "OP_RETURN (20 byte)"),
104 OpReturnFlavor::Len80Byte => write!(f, "OP_RETURN (80 byte)"),
105 OpReturnFlavor::Bip47PaymentCode => write!(f, "OP_RETURN (BIP 47 Payment Code)"),
106 OpReturnFlavor::RSKBlock => write!(f, "OP_RETURN (Rootstock merge mining info)"),
107 OpReturnFlavor::CoreDao => write!(f, "OP_RETURN (CoreDao delegation info)"),
108 OpReturnFlavor::ExSat => write!(f, "OP_RETURN (ExSat info)"),
109 OpReturnFlavor::HathorNetwork => write!(f, "OP_RETURN (HatorNetwork aux_block_hash)"),
110 OpReturnFlavor::Runestone => write!(f, "OP_RETURN (Runestone)"),
111 }
112 }
113}
114
115impl fmt::Display for OutputType {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 OutputType::P2pk => write!(f, "P2PK"),
119 OutputType::P2pkh => write!(f, "P2PKH"),
120 OutputType::P2wpkhV0 => write!(f, "P2WPKH v0"),
121 OutputType::P2ms => write!(f, "P2MS"),
122 OutputType::P2sh => write!(f, "P2SH"),
123 OutputType::P2wshV0 => write!(f, "P2WSH v0"),
124 OutputType::OpReturn(flavor) => write!(f, "{}", flavor),
125 OutputType::P2tr => write!(f, "P2TR"),
126 OutputType::P2a => write!(f, "P2A"),
127 OutputType::Unknown => write!(f, "UNKNOWN"),
128 }
129 }
130}
131
132pub trait OutputTypeDetection {
133 fn get_type(&self) -> OutputType;
134
135 fn is_p2ms(&self) -> bool;
136 fn is_p2tr(&self) -> bool;
137 fn is_p2a(&self) -> bool;
138
139 fn is_witness_commitment(&self) -> bool;
141 fn is_opreturn_omni(&self) -> bool;
142 fn is_opreturn_stacks_blockcommit(&self) -> bool;
143 fn is_opreturn_with_len(&self, length: usize) -> bool;
144 fn is_opreturn_bip47_payment_code(&self) -> bool;
145 fn is_opreturn_rsk_block(&self) -> bool;
146 fn is_opreturn_coredao(&self) -> bool;
147 fn is_opreturn_exsat(&self) -> bool;
148 fn is_opreturn_hathor(&self) -> bool;
149 fn is_opreturn_runestone(&self) -> bool;
150}
151
152impl OutputTypeDetection for TxOut {
153 fn get_type(&self) -> OutputType {
154 if self.script_pubkey.is_p2pkh() {
155 OutputType::P2pkh
156 } else if self.script_pubkey.is_p2sh() {
157 OutputType::P2sh
158 } else if self.script_pubkey.is_p2wpkh() {
159 OutputType::P2wpkhV0
160 } else if self.script_pubkey.is_p2wsh() {
161 OutputType::P2wshV0
162 } else if self.is_p2tr() {
163 OutputType::P2tr
164 } else if self.is_p2a() {
165 OutputType::P2a
166 } else if self.script_pubkey.is_op_return() {
167 if self.is_witness_commitment() {
168 return OutputType::OpReturn(OpReturnFlavor::WitnessCommitment);
169 } else if self.is_opreturn_omni() {
170 return OutputType::OpReturn(OpReturnFlavor::Omni);
171 } else if self.is_opreturn_stacks_blockcommit() {
172 return OutputType::OpReturn(OpReturnFlavor::StacksBlockCommit);
173 } else if self.is_opreturn_bip47_payment_code() {
174 return OutputType::OpReturn(OpReturnFlavor::Bip47PaymentCode);
175 } else if self.is_opreturn_rsk_block() {
176 return OutputType::OpReturn(OpReturnFlavor::RSKBlock);
177 } else if self.is_opreturn_coredao() {
178 return OutputType::OpReturn(OpReturnFlavor::CoreDao);
179 } else if self.is_opreturn_exsat() {
180 return OutputType::OpReturn(OpReturnFlavor::ExSat);
181 } else if self.is_opreturn_hathor() {
182 return OutputType::OpReturn(OpReturnFlavor::HathorNetwork);
183 } else if self.is_opreturn_runestone() {
184 return OutputType::OpReturn(OpReturnFlavor::Runestone);
185 } else if self.is_opreturn_with_len(1) {
186 return OutputType::OpReturn(OpReturnFlavor::Len1Byte);
187 } else if self.is_opreturn_with_len(20) {
188 return OutputType::OpReturn(OpReturnFlavor::Len20Byte);
189 } else if self.is_opreturn_with_len(80) {
191 return OutputType::OpReturn(OpReturnFlavor::Len80Byte);
192 }
193 OutputType::OpReturn(OpReturnFlavor::Unspecified)
194 } else if self.script_pubkey.is_p2pk() {
195 OutputType::P2pk
196 } else if self.is_p2ms() {
197 OutputType::P2ms
198 } else {
199 OutputType::Unknown
200 }
201 }
202
203 fn is_p2ms(&self) -> bool {
210 if let Ok(Some(n_of_m)) = self.script_pubkey.get_opcheckmultisig_n_m() {
211 let n = n_of_m.0;
212 let m = n_of_m.1;
213 if n <= 3 && m <= 3 && m >= n {
214 return true;
215 }
216 }
217 false
218 }
219
220 fn is_p2tr(&self) -> bool {
225 let script_pubkey_bytes = self.script_pubkey.as_bytes();
226 if script_pubkey_bytes.len() == 34
227 && script_pubkey_bytes[0] == opcodes::OP_PUSHNUM_1.to_u8()
228 && script_pubkey_bytes[1] == opcodes::OP_PUSHBYTES_32.to_u8()
229 {
230 return true;
231 }
232 false
233 }
234
235 fn is_p2a(&self) -> bool {
240 let script_pubkey_bytes = self.script_pubkey.as_bytes();
241 script_pubkey_bytes.len() == 4
242 && script_pubkey_bytes[0] == opcodes::OP_PUSHNUM_1.to_u8()
243 && script_pubkey_bytes[1] == opcodes::OP_PUSHBYTES_2.to_u8()
244 && script_pubkey_bytes[2] == 0x4eu8
245 && script_pubkey_bytes[3] == 0x73u8
246 }
247
248 fn is_witness_commitment(&self) -> bool {
254 let script_pubkey_bytes = self.script_pubkey.as_bytes();
255 if script_pubkey_bytes.len() >= 38
256 && script_pubkey_bytes[0] == 0x6A
257 && script_pubkey_bytes[1] == 0x24
258 && script_pubkey_bytes[2] == 0xAA
259 && script_pubkey_bytes[3] == 0x21
260 && script_pubkey_bytes[4] == 0xA9
261 && script_pubkey_bytes[5] == 0xED
262 {
263 return true;
264 }
265 false
266 }
267
268 fn is_opreturn_omni(&self) -> bool {
272 let script_pubkey_bytes = self.script_pubkey.as_bytes();
273 if script_pubkey_bytes.len() > 6 && script_pubkey_bytes[0] == 0x6A &&
274 script_pubkey_bytes[2] == 0x6f &&
277 script_pubkey_bytes[3] == 0x6d &&
278 script_pubkey_bytes[4] == 0x6e &&
279 script_pubkey_bytes[5] == 0x69
280 {
281 return true;
282 }
283 false
284 }
285
286 fn is_opreturn_stacks_blockcommit(&self) -> bool {
294 let script_pubkey_bytes = self.script_pubkey.as_bytes();
295 if script_pubkey_bytes.len() == 83
296 && script_pubkey_bytes[0] == 0x6A
297 && script_pubkey_bytes[1] == 0x4C
298 && script_pubkey_bytes[2] == 0x50
299 && script_pubkey_bytes[3] == 0x58
300 && script_pubkey_bytes[4] == 0x32
301 && script_pubkey_bytes[5] == 0x5b
302 {
303 return true;
304 }
305 false
306 }
307
308 fn is_opreturn_bip47_payment_code(&self) -> bool {
316 let script_pubkey_bytes = self.script_pubkey.as_bytes();
317 if script_pubkey_bytes.len() != 83 {
318 return false;
319 }
320
321 if !(script_pubkey_bytes[0] == opcodes::OP_RETURN.to_u8()
322 && script_pubkey_bytes[1] == opcodes::OP_PUSHDATA1.to_u8()
323 && script_pubkey_bytes[2] == 80)
324 {
325 return false;
326 }
327
328 let payload = &script_pubkey_bytes[3..];
330 if payload[0] != 0x01 && payload[0] != 0x02 {
332 return false;
333 }
334 let sign_byte = payload[2];
336 if sign_byte != 0x02 && sign_byte != 0x03 {
337 return false;
338 }
339 if payload[3..35].iter().all(|&b| b == 0) {
344 return false;
345 }
346
347 let chain_code = &payload[35..67];
349 if chain_code.iter().all(|&b| b == 0) {
350 return false;
351 }
352 let reserved_bytes = &payload[67..80];
354 if !reserved_bytes.iter().all(|&b| b == 0) {
355 return false;
356 }
357 true
358 }
359
360 fn is_opreturn_rsk_block(&self) -> bool {
365 let script_pubkey_bytes = self.script_pubkey.as_bytes();
366 script_pubkey_bytes.len() == 43
367 && script_pubkey_bytes[0] == 0x6A
368 && script_pubkey_bytes[1] == 0x29 && script_pubkey_bytes[2] == b'R'
370 && script_pubkey_bytes[3] == b'S'
371 && script_pubkey_bytes[4] == b'K'
372 && script_pubkey_bytes[5] == b'B'
373 && script_pubkey_bytes[6] == b'L'
374 && script_pubkey_bytes[7] == b'O'
375 && script_pubkey_bytes[8] == b'C'
376 && script_pubkey_bytes[9] == b'K'
377 && script_pubkey_bytes[10] == b':'
378 || script_pubkey_bytes.len() == 44
380 && script_pubkey_bytes[0] == 0x6A
381 && script_pubkey_bytes[1] == 0x4c && script_pubkey_bytes[2] == 0x29 && script_pubkey_bytes[3] == b'R'
384 && script_pubkey_bytes[4] == b'S'
385 && script_pubkey_bytes[5] == b'K'
386 && script_pubkey_bytes[6] == b'B'
387 && script_pubkey_bytes[7] == b'L'
388 && script_pubkey_bytes[8] == b'O'
389 && script_pubkey_bytes[9] == b'C'
390 && script_pubkey_bytes[10] == b'K'
391 && script_pubkey_bytes[11] == b':'
392 }
393
394 fn is_opreturn_coredao(&self) -> bool {
399 let script_pubkey_bytes = self.script_pubkey.as_bytes();
400 script_pubkey_bytes.len() == 47
401 && script_pubkey_bytes[0] == 0x6A
402 && script_pubkey_bytes[1] == 0x2d && script_pubkey_bytes[2] == b'C'
404 && script_pubkey_bytes[3] == b'O'
405 && script_pubkey_bytes[4] == b'R'
406 && script_pubkey_bytes[5] == b'E'
407 && script_pubkey_bytes[6] == 0x01 }
409
410 fn is_opreturn_exsat(&self) -> bool {
415 let script_pubkey_bytes = self.script_pubkey.as_bytes();
416 script_pubkey_bytes.len() > 8
417 && script_pubkey_bytes[0] == 0x6A
418 && script_pubkey_bytes[2] == b'E'
420 && script_pubkey_bytes[3] == b'X'
421 && script_pubkey_bytes[4] == b'S'
422 && script_pubkey_bytes[5] == b'A'
423 && script_pubkey_bytes[6] == b'T'
424 && script_pubkey_bytes[7] == 0x01 }
426
427 fn is_opreturn_hathor(&self) -> bool {
432 let script_pubkey_bytes = self.script_pubkey.as_bytes();
433 script_pubkey_bytes.len() == 38
434 && script_pubkey_bytes[0] == 0x6A
435 && script_pubkey_bytes[1] == 0x24 && script_pubkey_bytes[2] == b'H'
437 && script_pubkey_bytes[3] == b'a'
438 && script_pubkey_bytes[4] == b't'
439 && script_pubkey_bytes[5] == b'h'
440 }
441
442 fn is_opreturn_runestone(&self) -> bool {
447 let script_pubkey_bytes = self.script_pubkey.as_bytes();
448 if script_pubkey_bytes.len() > 2
449 && script_pubkey_bytes[0] == opcodes::OP_RETURN.to_u8()
450 && script_pubkey_bytes[1] == opcodes::OP_PUSHNUM_13.to_u8()
451 {
452 for (index, inst_result) in self.script_pubkey.instructions().enumerate() {
453 if let Ok(inst) = inst_result {
454 match index {
455 0 => (), 1 => (), _ => {
458 match inst {
460 script::Instruction::Op(_) => {
461 return false;
462 }
463 script::Instruction::PushBytes(_) => (),
464 }
465 }
466 }
467 } else {
468 return false;
469 }
470 }
471 return true;
472 }
473 false
474 }
475
476 fn is_opreturn_with_len(&self, data_length: usize) -> bool {
483 const MIN_OPRETURN_LEN: usize = 1 + 1; const MAX_OPRETURN_LEN: usize = 1 + 1 + 1 + 80; const MAX_OPPUSHBYTES_LEN: usize = 1 + 1 + 75; if self.script_pubkey.len() < MIN_OPRETURN_LEN
488 || self.script_pubkey.len() > MAX_OPRETURN_LEN
489 {
490 return false;
491 }
492
493 if !self.script_pubkey.as_bytes()[0] == 0x6A {
494 return false;
495 }
496
497 if self.script_pubkey.len() <= MAX_OPPUSHBYTES_LEN {
498 return self.script_pubkey.len() - 1 - 1 == data_length;
499 }
500
501 if self.script_pubkey.len() > MAX_OPPUSHBYTES_LEN {
502 return self.script_pubkey.len() - 1 - 1 - 1 == data_length;
503 }
504
505 false
506 }
507}
508
509pub trait OutputSigops {
510 fn sigops(&self) -> usize;
511}
512
513impl OutputSigops for TxOut {
514 fn sigops(&self) -> usize {
515 const SIGOPS_SCALE_FACTOR: usize = 4;
516
517 if self.is_p2tr() {
519 return 0;
520 }
521
522 SIGOPS_SCALE_FACTOR * self.script_pubkey.count_sigops_legacy()
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::{OpReturnFlavor, OutputType, OutputTypeDetection};
530 use bitcoin::Transaction;
531
532 #[test]
533 fn output_type_detection_p2ms() {
534 let raw_tx = hex::decode("0100000001ffc0d6d6b592cd2b4160300a278ea5e250b5055b5536dcfb2da5dcc46022765a00000000694630430220575ddd235a989befbf98f43b008666e56af07be89e47e09d18690c75846fb587021f00830605aa09febc51132001e0dbcad860e54d4657b55aaf961b527a935b8a01210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1fffffffff03204e0000000000002551210281feb90c058c3436f8bc361930ae99fcfb530a699cdad141d7244bfcad521a1f51ae204e0000000000001976a914a988f8039a203cf86136e0d32b9d77eafa5a6bef88ac46f4d501000000001976a914161d7a3d0ee15c793ab300433192f949d8f3566588ac00000000").unwrap();
536 let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
537 let out0 = &tx.output[0];
538 assert!(out0.is_p2ms());
539 assert_eq!(out0.get_type(), OutputType::P2ms);
540 }
541
542 #[test]
543 fn output_type_detection_p2ms2() {
544 let raw_tx = hex::decode("010000000150db0324e3733b7d4915a42acf51d4cd95629fb5a659da68d01292e3152abf7d010000006b4830450221008c24014a99a87736aa47a773d738cbbcd60dadfbb2aa294d4f00cda1e4dae66f022076fa9be5d50eecd2e8e1dbe364b158f2d5df049cbcd8cc759970dd23fab41423012102dc6546ba58b9bc26365357a428516d48c9bbc230dd6fc72912654aaad460ef19ffffffff02781e00000000000069512102d7f69a1fc373a72468ae84634d9949fdeab4d1c903c6f23a3465f79c889342a421028836687b0c942c94801ce11b2601cbb1e900e6544ef28369e69977195794d47b2102dc6546ba58b9bc26365357a428516d48c9bbc230dd6fc72912654aaad460ef1953ae3c660d00000000001976a914e4e9d188d9806fef75904225f370009aa4103a9d88ac00000000").unwrap();
546 let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
547 let out0 = &tx.output[0];
548 assert!(out0.is_p2ms());
549 assert_eq!(out0.get_type(), OutputType::P2ms);
550 }
551
552 #[test]
553 fn output_type_detection_p2tr() {
554 let raw_tx = hex::decode("020000000001029dac93ef467e6035bf641f4076b2a8ac6a4368e93d6c7dc8dcfb38b9bed7da840100000000feffffffbe415b1058e5294f30ccc12332d00636aa8874448141a0446737a1ffc7e6f5060100000000feffffff0410270000000000002251207a61c588fd357d8ed58f624fa7f97a651d1ac00b53b055e9b852507dd319a3d41027000000000000225120acd385f4c428f2ce97644de474a579a77435f40b6161d1c1875f48f2626fccde1e0e1e00000000001600147f611a8cfa64617c05c1b44341b4e469631371c3102700000000000022512070271d98a521d0e4102ebdbc40f3e553666fb5b85c8c3d2709138568c6c90b230247304402202945170a29517bf8773f6a741e587d87b3f4ec6e7348fae8443d45bc5a30f82402200207fcdb3369e55060725bdc2343236271e2dddb62a3077577a85e6f79d22404012103f682085f03c8a27288258933370b4cef8badb4c8a0e8bbfa31d78a450dffd543024730440220711d103aaed2122a8ddef8fd5523ccc7e3748382804dddccdf46e4755c2d1e9f022060e0564f3bf307d5c2128a4bcfd521c33a2bf1c3590cfc0d4fa7c8e02af26ab4012103f682085f03c8a27288258933370b4cef8badb4c8a0e8bbfa31d78a450dffd54300000000").unwrap();
556 let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
557 let out0 = &tx.output[0];
558 let out1 = &tx.output[1];
559 let out3 = &tx.output[3];
560 assert!(out0.is_p2tr());
561 assert!(out1.is_p2tr());
562 assert!(out3.is_p2tr());
563 assert_eq!(out0.get_type(), OutputType::P2tr);
564 assert_eq!(out1.get_type(), OutputType::P2tr);
565 assert_eq!(out3.get_type(), OutputType::P2tr);
566 }
567
568 #[test]
569 fn output_type_detection_witness_commitment() {
570 let raw_tx = hex::decode("010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5403b54a0a41d8134fa906ec3741d8134fa83cdae32f3154486173682ffabe6d6d022644aec8e65c41869919439905bd5a6546045825016e8ab843121b089ee79f8000000000000000bf00d52a508f000000000000ffffffff02114cf82c000000001976a9142220867b1e79c403fafe339a809a65ed01cb697988ac0000000000000000266a24aa21a9ed0a8154218fc45bc35f274fafd2490849f8b88f75b3cd63b95096b2a861018f300120000000000000000000000000000000000000000000000000000000000000000000000000").unwrap();
573 let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
574 let out1 = &tx.output[1];
575 assert!(out1.is_witness_commitment());
576 assert_eq!(
577 out1.get_type(),
578 OutputType::OpReturn(OpReturnFlavor::WitnessCommitment)
579 );
580 }
581
582 #[test]
583 fn output_type_detection_opreturn_omni() {
584 let raw_tx = hex::decode("0100000001305fe5e034c625b571638cbce7837970ca1e84830e794c77de6582ea419bcfb5000000006a47304402206d3f22eff2a26e7f6e2c6e1ccfafa0ae174b5c265353b19d8ee3510316de40ed02201a49d55eaad2f78e7a0358e6ec0f9230ace39451c883845ad107af8822e1ccef0121030651e1d15ae9a284ffd712885529d3344db3700be756e6c22c56a6c1b57d359dffffffff03f64e0600000000001976a914b64513c1f1b889a556463243cca9c26ee626b9a088ac22020000000000001976a914c958135faa72449c106564acba252cfbc3a35ca688ac0000000000000000166a146f6d6e69000000000000001f000002115728ef0000000000").unwrap();
586 let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
587 let out2 = &tx.output[2];
588 assert!(out2.is_opreturn_omni());
589 assert_eq!(out2.get_type(), OutputType::OpReturn(OpReturnFlavor::Omni));
590 }
591
592 #[test]
593 fn output_type_detection_opreturn_stacks_blockcommmit() {
594 let raw_tx = hex::decode("01000000018d9da5a2bc0789aa38bd9e2c1248a5ddaea8f00e097748ca80695f6333adcc04030000006a47304402200704d5f943227080b0c1b8668a37d819804a5c99a0bdda593b3dc167c32a39d402207d2bbd4384e369d671f0c46a54fc4fbfeb14724ab99ea695d1184d4f1074ad18012103fb3bc5bae4c088ca38a8c68bfe741f3b1cb62a067b69917908089a2082af31aefdffffff040000000000000000536a4c5058325b1352fba61836c82246b240fb64043b3e705f8975aa2062d886e247aaeee76ad26f14f91d22c38c8c2fd41ff85e4b22b0cf97b412c53f9aa0182bd6deb51c9567000a3596004b000a2f80012b03989f0400000000001976a914000000000000000000000000000000000000000088ac989f0400000000001976a914000000000000000000000000000000000000000088ac1132920b000000001976a9142c16c83270b688fa3ac46dc69cc01f6321bce41088ac00000000").unwrap();
596 let tx: Transaction = bitcoin::consensus::deserialize(&raw_tx).unwrap();
597 let out2 = &tx.output[0];
598 assert!(out2.is_opreturn_stacks_blockcommit());
599 assert_eq!(
600 out2.get_type(),
601 OutputType::OpReturn(OpReturnFlavor::StacksBlockCommit)
602 );
603 }
604
605 #[test]
606 fn output_type_detection_p2a() {
607 let rawtx = hex::decode("020000000001016352fb25843f7e3e20ac70119a9e46447d24257e0a250215402da5449764610f0100000000fdffffff02d0070000000000000451024e73581b00000000000022512044b35747ac9a995294839fdbefa823ae2d0cfbed950b72755499d9039ae739b501400cb9cb49f7790080d9601d1c24bebfd0668ef170145888f303f487ab5a9c4acdb511274f7b305faef35718af80c108df5ac51538f71f450ecda63c8fa952365200000000").unwrap();
609 let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
610 let out0 = &tx.output[0];
611 let out1 = &tx.output[1];
612 assert!(out0.is_p2a());
613 assert!(out1.is_p2tr());
614 assert_eq!(out0.get_type(), OutputType::P2a);
615 assert_eq!(out1.get_type(), OutputType::P2tr);
616 }
617
618 #[test]
619 fn output_type_detection_bip47_failure() {
620 let txhex = "020000000001015792fbb130d7429483d7b30a90408f917883434c825b1a1ff06f416ac909f40d0300000000ffffffff030000000000000000036a01003c2d5000000000001976a914b7b5c111d77baf2881a1076e3224ff318b620fb888ac7bd80c0000000000220020eca0c4e30e7eafea1c3388ba7760de0484be8e548e6520bc69a7635e0f640fab0500483045022100b50e9df79b938f4dbc1af2b75635c5192a5b1819671b0f84c3707829ec138c7402206fd3413a5475602e1414ddb2dd1eed67360cf845312801ab145e8072079058b5014730440220774d3856a6d05fe0877ef117462cfe5e4812c2e3b53402b60b11743dd26ed90602206d1d212b8f3f2dd2e454d5c35a2a8017aa8674426f24097c569cb0049658850701473044022017af04b20a119352d06def462cef26942e92f0f2ef2f0216d5993930f83f0c34022035d2ccd624b63a0531cea9259be703c0aa9ff1c2cf3d576c4878310cfa3ae9ff01ad532102272aefe25b54a7f5fe991a68e21146c34ac9bcafd81682dc599d4a337ce2535c2102c33eb3a569e78f8cd53480128383e602ba06314700e83dfcb5da149132a6b3e52102f1ec97fcf92d60baa4baa9b35d6e61ea06337db698919125e590af67cc30ae192102f2416ebb7b55a691ef094056d989b30d97f4600db03e6a6f9339e3034977b54f21039be0c58b27b79011b50a0ca2c73dd029ec909711345a61c3984630d1010dbe5355ae00000000";
626 let rawtx = hex::decode(txhex).unwrap();
627 let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
628 let out = &tx.output[0];
629 assert!(!out.is_opreturn_bip47_payment_code());
630 }
631
632 #[test]
633 fn output_type_detection_bip47_payment_code() {
634 let testcases = vec![
635 (0, "02000000000101cdd51d9048b22420cd2af3538aa7ea71951b81b4dee3894cc20f5c13fe463f783500000000fdffffff040000000000000000536a4c50010003a4b1880f11b6de85617c0aa9a21a3073dbe5a2fa277aa8f626ba9cb95b3c9c025e36a6ec791cedb876a079a264a40fa42531aa60825ca1d243998f54dd8977450000000000000000000000000022020000000000001976a914f522819122c73c04068577724e2ee7d05a0965d888ac983a00000000000016001437808929e894e2c691bd705f802c203716ea11fc9bdb00000000000016001427f5f2387d9efc49fade31556e81c30f8fa666730247304402206661b9bba8f4bf116f9a53c36de4b4ba35501f0d46a1c36a76c0e792b1c422ef02206dc75e32ec9a47068a749d0e1fee455ee483568a78387b86c8b179d293230f90012103c6c004d651ae8428b33a7ba4222bc92e4a7631f5791cb286e49fb38e89a3e662806b0b00"),
640 (0, "02000000000101ba7f679c0adb514945c9a4a00f73ecbb73f2e6116208e4ae314b9910c9f221020000000000ffffffff040000000000000000536a4c50010002b13b2911719409d704ecc69f74fa315a6cb20fdd6ee39bc9874667703d67b164927b0e88f89f3f8b963549eab2533b5d7ed481a3bea7e953b546b4e91b6f50d80000000000000000000000000022020000000000001976a914e335b83658f7565d19405117cbd2f85a743653ba88ac983a000000000000160014b1c8c8ba853483ac9faaa9445e5d2b64f6e4e5fed291200000000000160014b9623df59eda43987c3102cf8fc3935c0c651b2e0247304402201de79d8959e8d0420b9ca54932497aad6d068c0d7222c0a7ac5badab335169ff02206f67d38356d7238eb0a5a53cfe57bf7c0553a6bbe270199b969eba008fa1a35f01210281ae97b31eccbbfd83b3cbef1c0c6dc82cd13f34668fae266cf9dc87352144ef00000000"),
644 (0, "01000000000101a390284b399704a12c1d5c9b43181f0b984f2c17f619522675de80962546e5af0000000000ffffffff030000000000000000536a4c500100036c11d62dc5d47a03edf9027dc045406f81da1bf89e701c3ac691468ecb154661d7e65b9aa400701c44f396b627e8592bbb081764d92df98ebce9a8a3b9360dfc0000000000000000000000000022020000000000001976a9143431c401040f906b9eaea2d0fe2511688b4033c988ac1c470000000000001600147701a0aa820832428ef0133c7ba1b44b627ecd0f0247304402202ebad56e95f7dfa91c68c0e7a7e4e70e4b0eff674e903f1909ae3b03649ca25602206b6b31a17a231e90a035e10127e4cde84a36270d1e5e21ef11140078f65fac91012102a7a6edc3de4e35ab54f98b69bedace05cc649e0dc51c7dfdeabe0db42be32d7c00000000"),
648 (0, "020000000001012364df1e5c5b2a357e63183f7985bfd14e0be165c76bfb34d9067c63e6bfd19be400000000fdffffff030000000000000000536a4c500100029a516f23dcc89a26430b72baac2e6b3abded6cdf98e1a6b377f519e77b9facf5c2affa8263329f80d922378fbf63a62cf07cda0b67942bd00db44823c91f72070000000000000000000000000056fa05000000000016001418387b71aa4c8d241f5ae8848e79299a40bb5e6922020000000000001976a91465724c591ae40f62c0815a51522d60d2e319b45f88ac0247304402202a10fff3b0fc8cf275f54bd4e71fdc1fad6580fc442842c3a6da81e63dcfe75102203b96541ceef9e33cc9c459f028446e36300a83f958598b750b64d608478dffe0012102010012e1eb74242bdd7c9c43be974cdd1d8e301f3cd52823d28f100bc53b40becaad0300"),
651 (0, "02000000000101dca9b1082c0ebb7d3c56368c5edca4feb254c55bae4fca2bdf2a92f52a9ce5320200000000fdffffff030000000000000000536a4c50010002fc575207a99903a525022be54fb6c9872ab418ff766795a6437c80393a57861d078e55fe8dbc995d772703bbe6076143b05c5ee8e007e2bffedf87d618e3d76a0000000000000000000000000002e80500000000001600145f13323a3d6271d8ea28370cdeb2d3c07d172fdf22020000000000001976a9142cedadc4dc423f698cbdcac8ee9571b91c56f13f88ac0247304402201168bb51d7031ddeaf6fd95abe14b6641b2cdfcc38e3983ecc0ff19f249063d002200a2afd7afed9cae6888761808c1440370f48eaada67d008f1143e0dd96ec884f01210288263dfde13ab364938a44fa983c3f51f7c38cad01fdde7d0c94ade12deeb860caad0300"),
654 (1, "02000000000101866718b8560b53bdb878865e6c8c5ca76415d00a0d00cd9cf4b7b7154a81fdd90100000000fdffffff032cf10500000000001600149e1c68bf6d006ac3ffa59cd75efab7888ee862390000000000000000536a4c50010002c1addde6d69e908195bc0e43c4fd16ba0f7c1e4181a03f7c04d5a21dd8c378af6c0c378f068498915452cf7005b0cd5f33f228971c345038271bf974cec731730000000000000000000000000022020000000000001976a91464fa781a0d4389518af519efe591dd8f1ca63e2488ac0247304402204458c82463809b368cf705f1ade063bd46174d2f679e2e9ebc4e54fe7f37e6ac02204c1c7f6e1b7541c8a6eb3954b4eca694a9a4fd8da9712c1348a34b951c9506e1012103ce30da189fed4a7885ec579feacc008a2df738d5592ecbd3ac840fd973675063caad0300"),
657 (0, "020000000001018d456318a27c4b421fbbcc8f9714dec7f99c21fa19677c9e7478e01ee041f89a0200000000fdffffff030000000000000000536a4c5001000202d0f2d07130dace07e45b57e5f6fb6a5f1c86e5aa7d6dd4d8aa4f30337f1fe3827dc9098a6650fc8b8e81a48c4c9c5b04dca2f77e8a98241d319842ab2517f1000000000000000000000000003af405000000000016001431afd3b96454958646eaf28726ab8f9916267b4622020000000000001976a914d86b062ac7d8d682149d71ab884dae54b4b816f188ac0247304402200a7fa0ee9048147bcdf043e93c6913e7b3620edc4eaa570d7eaf764c98c775a502203664108b6f06b237fb41911390b94267f2177b90319c3ecafdf2fcb87d8833810121026d02c3435f4e13f348c8ca852995b756ee34539cdeb3da39c8d87ec163ef2bcccaad0300"),
660 (0, "0200000000010129b218494093ff702215eeaeb9103e66da78baaed160c43da5487f78841f00ee0100000000fdffffff030000000000000000536a4c50010002cdd82e5a14af597148a04cc15b0209c67b27ade0f763de4af9c8277a80725b6f22f46db3d2815e080fac413c758cd26ad54196e42744ebde36a159b0179c55b30000000000000000000000000022020000000000001976a914f85cc4a7022a3f2b91eccd5b426d68ba40be9b8988ac48f7050000000000160014043f2fd2079a9d7a2e0fec2e25a9667cd0f79c380247304402206fb17a155cfce2a1c0c55bdafdc7935f538bea5ff74cfacd4ee998dbb51ffae20220571dce283d7288fd759aed0bcd44aeeee897a588434c62f0952e0f18cf9c6a46012103c961e88949b6ed625a9bacdd104d190dd2b0fbcc94f97afdfe8ddfad25dca9c0caad0300"),
663 (1, "02000000000101828561be351a7a8a82fa3de6f9c2ab78668d19e4709b6ad78e3a20661dc68d4d0000000000fdffffff031eee05000000000016001444267dab545a75dfe6af8178e20b9cad6cd8e2ff0000000000000000536a4c500100020ddf6d3bb46d48e5211db2760ef7438dc419654dbe695d58c948b8b1f5ea7353cfdced7eab6f6a80fa14bc74d91e058b0c1d120a605c36ad5f3246ffa4d89b580000000000000000000000000022020000000000001976a914e705081188cdd43527fbea2150db9b0189970e0c88ac0247304402204261a04f3b8651612bb94cb204a5f90139f196a87aa166ebf92a26c75d3c18a402204422255c8222527ad445e2f063175dfc274b98b14a97b065b33e5c4cd6c74de9012103b1a3d3e1277b64dfe9e05f3a2421c1a53a3bd41aa2f8e69fd24985bf105a355ecaad0300"),
666 (1, "02000000000101bbaa3446baff6a3baf121131e93a7dd2c1531a904da5d17dc7ee4cae2c845bfb0000000000fdffffff0322020000000000001976a914ffba4d90d0be82b01c490e08697b9ffc673310e388ac0000000000000000536a4c50010002d0e5ecefdcad186cee3d4bf2a5c68a0bd8cd463a4ee644e2f21023cdf0e4a68dd79604907773547b8e0f7acf57c55fdcf7da42b1ce3761ea333498a4904824a00000000000000000000000000010eb05000000000016001411732aa696294ca454a633e909d5544367a6f9250247304402202ae0ceb7a1f898e99bfb79c5d60143ea4505dae0bb225c0fdb3481189c0c4d050220521b0a2e98ad682d151155b16d7f97ba1458e72741359d61e093756c02d96bde012102f1f400b3976309cc5bf1c81b09fd8cb59e4c49d5118c2d8ad5e3036d25fe1314caad0300"),
669 (1, "010000000186f411ab1c8e70ae8a0795ab7a6757aea6e4d5ae1826fc7b8f00c597d500609c010000006b483045022100ac8c6dbc482c79e86c18928a8b364923c774bfdbd852059f6b3778f2319b59a7022029d7cc5724e2f41ab1fcfc0ba5a0d4f57ca76f72f19530ba97c860c70a6bf0a801210272d83d8a1fa323feab1c085157a0791b46eba34afb8bfbfaeb3a3fcc3f2c9ad8ffffffff0210270000000000001976a9148066a8e7ee82e5c5b9b7dc1765038340dc5420a988ac1027000000000000536a4c50010002063e4eb95e62791b06c50e1a3a942e1ecaaa9afbbeb324d16ae6821e091611fa96c0cf048f607fe51a0327f5e2528979311c78cb2de0d682c61e1180fc3d543b0000000000000000000000000000000000")
673 ];
674 for (i, (output_index, txhex)) in testcases.iter().enumerate() {
675 println!("Testing case {}", i);
676 let rawtx = hex::decode(txhex).unwrap();
677 let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
678 let out = &tx.output[*output_index];
679 assert!(out.is_opreturn_bip47_payment_code());
680 assert_eq!(
681 out.get_type(),
682 OutputType::OpReturn(OpReturnFlavor::Bip47PaymentCode)
683 );
684 }
685 }
686
687 #[test]
688 fn output_type_detection_opreturn_coinbase() {
689 const NA: usize = usize::MAX;
690 let testcases = vec![
691 (5, 3, 4, NA, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff580338970d1b4d696e656420627920416e74506f6f6c3937304d0043010b007fe4fabe6d6d0b528b660c38cd611ff89d34e03ae3d92d7f9ac6bec6599422e8fcb18978507f08000000000000000000936100aeb83100000000ffffffff06220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade7874b31eb120000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed2b33a26f7157d656c9fd7aab093e4a63a3c463ecf9294156002e6a134ac22f7200000000000000002f6a2d434f52450142fdeae88682a965939fee9b7b2bd5b99694ff644e3ecda72cb7961caa4b541b1e322bcfe0b5a0300000000000000000146a12455853415401000d130f0e0e0b041f12001300000000000000002b6a2952534b424c4f434b3a359c5f6d8523559163efd9f7884d3ef37b5953b334cd79efab0af412007111430120000000000000000000000000000000000000000000000000000000000000000000000000"),
695 (5, 3, 4, NA, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff590340970d1c4d696e656420627920416e74506f6f6c3935394d006b001d88b8f801fabe6d6d332a2c23490221c9658da4e117b5d8a3d6aea298b15e8eca1e6ef8b5060d19bc08000000000000000000f8608d6d020000000000ffffffff06220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade7878f3dc4120000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed3bca42ac84fe2476aa283585a57912919dc0989a4ff0c2ed69006ec4f0aedcdb00000000000000002f6a2d434f5245012953559db5cc88ab20b1960faa9793803d0703374e3ecda72cb7961caa4b541b1e322bcfe0b5a0300000000000000000146a12455853415401000d130f0e0e0b041f12001300000000000000002b6a2952534b424c4f434b3ac217de142117834272725babb0a7ffc355805877cc34cd79efab0a0a007111bf0120000000000000000000000000000000000000000000000000000000000000000000000000"),
697 (3, 2, NA, NA,"010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5803ff340c1b4d696e656420627920416e74506f6f6c3930374a00b3004c23a345fabe6d6d4bd088d7ad6d953c6204534adf6e781f1b9dc83b631c445e22010a064e1e080b02000000000000002fd40000510e160000000000ffffffff04485d0f260000000017a9144b09d828dfc8baaba5d04ee77397e04b1050cc73870000000000000000266a24aa21a9ed8d16907a7020cedcdeb04966ea3550de7240d647e24ebbdd9dad990f324339c300000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d55997be5a09d05bb9bac27ec60419d0b373f32b2000000000000000002b6a2952534b424c4f434b3a4a327f77c940503af6f3b98b88419e0bfefff343ffe2a212327ed32a0053dfbf0120000000000000000000000000000000000000000000000000000000000000000000000000"),
699 (1, NA, NA, NA, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff600340d10c192f5669614254432f4d696e65642062792062757a7a3132302f2cfabe6d6d144b553283a6e1a150c9989428c0695e3a1bef7d482ed1f829bbe25897fd37dc10000000000000001058a4c9000cc3a31889b38ae08249000000000000ffffffff03fb80e4f2000000001976a914536ffa992491508dca0354e52f32a3a7a679a53a88ac00000000000000002b6a2952534b424c4f434b3a52e15efafb3e2cf6dc2fc0e6bde5cb1d7d2143f1e089bd874e6b7913005fb2a00000000000000000266a24aa21a9ed88601d3d03ccce017fe2131c4c95a7292e4372983148e62996bb5e2de0e4d1d80120000000000000000000000000000000000000000000000000000000000000000000000000"),
701 (5, 3, NA, NA, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff64034bd10c2cfabe6d6d1f0fa69d2963d0c315a112f5f919c4d0225e21d109a8a40151a16f09460d4a6110000000f09f909f092f4632506f6f6c2f6600000000000000000000000000000000000000000000000000000000000000000000000500423d0100000000000622020000000000001976a914c6740a12d0a7d556f89782bf5faf0e12cf25a63988acf47c9083000000001976a914c85526a428126c00ad071b56341a5a553a5e96a388ac0000000000000000266a24aa21a9ed74a2c74f1251642cdeb0c0ac9222465f604afe78bfe6db8fc89d0c22924f8da300000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d5e7ec323813c943336c579e238228a8ebd096a7e50000000000000000266a24486174681f48b44796265b5f7229ddd13df801436533bfafb4ceb84c58c77483a9bbf3a200000000000000002c6a4c2952534b424c4f434b3a84396648c7e1be1123bfc316ffd41792323f833bb794b7e089bd871b005fb368012000000000000000000000000000000000000000000000000000000000000000000fdbe040"),
703 (6, 3, 4, 5, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff6403d1640d2cfabe6d6d2b1fb3a4a828afdd7608d1f8c1b4dccc39d8a75f797a133a2f8a16d66f54893f10000000f09f909f092f4632506f6f6c2f7300000000000000000000000000000000000000000000000000000000000000000000000500673f084b000000000722020000000000001976a914c6740a12d0a7d556f89782bf5faf0e12cf25a63988acaefec312000000001976a914c85526a428126c00ad071b56341a5a553a5e96a388ac0000000000000000266a24aa21a9ed93a39b1567cf5ca8b9c4a80e4567e2ad642ed52c3b087f19b3f652148ed5ebb700000000000000002f6a2d434f524501ebbaf365b0d5fa072e2b2429db23696291f2c038e7ec323813c943336c579e238228a8ebd096a7e50000000000000000126a10455853415401051b0f0e0e0b1f1200130000000000000000266a24486174684878f3caa0965ac6a8c27596f07bb0968e14c2f68372c3903d970ed4e107e8f000000000000000002c6a4c2952534b424c4f434b3a16cf32df3db0d8dcd29513c17408ecdaffb11af833681fd54be8aa0b006c3ecb012000000000000000000000000000000000000000000000000000000000000000009a24f33e"),
705 (4, 3, NA, NA, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff5a03d4640d1d506f7765726564206279204c75786f7220546563682400320238d28ebcfabe6d6d84e0cf7e6b67e03bd3ff229bb662e6878c4f4ad115d4c9a1284fb8c6bd9012ec10000000000000000000c437001a360200000000ffffffff05220200000000000017a914bf73ad4cf3a107812bad3deb310611bee49a3c7987f53900130000000017a914056adde53ebc396a1b3b678bb0d3a5c116ff430c870000000000000000266a24aa21a9ed2159e3043910b8a942205da1e208a55841697cbc5fdc30add503374a23622d0000000000000000002f6a2d434f524501a21cbd3caa4fe89bccd1d716c92ce4533e4d4733f459cc4ca322d298304ff163b2a360d756c5db8400000000000000002b6a2952534b424c4f434b3ae5156a9b29201650c69df60be76c488512831869a47d33681fd54b18006c3f560120000000000000000000000000000000000000000000000000000000000000000000000000"),
707 (NA, 3, NA, NA, "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff3103e0640d04843979672f466f756e6472792055534120506f6f6c202364726f70676f6c642f234456df3d616f0000000000ffffffff0422020000000000002251203daaca9b82a51aca960c1491588246029d7e0fc49e0abdbcc8fd17574be5c74b7efcc512000000002200207086320071974eef5e72eaa01dd9096e10c0383483855ea6b344259c244f73c20000000000000000266a24aa21a9ed9674ac27a1d6a81ee2087cc127ef242ccfa4d7f8245e41df9d2007c337dfb72d00000000000000002f6a2d434f5245012e50087fb834747606ed01ad67ad0f32129ab431e6d18fda214e5b9f350ffc7b6cf3058b9026e7650120000000000000000000000000000000000000000000000000000000000000000000000000"),
709 (6, 3, 4, 5, "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff640363970d2cfabe6d6db1815c3a23ca2384f392a766b033f6f67bc0e52231e93d39eb34dba2dd84fd4610000000f09f909f092f4632506f6f6c2f6b000000000000000000000000000000000000000000000000000000000000000000000005000477be32000000000722020000000000001976a914c6740a12d0a7d556f89782bf5faf0e12cf25a63988ac5559c412000000001976a91469f2a01f4ff9e6ac24df9062e9828753474b348088ac0000000000000000266a24aa21a9ed825c6312e536c2ccf9a406c746b30eab23ddcf59a0313906f7a9e69a712b569900000000000000002f6a2d434f524501f6fdbc19a25dc91454cec19ef7714e8b67c4e0e6e7ec323813c943336c579e238228a8ebd096a7e50000000000000000126a10455853415401051b0f0e0e0b1f1200130000000000000000266a24486174686db90bbcde2697d1bbf9fc11cd57ae4c085349ea646f25bdae43bff94b5f6aeb00000000000000002c6a4c2952534b424c4f434b3a87f76f090e7e9934cebcaa5ba23f3a48e3edfc79522277f27a6160150071149801200000000000000000000000000000000000000000000000000000000000000000faa04d41"),
711 ];
712 for (i, (rsk_out_i, coredao_out_i, exsat_out_i, hathor_out_i, txhex)) in
713 testcases.iter().enumerate()
714 {
715 println!("Testing case {}", i);
716 let rawtx = hex::decode(txhex).unwrap();
717 let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
718
719 if *rsk_out_i != NA {
721 let rsk_out = &tx.output[*rsk_out_i];
722 assert!(rsk_out.is_opreturn_rsk_block());
723 assert_eq!(
724 rsk_out.get_type(),
725 OutputType::OpReturn(OpReturnFlavor::RSKBlock)
726 );
727 }
728
729 if *coredao_out_i != NA {
731 let coredao_out = &tx.output[*coredao_out_i];
732 assert!(coredao_out.is_opreturn_coredao());
733 assert_eq!(
734 coredao_out.get_type(),
735 OutputType::OpReturn(OpReturnFlavor::CoreDao)
736 );
737 }
738
739 if *exsat_out_i != NA {
741 let exsat_out = &tx.output[*exsat_out_i];
742 assert!(exsat_out.is_opreturn_exsat());
743 assert_eq!(
744 exsat_out.get_type(),
745 OutputType::OpReturn(OpReturnFlavor::ExSat)
746 );
747 }
748
749 if *hathor_out_i != NA {
751 let hathor_out = &tx.output[*hathor_out_i];
752 assert!(hathor_out.is_opreturn_hathor());
753 assert_eq!(
754 hathor_out.get_type(),
755 OutputType::OpReturn(OpReturnFlavor::HathorNetwork)
756 );
757 }
758 }
759 }
760
761 #[test]
762 fn output_type_detection_opreturn_runestone() {
763 let testcases = vec![
764 (0, "02000000000102b3d7ed7f7749a7b1ee032f1f8e40df38f97ce4b4cb0ffae1b3d3d31a96adf9090100000000ffffffff7eff527136c8eb2c37a1c0cea9076ca34411e491759ad8685046d97aa9e3e15d0200000000ffffffff0300000000000000000e6a5d0b00c0a23303d89af1dc12012202000000000000160014979bdd94f2d5972c062a7839ef114c193eca970ae1a4160000000000160014d58d2895ae676864b28af1d980c797d83f4d781f02473044022005309bf4bb5df2e5757deffedf171f3b93c19431ac1706debc6c0419ac3ab31e022002600e160dbeede6ab9473746c24cd9a382a2e05c67db203886497a8f713099c012102b9f2ec0c7c12cf5d1781518cfeadbe70d912084c07659f38a537ed1c2aa0e7a502473044022050ee4c0c3ba75d83a40d6304e4285e368b626e669d602f28c4ae7f6c32ac8b3e0220651b3299be01d93bdf91def09a1430e36a5fd25e43218caa5402e94c3b2a6918012103497a874c3412319689cc435756a820c3cf4cbb22d88ccafa29a8dc5e74eaa53800000000"),
769 (1, "02000000000101c68b2c3b546fb597e8cc4d10c9149a422b3dabef222bb28b83090b106da1282d0000000000ffffffff042202000000000000225120ffdd573c4ffac896f32c87c55923c5259736c4eda62b69e2317da1f41d8760120000000000000000116a5d0eff7f818cec82d08bc0a88281d215d62900000000000016001471a0ad6195dfc360f87452dd43e7a185f2667f28e867040000000000160014becb2a1cb327c74b96b1b5e4944f3155fa38793902473044022077e76fd6d6507fc7ac123c0a8c54b22be3566261ea28aab05479e9f86434ba1b02205c67a890799723a4174f48fef696e5d53a2ee30ae6481ef1cfe2fc10d8cb7c1c012102e951e84704fca4c56e7687e5d66b70501408860b1eb38f96930115c2c1af0b4700000000"),
772 (1, "020000000001017fb9cc941aa0ca3aaf339783564d2d29ec3254a9128f5d49ad3eeb002aeb40ec0000000000000000000242342a6b000000002251203b8b3ab1453eb47e2d4903b963776680e30863df3625d3e74292338ae7928da10000000000000000246a5d21020704b5e1d8e1c8eeb788a30705a02d039f3e01020680dc9afd2808c7e8430a640340924b2624416402a52ed7cf4eba6b2c535d2def8e649a74ed97aaca5ec54881ef3b34da68bb13d76d6b420e60297a9247cb081d1e59cb2c260b1509cff25d4b3158204c04e894d5357840e324b24c959ca6a5082035f6ffae12f331202bc84bf4612eac0063036f7264010b2047f22ed15d3082f5e9a005864528e4f991ade841a9c5846e2c118425878b6be1010d09b530368c74df10a3036821c04c04e894d5357840e324b24c959ca6a5082035f6ffae12f331202bc84bf4612e00000000"),
775 (1, "02000000000101ebbdf6c9c8802683f7002220f4598f4fb09e9312246e84295618d8a236cb647a0000000000fdffffff024404000000000000225120bdd80b1763ec6cc124c578c9cd3f9321f65186135123b921e2062cea7b029f3700000000000000001e6a5d1b0205048b93fbc8f4c580850a010203940505e84b06acd1d5bec304034085e21f3bd22fa1c17c974891fb274ce5daf19cdc7a8e4dc246c044b52748559ec1c55237753edbd8ebe7e9148336a459043517eb252fd80db0bd3b3d44c5eb537f203706667a27041bec19c7e0ff94aaa11457860e960b34cc2aa3b7bc4c8866d326ac0063036f72640102022202010320ef287cdfbf0d302346654d1e5c75c4ddf708ae4abc81caa6a89eb9880abf1d93010b209198aa67989b85f5e3c404f3465a0d0e7f8b137e963100b18bd298e3b03166b6010d088bc91e492f020a0a6821c13706667a27041bec19c7e0ff94aaa11457860e960b34cc2aa3b7bc4c8866d32640d10c00"),
778 ];
779 for (i, (output_index, txhex)) in testcases.iter().enumerate() {
780 println!("Testing case {}", i);
781 let rawtx = hex::decode(txhex).unwrap();
782 let tx: Transaction = bitcoin::consensus::deserialize(&rawtx).unwrap();
783 let out = &tx.output[*output_index];
784 assert!(out.is_opreturn_runestone());
785 assert_eq!(
786 out.get_type(),
787 OutputType::OpReturn(OpReturnFlavor::Runestone)
788 );
789 }
790 }
791}