1use crate::input::{InputError, InputType, InputTypeDetection, ScriptHashInput};
4use crate::output::{OutputError, OutputType, OutputTypeDetection};
5use bitcoin::blockdata::opcodes::all as opcodes;
6use bitcoin::blockdata::script;
7use bitcoin::secp256k1::{ecdsa, schnorr};
8use std::convert::TryInto;
9
10const SECP256K1_HALF_CURVE_ORDER: [u8; 32] = [
11 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
12 0x5d, 0x57, 0x6e, 0x73, 0x57, 0xa4, 0x50, 0x1d, 0xdf, 0xe9, 0x2f, 0x46, 0x68, 0x1b, 0x20, 0xa0,
13];
14const LOW_R_THRESHOLD: [u8; 32] = [
15 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
17];
18
19const ECDSA_SIG_MIN_LEN: usize = 9;
20const ECDSA_SIG_MAX_LAX_DER_LEN: usize = 123;
22const ECDSA_SIG_MAX_STRICT_DER_LEN: usize = 73;
23
24pub fn instructions_as_vec(
27 script: &bitcoin::Script,
28) -> Result<Vec<script::Instruction>, script::Error> {
29 script
30 .instructions()
31 .collect::<Result<Vec<script::Instruction>, script::Error>>()
32}
33
34pub trait PublicKey {
35 fn is_pubkey(&self) -> bool {
36 self.is_ecdsa_pubkey()
37 }
38 fn is_ecdsa_pubkey(&self) -> bool;
39 fn is_schnorr_pubkey(&self) -> bool;
40}
41
42impl PublicKey for script::Instruction<'_> {
43 fn is_ecdsa_pubkey(&self) -> bool {
44 match self {
45 script::Instruction::PushBytes(bytes) => bytes.as_bytes().is_ecdsa_pubkey(),
46 script::Instruction::Op(_) => false,
47 }
48 }
49
50 fn is_schnorr_pubkey(&self) -> bool {
51 match self {
52 script::Instruction::PushBytes(bytes) => bytes.as_bytes().is_schnorr_pubkey(),
53 script::Instruction::Op(_) => false,
54 }
55 }
56}
57
58impl PublicKey for [u8] {
59 fn is_ecdsa_pubkey(&self) -> bool {
60 if self.len() != bitcoin::key::constants::PUBLIC_KEY_SIZE
62 && self.len() != bitcoin::key::constants::UNCOMPRESSED_PUBLIC_KEY_SIZE
63 {
64 return false;
65 }
66 bitcoin::PublicKey::from_slice(self).is_ok()
67 }
68
69 fn is_schnorr_pubkey(&self) -> bool {
70 if self.len() != bitcoin::key::constants::SCHNORR_PUBLIC_KEY_SIZE {
72 return false;
73 }
74 bitcoin::key::XOnlyPublicKey::from_slice(self).is_ok()
75 }
76}
77
78impl PublicKey for Vec<u8> {
79 fn is_ecdsa_pubkey(&self) -> bool {
80 if self.len() != bitcoin::key::constants::PUBLIC_KEY_SIZE
82 && self.len() != bitcoin::key::constants::UNCOMPRESSED_PUBLIC_KEY_SIZE
83 {
84 return false;
85 }
86 bitcoin::PublicKey::from_slice(self).is_ok()
87 }
88
89 fn is_schnorr_pubkey(&self) -> bool {
90 if self.len() != bitcoin::key::constants::SCHNORR_PUBLIC_KEY_SIZE {
92 return false;
93 }
94 bitcoin::key::XOnlyPublicKey::from_slice(self).is_ok()
95 }
96}
97
98pub trait Signature {
99 fn is_signature(&self) -> bool {
100 self.is_ecdsa_signature(false) || self.is_schnorr_signature()
101 }
102 fn is_ecdsa_signature(&self, strict_der: bool) -> bool;
105
106 fn is_schnorr_signature(&self) -> bool;
109}
110
111impl Signature for script::Instruction<'_> {
112 fn is_ecdsa_signature(&self, strict_der: bool) -> bool {
113 match self {
114 script::Instruction::PushBytes(bytes) => {
115 bytes.as_bytes().is_ecdsa_signature(strict_der)
116 }
117 script::Instruction::Op(_) => false,
118 }
119 }
120 fn is_schnorr_signature(&self) -> bool {
121 match self {
122 script::Instruction::PushBytes(bytes) => bytes.as_bytes().is_schnorr_signature(),
123 script::Instruction::Op(_) => false,
124 }
125 }
126}
127
128impl Signature for [u8] {
129 fn is_ecdsa_signature(&self, strict_der: bool) -> bool {
130 self.to_vec().is_ecdsa_signature(strict_der)
131 }
132
133 fn is_schnorr_signature(&self) -> bool {
134 self.to_vec().is_schnorr_signature()
135 }
136}
137
138impl Signature for Vec<u8> {
139 fn is_ecdsa_signature(&self, strict_der: bool) -> bool {
140 if self.len() < ECDSA_SIG_MIN_LEN
141 || ((strict_der && self.len() > ECDSA_SIG_MAX_STRICT_DER_LEN)
142 || self.len() > ECDSA_SIG_MAX_LAX_DER_LEN)
143 {
144 false
145 } else {
146 let sighash_stripped = &self[..self.len() - 1];
147 if strict_der {
148 ecdsa::Signature::from_der(sighash_stripped).is_ok()
149 } else {
150 ecdsa::Signature::from_der_lax(sighash_stripped).is_ok()
151 }
152 }
153 }
154
155 fn is_schnorr_signature(&self) -> bool {
156 if self.len() == 64 {
157 return true;
159 } else if self.len() == 65 {
160 let sighash = self.last().unwrap();
161 return *sighash == 0x01u8
162 || *sighash == 0x02u8
163 || *sighash == 0x03u8
164 || *sighash == 0x81u8
165 || *sighash == 0x82u8
166 || *sighash == 0x83u8;
167 }
168 false
169 }
170}
171
172#[derive(PartialEq, Eq, Debug, Clone)]
173pub enum SignatureType {
174 Ecdsa(ecdsa::Signature),
175 Schnorr(schnorr::Signature),
176}
177
178#[derive(PartialEq, Eq, Debug, Clone)]
179pub enum DEREncoding {
180 NotApplicable,
183
184 Valid,
186
187 SigTooShort,
189 SigTooLong,
191 NoCompoundMarker,
193 InvalidCompoundLengthDescriptor,
195 NoSValueLengthDescriptor,
197 DescribedLengthMismatch,
200 RElementNotAnInteger,
202 RLengthIsZero,
204 NegativeRValue,
206 NullByteAtRValueStart,
208 SElementNotAnInteger,
210 SLengthIsZero,
212 NegativeSValue,
214 NullByteAtSValueStart,
216}
217
218pub fn is_strict_der_encoded_ecdsa_sig(sig: &[u8]) -> DEREncoding {
221 if sig.len() < 9 {
235 return DEREncoding::SigTooShort;
236 }
237 if sig.len() > 73 {
238 return DEREncoding::SigTooLong;
239 }
240
241 if sig[0] != 0x30 {
243 return DEREncoding::NoCompoundMarker;
244 }
245
246 if usize::from(sig[1]) != sig.len() - 3 {
248 return DEREncoding::InvalidCompoundLengthDescriptor;
249 }
250
251 let len_r: u8 = sig[3];
253
254 if usize::from(5 + len_r) >= sig.len() {
256 return DEREncoding::NoSValueLengthDescriptor;
257 }
258
259 let len_s: u8 = sig[usize::from(5 + len_r)];
261
262 if usize::from(len_r + len_s + 7) != sig.len() {
265 return DEREncoding::DescribedLengthMismatch;
266 }
267
268 if sig[2] != 0x02 {
270 return DEREncoding::RElementNotAnInteger;
271 }
272
273 if len_r == 0 {
275 return DEREncoding::RLengthIsZero;
276 }
277
278 if sig[4] & 0x80 > 0 {
280 return DEREncoding::NegativeRValue;
281 }
282
283 #[allow(clippy::nonminimal_bool)]
286 if len_r > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80 > 0) {
287 return DEREncoding::NullByteAtRValueStart;
288 }
289
290 if sig[usize::from(len_r + 4)] != 0x02 {
292 return DEREncoding::SElementNotAnInteger;
293 }
294
295 if len_s == 0 {
297 return DEREncoding::SLengthIsZero;
298 }
299
300 if sig[usize::from(len_r + 6)] & 0x80 > 0 {
302 return DEREncoding::NegativeSValue;
303 }
304
305 #[allow(clippy::nonminimal_bool)]
308 if len_s > 1
309 && (sig[usize::from(len_r + 6)] == 0x00)
310 && !(sig[usize::from(len_r + 7)] & 0x80 > 0)
311 {
312 return DEREncoding::NullByteAtSValueStart;
313 }
314
315 DEREncoding::Valid
316}
317
318#[derive(PartialEq, Eq, Debug, Clone)]
320pub struct SignatureInfo {
321 pub signature: SignatureType,
323 pub der_encoded: DEREncoding,
326 pub sig_hash: u8,
328 pub length: usize,
330}
331
332impl SignatureInfo {
333 pub fn low_s(&self) -> bool {
334 let compact: [u8; 64] = match self.signature {
335 SignatureType::Ecdsa(s) => s.serialize_compact(),
336 SignatureType::Schnorr(s) => *s.as_ref(),
337 };
338
339 let (_, s) = compact.split_at(32);
340 let s: [u8; 32] = s
341 .try_into()
342 .expect("Splitting a 64 byte array in half should procude two 32 byte arrays.");
343
344 s <= SECP256K1_HALF_CURVE_ORDER
345 }
346
347 pub fn low_r(&self) -> bool {
348 let compact: [u8; 64] = match self.signature {
349 SignatureType::Ecdsa(s) => s.serialize_compact(),
350 SignatureType::Schnorr(s) => *s.as_ref(),
351 };
352 let (r, _) = compact.split_at(32);
353 let r: [u8; 32] = r
354 .try_into()
355 .expect("Splitting a 64 byte array in half should procude two 32 byte arrays.");
356
357 r < LOW_R_THRESHOLD
358 }
359
360 pub fn from_instruction_ecdsa(instruction: &script::Instruction) -> Option<SignatureInfo> {
363 if instruction.is_ecdsa_signature(false) {
364 match instruction {
365 script::Instruction::PushBytes(bytes) => {
366 return SignatureInfo::from_u8_slice_ecdsa(bytes.as_bytes());
367 }
368 script::Instruction::Op(_) => return None,
369 }
370 }
371 None
372 }
373
374 pub fn from_instruction_schnorr(instruction: &script::Instruction) -> Option<SignatureInfo> {
377 if instruction.is_schnorr_signature() {
378 match instruction {
379 script::Instruction::PushBytes(bytes) => {
380 return SignatureInfo::from_u8_slice_schnorr(bytes.as_bytes());
381 }
382 script::Instruction::Op(_) => return None,
383 }
384 }
385 None
386 }
387
388 pub fn from_u8_slice_ecdsa(bytes: &[u8]) -> Option<SignatureInfo> {
391 if bytes.len() < ECDSA_SIG_MIN_LEN || bytes.len() > ECDSA_SIG_MAX_LAX_DER_LEN {
392 return None;
393 }
394
395 let sighash_stripped = &bytes[..bytes.len() - 1];
396 if let Ok(sig) = ecdsa::Signature::from_der(sighash_stripped) {
397 if is_strict_der_encoded_ecdsa_sig(bytes) == DEREncoding::Valid {
398 return Some(SignatureInfo {
399 signature: SignatureType::Ecdsa(sig),
400 sig_hash: *bytes.last().unwrap(),
401 length: bytes.len(),
402 der_encoded: DEREncoding::Valid,
403 });
404 }
405 }
406
407 if let Ok(sig) = ecdsa::Signature::from_der_lax(sighash_stripped) {
408 return Some(SignatureInfo {
409 signature: SignatureType::Ecdsa(sig),
410 sig_hash: *bytes.last().unwrap(),
411 length: bytes.len(),
412 der_encoded: is_strict_der_encoded_ecdsa_sig(bytes),
413 });
414 }
415
416 None
417 }
418
419 pub fn from_u8_slice_schnorr(bytes: &[u8]) -> Option<SignatureInfo> {
422 if bytes.to_vec().is_schnorr_signature() {
423 let sighash: u8;
424 let signature: schnorr::Signature = if bytes.len() == 64 {
425 sighash = 0x01u8;
426 match schnorr::Signature::from_slice(bytes) {
427 Ok(sig) => sig,
428 Err(_) => return None,
429 }
430 } else {
431 sighash = *bytes.last().unwrap();
432 match schnorr::Signature::from_slice(&bytes[..bytes.len() - 1]) {
433 Ok(sig) => sig,
434 Err(_) => return None,
435 }
436 };
437
438 return Some(SignatureInfo {
439 signature: SignatureType::Schnorr(signature),
440 sig_hash: sighash,
441 length: bytes.len(),
442 der_encoded: DEREncoding::NotApplicable,
443 });
444 }
445 None
446 }
447
448 pub fn all_from(input: &bitcoin::TxIn) -> Result<Vec<SignatureInfo>, InputError> {
452 let input_type = input.get_type()?;
453
454 let mut signature_infos = vec![];
455
456 match input_type {
457 InputType::P2pk | InputType::P2pkLaxDer => {
458 signature_infos.push(
462 SignatureInfo::from_u8_slice_ecdsa(
463 &input.script_sig.as_script().as_bytes()[1..],
464 )
465 .unwrap(),
466 );
467 }
468 InputType::P2ms | InputType::P2msLaxDer => {
469 let instructions = match instructions_as_vec(&input.script_sig) {
472 Ok(ins) => ins,
473 Err(e) => return Err(InputError::PubkeyInfo(e)),
474 };
475 for instruction in instructions[1..].iter() {
476 signature_infos
477 .push(SignatureInfo::from_instruction_ecdsa(instruction).unwrap());
478 }
479 }
480 InputType::P2pkh | InputType::P2pkhLaxDer => {
481 let instructions = match instructions_as_vec(&input.script_sig) {
484 Ok(ins) => ins,
485 Err(e) => return Err(InputError::PubkeyInfo(e)),
486 };
487 signature_infos
488 .push(SignatureInfo::from_instruction_ecdsa(&instructions[0]).unwrap());
489 }
490 InputType::P2shP2wpkh => {
491 signature_infos
494 .push(SignatureInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[0]).unwrap());
495 }
496 InputType::P2wpkh => {
497 signature_infos
500 .push(SignatureInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[0]).unwrap())
501 }
502 InputType::P2sh => {
503 let instructions = match instructions_as_vec(&input.script_sig) {
507 Ok(ins) => ins,
508 Err(e) => return Err(InputError::PubkeyInfo(e)),
509 };
510 for instruction in instructions[..instructions.len() - 1].iter() {
511 if let Some(signature_info) = SignatureInfo::from_instruction_ecdsa(instruction)
512 {
513 signature_infos.push(signature_info);
514 }
515 }
516 }
517 InputType::P2shP2wsh => {
518 for bytes in input.witness.to_vec()[..input.witness.len() - 1].iter() {
522 if let Some(signature_info) = SignatureInfo::from_u8_slice_ecdsa(bytes) {
523 signature_infos.push(signature_info);
524 }
525 }
526 }
527 InputType::P2wsh => {
528 for bytes in input.witness.to_vec()[..input.witness.len() - 1].iter() {
532 if let Some(signature_info) = SignatureInfo::from_u8_slice_ecdsa(bytes) {
533 signature_infos.push(signature_info);
534 }
535 }
536 }
537 InputType::P2trkp => {
538 signature_infos
541 .push(SignatureInfo::from_u8_slice_schnorr(&input.witness.to_vec()[0]).unwrap())
542 }
543 InputType::P2trsp => {
544 for bytes in input.witness.to_vec()[..input.witness.len() - 2].iter() {
548 if let Some(signature_info) = SignatureInfo::from_u8_slice_schnorr(bytes) {
549 signature_infos.push(signature_info);
550 }
551 }
552 }
553 InputType::P2a => (),
556 InputType::Unknown => (),
557 InputType::Coinbase => (),
558 InputType::CoinbaseWitness => (),
559 }
560
561 Ok(signature_infos)
562 }
563}
564
565pub trait Multisig {
566 fn is_opcheckmultisig(&self) -> bool;
567 fn get_opcheckmultisig_n_m(&self) -> Result<Option<(u8, u8)>, script::Error>;
568}
569
570impl Multisig for bitcoin::Script {
571 fn is_opcheckmultisig(&self) -> bool {
574 if let Ok(res_option) = self.get_opcheckmultisig_n_m() {
575 if res_option.is_some() {
576 return true;
577 }
578 }
579 false
580 }
581
582 fn get_opcheckmultisig_n_m(&self) -> Result<Option<(u8, u8)>, script::Error> {
587 let script_bytes = self.to_bytes();
588
589 if script_bytes.is_empty() {
590 return Ok(None);
591 }
592
593 if let Some(last_byte) = script_bytes.last() {
594 if *(last_byte) != opcodes::OP_CHECKMULTISIG.to_u8() {
595 return Ok(None);
596 }
597 }
598
599 let instructions = instructions_as_vec(self)?;
601
602 if instructions.len() < 4 {
603 return Ok(None);
604 }
605
606 let n: u8; let m: u8; if let script::Instruction::Op(op) = instructions[0] {
610 if op.to_u8() >= opcodes::OP_PUSHNUM_1.to_u8()
611 && op.to_u8() <= opcodes::OP_PUSHNUM_16.to_u8()
612 {
613 n = (op.to_u8() - opcodes::OP_PUSHNUM_1.to_u8()) + 1;
614 } else {
615 return Ok(None);
616 }
617 } else {
618 return Ok(None);
619 }
620
621 if let script::Instruction::Op(op) = instructions[instructions.len() - 2] {
622 if op.to_u8() >= opcodes::OP_PUSHNUM_1.to_u8()
623 && op.to_u8() <= opcodes::OP_PUSHNUM_16.to_u8()
624 {
625 m = (op.to_u8() - opcodes::OP_PUSHNUM_1.to_u8()) + 1;
626 } else {
627 return Ok(None);
628 }
629 } else {
630 return Ok(None);
631 }
632
633 if instructions.len() - 2 - 1 != m as usize {
636 return Ok(None);
637 }
638
639 if !instructions[1..instructions.len() - 2]
643 .iter()
644 .any(|inst| inst.push_bytes().is_some())
645 {
646 return Ok(None);
647 }
648
649 if n > m {
653 return Ok(None);
654 }
655
656 Ok(Some((n, m)))
657 }
658}
659
660pub enum PubkeyError {
661 Script(script::Error),
662}
663
664#[derive(PartialEq, Eq, Debug, Clone)]
665pub enum PubkeyType {
666 ECDSA,
667 Schnorr,
668}
669
670#[derive(PartialEq, Eq, Debug, Clone)]
672pub struct PubKeyInfo {
673 pub compressed: bool,
675 pub pubkey_type: PubkeyType,
676}
677
678impl PubKeyInfo {
679 pub fn from_instruction_ecdsa(instruction: &script::Instruction) -> Option<PubKeyInfo> {
680 match instruction {
681 script::Instruction::PushBytes(bytes) => {
682 PubKeyInfo::from_u8_slice_ecdsa(bytes.as_bytes())
683 }
684 script::Instruction::Op(_) => None,
685 }
686 }
687
688 pub fn from_u8_slice_ecdsa(bytes: &[u8]) -> Option<PubKeyInfo> {
689 if bytes.is_ecdsa_pubkey() {
690 return Some(PubKeyInfo {
691 compressed: bytes.len() == bitcoin::key::constants::PUBLIC_KEY_SIZE,
692 pubkey_type: PubkeyType::ECDSA,
693 });
694 }
695 None
696 }
697
698 pub fn from_input(input: &bitcoin::TxIn) -> Result<Vec<PubKeyInfo>, InputError> {
699 let input_type = input.get_type()?;
700
701 let mut pubkey_infos = vec![];
702
703 match input_type {
704 InputType::P2pk | InputType::P2pkLaxDer => (),
706 InputType::P2ms | InputType::P2msLaxDer => (),
709 InputType::P2pkh | InputType::P2pkhLaxDer => {
710 if let Ok(instructions) = instructions_as_vec(&input.script_sig) {
713 pubkey_infos
714 .push(PubKeyInfo::from_instruction_ecdsa(&instructions[1]).unwrap());
715 }
716 }
717 InputType::P2shP2wpkh => {
718 pubkey_infos
721 .push(PubKeyInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[1]).unwrap());
722 }
723 InputType::P2wpkh => {
724 pubkey_infos
727 .push(PubKeyInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[1]).unwrap())
728 }
729 InputType::P2sh => {
730 if let Some(redeem_script) = input.redeem_script().unwrap() {
732 if let Ok(instructions) = instructions_as_vec(&redeem_script) {
734 for instruction in instructions.iter() {
735 if let Some(pubkey_info) =
736 PubKeyInfo::from_instruction_ecdsa(instruction)
737 {
738 pubkey_infos.push(pubkey_info);
739 }
740 }
741 }
742 }
743 }
744 InputType::P2shP2wsh => {
745 if let Some(redeem_script) = input.redeem_script().unwrap() {
747 if let Ok(instructions) = instructions_as_vec(&redeem_script) {
749 for instruction in instructions.iter() {
750 if let Some(pubkey_info) =
751 PubKeyInfo::from_instruction_ecdsa(instruction)
752 {
753 pubkey_infos.push(pubkey_info);
754 }
755 }
756 }
757 }
758 }
759 InputType::P2wsh => {
760 if let Some(redeem_script) = input.redeem_script().unwrap() {
762 if let Ok(instructions) = instructions_as_vec(&redeem_script) {
764 for instruction in instructions.iter() {
765 if let Some(pubkey_info) =
766 PubKeyInfo::from_instruction_ecdsa(instruction)
767 {
768 pubkey_infos.push(pubkey_info);
769 }
770 }
771 }
772 }
773 }
774 InputType::P2trkp => (),
776 InputType::P2trsp => (),
779 InputType::P2a => (),
782 InputType::Unknown => (),
783 InputType::Coinbase => (),
784 InputType::CoinbaseWitness => (),
785 }
786
787 Ok(pubkey_infos)
788 }
789
790 pub fn from_output(output: &bitcoin::TxOut) -> Result<Vec<PubKeyInfo>, OutputError> {
791 let output_type = output.get_type();
792
793 let mut pubkey_infos = vec![];
794
795 match output_type {
796 OutputType::P2pk => {
797 let instructions = match instructions_as_vec(&output.script_pubkey) {
798 Ok(ins) => ins,
799 Err(e) => return Err(OutputError::PubkeyInfo(e)),
800 };
801 if let Some(pk_info) = PubKeyInfo::from_instruction_ecdsa(&instructions[0]) {
802 pubkey_infos.push(pk_info);
803 }
804 }
805 OutputType::P2ms => {
806 let instructions = match instructions_as_vec(&output.script_pubkey) {
808 Ok(ins) => ins,
809 Err(e) => return Err(OutputError::PubkeyInfo(e)),
810 };
811 for instruction in instructions.iter() {
812 if let Some(pk_info) = PubKeyInfo::from_instruction_ecdsa(instruction) {
813 pubkey_infos.push(pk_info);
814 }
815 }
816 }
817 OutputType::P2tr => {
818 pubkey_infos.push(PubKeyInfo {
819 compressed: true,
820 pubkey_type: PubkeyType::Schnorr,
821 });
822 }
823 OutputType::P2pkh
824 | OutputType::P2wpkhV0
825 | OutputType::P2wshV0
826 | OutputType::P2sh
827 | OutputType::P2a
828 | OutputType::OpReturn(_)
829 | OutputType::Unknown => (),
830 }
831
832 Ok(pubkey_infos)
833 }
834}
835
836#[cfg(test)]
837mod tests {
838 use super::Multisig;
839 use crate::script::{DEREncoding, PubkeyType, PublicKey};
840 use crate::{input::InputInfo, output::OutputInfo, script::PubKeyInfo};
841 use bitcoin::{ScriptBuf, Transaction};
842
843 #[test]
844 fn test_input_pubkey_info() {
845 struct TestCase {
846 rawtx: Vec<u8>,
847 pkinfos_input: Vec<Vec<PubKeyInfo>>,
848 pkinfos_output: Vec<Vec<PubKeyInfo>>,
849 }
850
851 let testcases = vec![
852 TestCase{
853 rawtx: hex::decode("02000000000102ad4d1f661a4251621d23aa52abc9d21f3dabfac7de2d771a831a7263ebc47e14000000006a473044022066fa04395f3d595a45a7687ab4e0260035a4796f7f6f974046ca6628a275b54902206a5183122336252cc4aa63e54030d4f3f5eb12965889f8f9929cc36ca11bab0601210201973a41bb610e199a1fbb208398fbacc732c3658d132dbf9f3a394bedc9a7b5ffffffff028b130336c335a1049fde9115470610f00ec6e7199c8f1aa32dfcc7087faa730100000000ffffffff0222020000000000001976a914234f39022fa4372c0e41f484a8f80644094fc75088acd442030000000000225120aed85526197b4cc9913aa2715b52001131a83eadbdadd2d6bdd0d303362539bc000140d30ee1b9be7f27630c93056b13faa5745e73e9642d8e3d41fa3f37cd715901eef9a00d5df17752d80c3c2388e1a1246aa398c91f347f0517f219e66548aa2b8500000000").unwrap(),
858 pkinfos_input: vec![vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![]],
859 pkinfos_output: vec![vec![], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::Schnorr }]],
860 },
861
862 TestCase{
863 rawtx: hex::decode("01000000014563f26698c0ea3ebd85d4767457370d7e2ebbe922a7736dbf70e1d0f8a9aa9c000000008a473044022039294d5c8843a6776d4a2032cf03549f41c634ba5e65898c7816973919e485b902205af1f61f6d7d6a5f32cbe46676303c141fe499288b1be0d8f0c4e80d4c0ecb5701410454ffbc96ef3c26acffa431066915308865d990e044c507e0ab3d26af34a8ba5b4cb3028fe7c91926bb8be47d652dc70ab300e3022f8259db5f79306b601fc66effffffff0190c9190000000000c9524104d81fd577272bbe73308c93009eec5dc9fc319fc1ee2e7066e17220a5d47a18314578be2faea34b9f1f8ca078f8621acd4bc22897b03daa422b9bf56646b342a24104ec3afff0b2b66e8152e9018fe3be3fc92b30bf886b3487a525997d00fd9da2d012dce5d5275854adc3106572a5d1e12d4211b228429f5a7b2f7ba92eb0475bb14104b49b496684b02855bc32f5daefa2e2e406db4418f3b86bca5195600951c7d918cdbe5e6d3736ec2abf2dd7610995c3086976b2c0c7b4e459d10b34a316d5a5e753ae00000000").unwrap(),
868 pkinfos_input: vec![vec![PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }]],
869 pkinfos_output: vec![vec![PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }]],
870 },
871
872 TestCase{
873 rawtx: hex::decode("010000000337bd40a022eea1edd40a678cddabe200b131afd5797b232ac21861d8e97eb367020000008a4730440220e8343f8ac7e96582d92a450ce314668db4f7a0e2c94a97aa6df026f93ebee2290220866b5728d4247688d91b4a30144762bc8bfd7f385de7f7d326d665ff5e3e900301410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342afffffffff96420befb14a9357181e5da089824a3e6ea5a95856ff74c06c7d5ea98d633cf9020000008a4730440220b7227a8f816f3810f97057102edf8be4434c1e00f48b4440976bcc478f1431030220af3cba150afdd44618de4369cdc65fea73e447d7b5fbe135d2f08f86d82aa85f01410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342afffffffff96420befb14a9357181e5da089824a3e6ea5a95856ff74c06c7d5ea98d633cf9010000008a47304402207d689e1a61e06440eab18d961517a97c49219a91f2c59d9630e902fcb2f4ea8b0220dcd274349ca264d8bd2bee5135664a92899e94a319a349d6d6e3660d04b564ad0141047a4c5d104002ebc203bef5cab6f13ff57ab624bb5f9f1186beb64c83a396da0d912e11a18ea15a2c784a62fed2bbd8258c3413c18bf4c3f2ba28f3d5565e328bffffffff0340420f000000000087514104cc71eb30d653c0c3163990c47b976f3fb3f37cccdcbedb169a1dfef58bbfbfaff7d8a473e7e2e6d317b87bafe8bde97e3cf8f065dec022b51d11fcdd0d348ac4410461cbdcc5409fb4b4d42b51d33381354d80e550078cb532a34bfa2fcfdeb7d76519aecc62770f5b0e4ef8551946d8a540911abe3e7854a26f39f58b25c15342af52ae50cec402000000001976a914c812a297b8e0e778d7a22bb2cd6d23c3e789472b88ac20a10700000000001976a914641ad5051edd97029a003fe9efb29359fcee409d88ac00000000").unwrap(),
879 pkinfos_input: vec![vec![PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }], vec![PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }], vec![PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }]],
880 pkinfos_output: vec![vec![PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }], vec![], vec![]],
881 },
882
883 TestCase{
884 rawtx: hex::decode("01000000000101ad2bb91208eef398def3ed3e784d9ee9b7befeb56a3053c3561849b88bc4cedf0000000000ffffffff037a3e0100000000001600148d7a0a3461e3891723e5fdf8129caa0075060cff7a3e0100000000001600148d7a0a3461e3891723e5fdf8129caa0075060cff0000000000000000256a2342697462616e6b20496e632e204a6170616e20737570706f727473205365675769742102483045022100a6e33a7aff720ba9f33a0a8346a16fdd022196862796d511d31978c40c9ad48b02206fb8f67bd699a8c952b3386a81d122c366d2d36cd08e2de21207e6aa6f96ce9501210283409659355b6d1cc3c32decd5d561abaac86c37a353b52895a5e6c196d6f44800000000").unwrap(),
889 pkinfos_input: vec![vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }]],
890 pkinfos_output: vec![vec![], vec![], vec![]],
891 },
892
893 TestCase{
894 rawtx: hex::decode("0100000000010a9b9653ae4536f14723f5e7fdd730af3e41536a0c57807de8d67b1d2c96246ad40000000048473044022004f027ae0b19bb7a7aa8fcdf135f1da769d087342020359ef4099a9f0f0ba4ec02206a83a9b78df3fed89a3b6052e69963e1fb08d8f6d17d945e43b51b5214aa41e601f78c3201e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb000000006946304302204dc2939be89ab6626457fff40aec2cc4e6213e64bcb4d2c43bf6b49358ff638c021f33d2f8fdf6d54a2c82bb7cddc62becc2cbbaca6fd7f3ec927ea975f29ad8510221028b98707adfd6f468d56c1a6067a6f0c7fef43afbacad45384017f8be93a18d4087693201e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb010000008300453042021e4f6ff73d7b304a5cbf3bb7738abb5f81a4af6335962134ce27a1cc45fec702201b95e3acb7db93257b20651cdcb79af66bf0bb86a8ae5b4e0a5df4e3f86787e2033b303802153b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63021f34793e2878497561e7616291ebdda3024b681cdacc8b863b5b0804cd30c2a481685e2d01d6ca61cbfb1bab46294999b8f5650861d71bb701c8cc101807cb9a5933cbe14500000000fdaa0100443041021d1313459a48bd1d0628eec635495f793e970729684394f9b814d2b24012022050be6d9918444e283da0136884f8311ec465d0fed2f8d24b75a8485ebdc13aea013a303702153b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63021e78644ba72eab69fefb5fe50700671bfb91dda699f72ffbb325edc6a3c4ef8239303602153b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63021d2c2db104e70720c39af43b6ba3edd930c26e0818aa59ff9c886281d8ba834ced532103e0a220d36f6f7ed5f3f58c279d055707c454135baf18fd00d798fec3cb52dfbc2103cf689db9313b9f7fc0b984dd9cac750be76041b392919b06f6bf94813da34cd421027f8af2eb6e904deddaa60d5af393d430575eb35e4dfd942a8a5882734b078906410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a34104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c55ae10000000e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb02000000171600149b27f072e4b972927c445d1946162a550b0914d88d000000e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb0300000023220020a18160de7291554f349c7d5cbee4ab97fb542e94cf302ce8d7e9747e4188ca75efbeaddee36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb0400000000406f400101d08b572974320b3d3650d672b1e53776ee38c72df9812bb2b18f8a92b37d630000000000d9b4bef9412c98a33b0d9ae72b87c117287b5722ffeb89d1ac5baeeb625012df02db2000000000000055010000b4061297f91ef316f4ed912e45f6d48f97d317d02d8d1c4bb131c4f9ec41577900000000005601000009400200000000000023210261542eb020b36c1da48e2e607b90a8c1f2ccdbd06eaf5fb4bb0d7cc34293d32aac22020000000000001976a9140240539af6c68431e4ce9cc5ef464f12c1741b3c88ac4602000000000000255121028b45a50f795be0413680036665d17a3eca099648ea80637bc3a70a7d2b52ae2851ae1c0200000000000017a91449ed2c96e33b6134408af8484508bcc3248c8dbd872601000000000000160014c8e51cf6891c0a2101aecea8cd5ce9bbbfaf7bba4a01000000000000220020c485bbb80c4be276e77eac3a983a391cc8b1a1b5f160995a36c3dff18296385a4a01000000000000225120a7a42b268957a06c9de4d7260f1df392ce4d6e7b743f5adc27415ce2afceb3b9f0000000000000000451024e730000000000000000356a224e6f7420796f757220696e707574732c206e6f7420796f7572206f7574707574732e005152535455565758595a5b5c5d5e5f600000000002433040021c23902a01d4c5cff2c33c8bdb778a5aadea78a9a0d6d4db60aaa0fba1022069237d9dbf2db8cff9c260ba71250493682d01a746f4a45c5c7ea386e56d2bc902210240187acd3e2fd3d8e1acffefa85907b6550730c24f78dfd3301c829fc4daf3cc0342303f021c65aee6696e80be6e14545cfd64b44f17b0514c150eefdb090c0f0bd9021f3fef4aa95c252a225622aba99e4d5af5a6fe40d177acd593e64cf2f8557ccc032103b55c6f0749e0f3e2caeca05f68e3699f1b3c62a550730f704985a6a9aae437a18576a914db865fd920959506111079995f1e4017b489bfe38763ac6721024d560f7f5d28aae5e1a8aa2b7ba615d7fc48e4ea27e5d27336e6a8f5fa0f5c8c7c820120876475527c2103443e8834fa7d79d7b5e95e0e9d0847f6b03ac3ea977979858b4104947fca87ca52ae67a91446c3747322b220fdb925c9802f0e949c1feab99988ac68680241303e021c11f60486afd0f5d6573603fb2076ef2f676455b92ada257d2f25558a021e317719c946f951d49bf4df4285a618629cd9e554fcbf787c319a0c4dd2260121032467f24cc31664f0cf34ff8d5cbb590888ddc1dcfec724a32ae3dd5338b8508e0340303d021c32f9454db85cb1a4ca63a9883d4347c5e13f3654e884ae44e9efa3c8021d62f07fe452c06b084bc3e09afd3aac4039136549a465533bc1ca6696790201014c632102fd6db4de50399b2aa086edb23f8e140bbc823d6651e024a0eb871288068789cd67012ab27521034134a2bb35c3f83dab2489d96160741888b8b5589bb694dea6e7bc24486e9c6f68ac0140d822f203827852998cad370232e8c57294540a5da51107fa26cf466bdd2b8b0b3d161999cc80aed8de7386a2bd5d5313aea159a231cc26fa53aaa702b7fa21ed0940fe6eb715dceffefc067fdc787d250a9a9116682d216f6356ea38fc1f112bd74995faa90315e81981d2c2260b7eaca3c41a16b280362980f0d8faf4c05ebb82c541e34ad0ad33885a473831f8ba8d9339123cb19d0e642e156d8e0d6e2ab2691aedb30e55a35637a806927225e1aa72223d41e59f92c6579b819e7d331a7ada9d2e01412a4861fb4cb951c791bf6c93859ef65abccd90034f91b9b77abb918e13b6fce75d5fa3e2d2f6eeeae105315178c2cb9db2ef238fe89b282f691c06db43bc71ca0241fc97bb2be673c3bf388aaf58178ef14d354caf83c92aca8ef1831d619b8511e928f4f5fdea3962067b11e7cecfe094cd0f66a4ea9af9ec836d70d18f2b37df028141a5781a0adaa80ab7f7f164172dd1a1cb127e523daa0d6949aba074a15c589f12dfb8183182afec9230cb7947b7422a4abc1bb78173550d66274ea19f6c9dd92c820000f0205f4237bd7dae576b34abc8a9c6fa4f0e4787c04234ca963e9e96c8f9b67b56d1ac205f4237bd7f93c69403a30c6b641f27ccf5201090152fcf1596474221307831c3ba205ac8ff25ce63564963d1148b84627f614af1f3c77d7caa23adc61264fa5e4996ba20b210c83e6f5b3f866837112d023d9ae8da2a6412168d54968ab87860ab970690ba20d3ee3b7a8b8149122b3c886330b3241538ba4b935c4040f4a73ddab917241bc5ba20cdfabb9d0e5c8f09a83f19e36e100d8f5e882f1b60aa60dacd9e6d072c117bc0ba20aab038c238e95fb54cdd0a6705dc1b1f8d135a9e9b20ab9c7ff96eef0e9bf545ba559cfdc102c0b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f5534a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33bf4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e166f7cf9580f1c2dfb3c4d5d043cdbb128c640e3f20161245aa7372e9666168516a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48dd5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb46829a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713d29c9c0e8e4d2a9790922af73f0b8d51f0bd4bb19940d9cf910ead8fbe85bc9bbb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e089fdbcf0ef9d8d00f66e47917f67cc5d78aec1ac786e2abb8d2facb4e4790aad6cc455ae816e6cdafdb58d54e35d4f46d860047458eacf1c7405dc634631c570d8d31992805518fd62daa3bdd2a5c4fd2cd3054c9b3dca1d78055e9528cff6adc8f907925d2ebe48765103e6845c06f1f2bb77c6adc1cc002865865eb5cfd5c1cb10c007c60e14f9d087e0291d4d0c7869697c6681d979c6639dbd960792b4d4133e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac9879903637777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8fd456524104a6674693c29946543f8a0befccce5a352bda55ec8559fc630f5f37393096d97bfee8660f4100ffd61874d62f9a65de9fb6acf740c4c386990ef7373be398c4bdc43709db7398106609eea2a7841aaf3a4fa2000dc18184faa2a7eb5a2af5845a8d3796308ff9840e567b14cf6bb158ff26c999e6f9a1f5448f9aa29ab5f49").unwrap(),
897 pkinfos_input: vec![
898 vec![], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![], vec![ PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA },PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: false, pubkey_type: PubkeyType::ECDSA }],
903 vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![], vec![] ],
910 pkinfos_output: vec![
911 vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![], vec![], vec![], vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::Schnorr }], vec![], vec![], ],
921 },
922
923 TestCase{
924 rawtx: hex::decode("01000000000101cd49818bb48793b0f5a2445517f64665805dd934d5bb5fe3f2443db18c3f63450000000000ffffffff02a00f000000000000a95141044a6572656d6961682032390d0a0d0a313120466f722049206b6e6f77207468652074686f756768747320746861742049207468696e6b20746f7761726420796f4104752c20736169746820746865204c6f72642c2074686f7567687473206f662070656163652c20616e64206e6f74206f66206576696c2c20746f206769766520792103c0e0bf0bbcdc53be9542359aeb1dde7c6289743b7b3460c12e2d57a478c6e48953aef7300f00000000001976a9148c51ed42f050b1bde974fb6649e25b782d168f4088ac0247304402206b41bae6dcec9129276d3df71e7d1f1f41097b338111a8932e034ca8747fdfaa0220447ab24f6d8f66fc0cc5a8a8c61928b63469bb28d6f51b7fc55f987509c57460012103c0e0bf0bbcdc53be9542359aeb1dde7c6289743b7b3460c12e2d57a478c6e48900000000").unwrap(),
927 pkinfos_input: vec![
928 vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], ],
930 pkinfos_output: vec![
931 vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], vec![], ],
934 },
935
936 ];
937
938 for testcase in testcases {
939 let tx: Transaction = bitcoin::consensus::deserialize(&testcase.rawtx).unwrap();
940 assert_eq!(testcase.pkinfos_input.len(), tx.input.len());
941 for (input, expected) in tx.input.iter().zip(testcase.pkinfos_input) {
942 let input_info = InputInfo::new(input).unwrap();
943 println!("-- info: {:?}\n\n", input_info);
944 assert_eq!(input_info.pubkey_stats, expected);
945 }
946 assert_eq!(testcase.pkinfos_output.len(), tx.output.len());
947 for (output, expected) in tx.output.iter().zip(testcase.pkinfos_output) {
948 let output_info = OutputInfo::new(output).unwrap();
949 println!("-- info: {:?}\n\n", output_info);
950 assert_eq!(output_info.pubkey_stats, expected);
951 }
952 }
953 }
954
955 #[test]
956 fn test_is_ecdsa_pubkey() {
957 struct Testcase {
958 raw: Vec<u8>,
959 expected: bool,
960 }
961
962 let testcases = vec![
963 Testcase { raw: hex::decode("044a6572656d6961682032390d0a0d0a313120466f722049206b6e6f77207468652074686f756768747320746861742049207468696e6b20746f7761726420796f").unwrap(),
965 expected: false,
966 },
967 Testcase { raw: hex::decode("04752c20736169746820746865204c6f72642c2074686f7567687473206f662070656163652c20616e64206e6f74206f66206576696c2c20746f20676976652079").unwrap(),
969 expected: false,
970 },
971 Testcase { raw: hex::decode("03c0e0bf0bbcdc53be9542359aeb1dde7c6289743b7b3460c12e2d57a478c6e489").unwrap(),
973 expected: true,
974 },
975 Testcase { raw: hex::decode("04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c").unwrap(),
977 expected: true,
978 },
979 Testcase { raw: hex::decode("0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3").unwrap(),
981 expected: true,
982 },
983 ];
984
985 for testcase in testcases {
986 assert_eq!(testcase.raw.is_pubkey(), testcase.expected);
987 }
988 }
989
990 #[test]
991 fn multisig_opcheckmultisig_2of2() {
992 let redeem_script_ms_2of2 = ScriptBuf::from_hex("522103a2ea7e0b94c48fd799bf123c1f19b50fb6d15da310db8223fd7a6afd8b03e6932102eba627e6ea5bb7e0f4c981596872d0a97d800fb836b5b3a585c3f2b99c77a0e552ae").unwrap();
994 assert_eq!(
995 redeem_script_ms_2of2.get_opcheckmultisig_n_m(),
996 Ok(Some((2, 2)))
997 )
998 }
999
1000 #[test]
1001 fn multisig_opcheckmultisig_1of3() {
1002 let p2ms_output_1of3 = ScriptBuf::from_hex("512102d7f69a1fc373a72468ae84634d9949fdeab4d1c903c6f23a3465f79c889342a421028836687b0c942c94801ce11b2601cbb1e900e6544ef28369e69977195794d47b2102dc6546ba58b9bc26365357a428516d48c9bbc230dd6fc72912654aaad460ef1953ae").unwrap();
1004 assert_eq!(p2ms_output_1of3.get_opcheckmultisig_n_m(), Ok(Some((1, 3))))
1005 }
1006
1007 #[test]
1008 fn multisig_opcheckmultisig_1of3_2() {
1009 let p2ms_output_1of3 = ScriptBuf::from_hex("5121027f86d68a007dc5c214c67f964350136c69fa5c783f70b9e7e13935b3f4a1c60e21032e6d71977d2685a41eecdfc260c2463903bef6cc83eaaa77555d90ea7ba09e7a2103030303030303030303030303030303030303030303030303030303030303030353ae").unwrap();
1011 assert_eq!(p2ms_output_1of3.get_opcheckmultisig_n_m(), Ok(Some((1, 3))))
1012 }
1013
1014 #[test]
1015 fn multisig_opcheckmultisig_4of5() {
1016 let redeem_script_ms_2of2 = ScriptBuf::from_hex("542102916d4144e950066e729b6142e6b0e24edeed8203303113c71bdf0fc8e1daad1e210236a7c19695857bacd26921ba932a287bfdc622498296166cd6ff5488525abf782103db287a99a4d208dd912e366494b1828c0046fd76cb2277f4a3abf4b43d9d6f6921034597594080142f4492f5f39a01c2ee203e1d9efedfebbccf77d0d5c6f54b92202102003af4953da0b10f848ce81c9564ff7cbe289fc9beda4e70d66176c12ec622e255ae").unwrap();
1018 assert_eq!(
1019 redeem_script_ms_2of2.get_opcheckmultisig_n_m(),
1020 Ok(Some((4, 5)))
1021 )
1022 }
1023
1024 #[test]
1025 fn multisig_opcheckmultisig_non_multiscript_sig() {
1026 let redeem_script_non_multisig = ScriptBuf::from_hex("6382012088a820697ce4e1d91cdc96d53d1bc591367bd48855a076301972842aa5ffcb8fcb8b618876a9142c3a8a495c16839d5d975c1ca0ee504af825b52188ac6704c9191960b17576a9145a59f40f4ecb1f86efb13752b600ea3b8d4c633988ac68").unwrap();
1027 assert_eq!(
1028 redeem_script_non_multisig.get_opcheckmultisig_n_m(),
1029 Ok(None)
1030 )
1031 }
1032
1033 #[test]
1034 fn multisig_opcheckmultisig_broken_2of3() {
1035 let redeem_script_non_multisig = ScriptBuf::from_hex("522103a2ea7e0b94c48fd799bf123c1f19b50fb6d15da310db8223fd7a6afd8b03e6932102eba627e6ea5bb7e0f4c981596872d0a97d800fb836b5b3a585c3f2b99c77a0e553ae").unwrap();
1037 assert_eq!(
1038 redeem_script_non_multisig.get_opcheckmultisig_n_m(),
1039 Ok(None)
1040 )
1041 }
1042
1043 #[test]
1044 fn multisig_opcheckmultisig_broken_2of1() {
1045 let redeem_script_non_multisig = ScriptBuf::from_hex(
1047 "5221021f5e6d618cf1beb74c79f42b0aae796094b3112bbe003209a2c1f757f1215bfd51ae",
1048 )
1049 .unwrap();
1050 assert_eq!(
1051 redeem_script_non_multisig.get_opcheckmultisig_n_m(),
1052 Ok(None)
1053 )
1054 }
1055
1056 #[test]
1057 fn multisig_opcheckmultisig_invalid_script() {
1058 let redeem_script_non_multisig = ScriptBuf::from_hex("b10cabcdae").unwrap();
1059 assert!(redeem_script_non_multisig
1060 .get_opcheckmultisig_n_m()
1061 .is_err())
1062 }
1063
1064 pub struct SignatureInfoTestcase {
1065 pub sig: String,
1066 pub length: usize,
1067 pub sighash: u8,
1068 pub low_r: bool,
1069 pub low_s: bool,
1070 pub der_encoded: DEREncoding,
1071 }
1072
1073 #[test]
1074 fn schnorr_signature_info_test() {
1075 use super::Signature;
1076 use super::SignatureInfo;
1077
1078 let testcases = vec![
1079 SignatureInfoTestcase {
1081 sig: "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0".to_string(),
1082 length: 64,
1083 sighash: 0x01,
1084 low_s: true,
1085 low_r: false,
1086 der_encoded: DEREncoding::NotApplicable,
1087 },
1088 SignatureInfoTestcase {
1090 sig: "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C001".to_string(),
1091 length: 65,
1092 sighash: 0x01,
1093 low_s: true,
1094 low_r: false,
1095 der_encoded: DEREncoding::NotApplicable,
1096 },
1097 SignatureInfoTestcase {
1099 sig: "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C002".to_string(),
1100 length: 65,
1101 sighash: 0x02,
1102 low_s: true,
1103 low_r: false,
1104 der_encoded: DEREncoding::NotApplicable,
1105 },
1106 SignatureInfoTestcase {
1108 sig: "80000000AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA67fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0".to_string(),
1109 length: 64,
1110 sighash: 0x01,
1111 low_s: true,
1112 low_r: false,
1113 der_encoded: DEREncoding::NotApplicable,
1114 },
1115 SignatureInfoTestcase {
1117 sig: "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1".to_string(),
1118 length: 64,
1119 sighash: 0x01,
1120 low_s: false,
1121 low_r: true,
1122 der_encoded: DEREncoding::NotApplicable,
1123 },
1124 ];
1125
1126 for testcase in testcases.iter() {
1127 let s = ScriptBuf::from_hex(&testcase.sig).unwrap().into_bytes();
1128 assert!(s.is_schnorr_signature());
1129 assert!(!s.is_ecdsa_signature(false));
1130 let si = SignatureInfo::from_u8_slice_schnorr(&s).unwrap();
1131
1132 println!("test signature: {}", testcase.sig);
1133 assert_eq!(si.der_encoded, DEREncoding::NotApplicable);
1134 assert_eq!(si.length, testcase.length, "");
1135 assert_eq!(si.sig_hash, testcase.sighash, "sighash flag");
1136 assert_eq!(si.low_s(), testcase.low_s, "low s?");
1137 assert_eq!(si.low_r(), testcase.low_r, "low r?");
1138 }
1139 }
1140
1141 #[test]
1142 fn ecdsa_signature_info_test() {
1143 use super::Signature;
1144 use super::SignatureInfo;
1145
1146 let testcases = vec![
1147 SignatureInfoTestcase {
1149 sig: "30440220d7a0417c3f6d1a15094d1cf2a3378ca0503eb8a57630953a9e2987e21ddd0a6502207a6266d686c99090920249991d3d42065b6d43eb70187b219c0db82e4f94d1a201".to_string(),
1150 length: 71,
1151 sighash: 0x01,
1152 low_s: true,
1153 low_r: false,
1154 der_encoded: DEREncoding::NegativeRValue,
1155 },
1156 SignatureInfoTestcase {
1157 sig: "30440220cad9530d55219cf16ed352385961288fb50f162f791dce23aafef91ede284fe60220a890a5608f42a2ece4c9e6d078f94ba7f93ff9f978ae4373abe97f1bd6c6af3201".to_string(),
1158 length: 71,
1159 sighash: 0x01,
1160 low_s: false,
1161 low_r: false,
1162 der_encoded: DEREncoding::NegativeRValue,
1163 },
1164 SignatureInfoTestcase {
1166 sig: "304502200064ddabf1af28c21103cf61cf19dbef814aff2eba0440c5e5e20a605d16d780022100f45c4bc6a4ab317dc3a600129fc6a87a0df6329dbc71c5fcca9effdb30f1857901".to_string(),
1167 length: 72,
1168 sighash: 0x01,
1169 low_s: false,
1170 low_r: true,
1171 der_encoded: DEREncoding::NullByteAtRValueStart,
1172 },
1173 SignatureInfoTestcase {
1175 sig: "3046022100eb232172f28bc933f8bd0b5c40c83f98d01792ed45c4832a887d4e95bff3322a022100f4d7ee1d3b8f71995f197ffcdd4e5b2327a735c5edca12724502051f33cb18c081".to_string(),
1176 length: 73,
1177 sighash: 0x81,
1178 low_s: false,
1179 low_r: false,
1180 der_encoded: DEREncoding::Valid,
1181 },
1182 SignatureInfoTestcase {
1184 sig: "3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db501".to_string(),
1185 length: 75,
1186 sighash: 0x01,
1187 low_s: true,
1188 low_r: true,
1189 der_encoded: DEREncoding::SigTooLong,
1190 },
1191 SignatureInfoTestcase {
1193 sig: "304502210099d6f5897eec6f2c4aeb3ccb43dc19f45f4a43372fd68a9e835bec463159e6620220365d554d87d656907af6a9c98768900c7e8cfbd3352d108c48d383cd6b08f6a02a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a01".to_string(),
1194 length: 123,
1195 sighash: 0x01,
1196 low_s: true,
1197 low_r: false,
1198 der_encoded: DEREncoding::SigTooLong,
1199 },
1200 SignatureInfoTestcase {
1202 sig: "3044022036f7dd9863dc3a42447a28533009530a5090b24f4d71f64248def0c3bd2ba0a40220a17d437e715e5e4eb4703b5477df94b88e5eb3d7fb1704f7a035eba6b25e617501".to_string(),
1203 length: 71,
1204 sighash: 0x01,
1205 low_s: false,
1206 low_r: true,
1207 der_encoded: DEREncoding::NegativeSValue,
1208 },
1209 ];
1210
1211 for testcase in testcases.iter() {
1212 println!("Testcase: {}", testcase.sig);
1213 let s = ScriptBuf::from_hex(&testcase.sig).unwrap().into_bytes();
1214 let si = SignatureInfo::from_u8_slice_ecdsa(&s).unwrap();
1215
1216 assert_eq!(si.der_encoded, testcase.der_encoded, "der encoded?");
1217 assert_eq!(si.length, testcase.length, "length");
1218 assert_eq!(si.sig_hash, testcase.sighash, "sighash flag");
1219 assert_eq!(si.low_s(), testcase.low_s, "low s?");
1220 assert_eq!(si.low_r(), testcase.low_r, "low r?");
1221
1222 assert!(s.is_ecdsa_signature(testcase.der_encoded == DEREncoding::Valid));
1223 assert!(!s.is_schnorr_signature());
1224 }
1225 }
1226}