rawtx_rs/
script.rs

1//! Information about Bitcoin PubKeys, Signatures and MultiSig constructs.
2
3use 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;
20// Longest I've seen on mainnet.
21const ECDSA_SIG_MAX_LAX_DER_LEN: usize = 123;
22const ECDSA_SIG_MAX_STRICT_DER_LEN: usize = 73;
23
24// Helper function collecting the Instructions iterator into a
25// `Vec<script::Instruction>` for easier handling.
26pub 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        // ECDSA Public keys should either be 33 bytes or 65 bytes long
61        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        // Schnorr public keys should be 32 byte long
71        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        // ECDSA Public keys should either be 33 bytes or 65 bytes long
81        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        // Schnorr public keys should be 32 byte long
91        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    /// Checks if the underlying bytes represent a Bitcoin ECDSA signature.
103    /// This function expects that the SigHash is included.
104    fn is_ecdsa_signature(&self, strict_der: bool) -> bool;
105
106    /// Checks if the underlying bytes represent a Bitcoin Schnoor signature.
107    /// This function expects that the SigHash is included.
108    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            // As long as we see excatly 64 bytes here, we assume it's a Schnoor signature.
158            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    /// The signature is not meant to be DER encoded. This is the case for
181    /// e.g. Schnorr signatures.
182    NotApplicable,
183
184    /// The ECDSA signature is DER encoded.
185    Valid,
186
187    /// The ECDSA signature is too short to be DER encoded.
188    SigTooShort,
189    /// The ECDSA signature is too long to be DER encoded.
190    SigTooLong,
191    /// The ECDSA signature does not have a compound marker.
192    NoCompoundMarker,
193    /// The compound length descriptor does not match the signature length.
194    InvalidCompoundLengthDescriptor,
195    /// The ECDSA signature does not have a S-value length descriptor.
196    NoSValueLengthDescriptor,
197    /// The encoded length of the r + S-value + markers + descriptors doesn't match the signature
198    /// length.
199    DescribedLengthMismatch,
200    /// The R element marker does not indicate an integer.
201    RElementNotAnInteger,
202    /// The R element length is zero.
203    RLengthIsZero,
204    /// The R element is negative.
205    NegativeRValue,
206    /// The R element starts with a NULL byte.
207    NullByteAtRValueStart,
208    /// The S element marker does not indicate an integer.
209    SElementNotAnInteger,
210    /// The S element length is zero.
211    SLengthIsZero,
212    /// The S element is negative.
213    NegativeSValue,
214    /// The S element starts with a NULL byte.
215    NullByteAtSValueStart,
216}
217
218/// Checks if an ECDSA signature (with a sighash flag) is DER encoded.
219/// Based on https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki#der-encoding-reference
220pub fn is_strict_der_encoded_ecdsa_sig(sig: &[u8]) -> DEREncoding {
221    // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
222    // * total-length: 1-byte length descriptor of everything that follows,
223    //   excluding the sighash byte.
224    // * R-length: 1-byte length descriptor of the R value that follows.
225    // * R: arbitrary-length big-endian encoded R value. It must use the shortest
226    //   possible encoding for a positive integers (which means no null bytes at
227    //   the start, except a single one when the next byte has its highest bit set).
228    // * S-length: 1-byte length descriptor of the S value that follows.
229    // * S: arbitrary-length big-endian encoded S value. The same rules apply.
230    // * sighash: 1-byte value indicating what data is hashed (not part of the DER
231    //   signature)
232
233    // Minimum and maximum size constraints.
234    if sig.len() < 9 {
235        return DEREncoding::SigTooShort;
236    }
237    if sig.len() > 73 {
238        return DEREncoding::SigTooLong;
239    }
240
241    // A signature is of type 0x30 (compound).
242    if sig[0] != 0x30 {
243        return DEREncoding::NoCompoundMarker;
244    }
245
246    // Make sure the length covers the entire signature.
247    if usize::from(sig[1]) != sig.len() - 3 {
248        return DEREncoding::InvalidCompoundLengthDescriptor;
249    }
250
251    // Extract the length of the R element.
252    let len_r: u8 = sig[3];
253
254    // Make sure the length of the S element is still inside the signature.
255    if usize::from(5 + len_r) >= sig.len() {
256        return DEREncoding::NoSValueLengthDescriptor;
257    }
258
259    // Extract the length of the S element.
260    let len_s: u8 = sig[usize::from(5 + len_r)];
261
262    // Verify that the length of the signature matches the sum of the length
263    // of the elements.
264    if usize::from(len_r + len_s + 7) != sig.len() {
265        return DEREncoding::DescribedLengthMismatch;
266    }
267
268    // Check whether the R element is an integer.
269    if sig[2] != 0x02 {
270        return DEREncoding::RElementNotAnInteger;
271    }
272
273    // Zero-length integers are not allowed for R.
274    if len_r == 0 {
275        return DEREncoding::RLengthIsZero;
276    }
277
278    // Negative numbers are not allowed for R.
279    if sig[4] & 0x80 > 0 {
280        return DEREncoding::NegativeRValue;
281    }
282
283    // Null bytes at the start of R are not allowed, unless R would
284    // otherwise be interpreted as a negative number.
285    #[allow(clippy::nonminimal_bool)]
286    if len_r > 1 && (sig[4] == 0x00) && !(sig[5] & 0x80 > 0) {
287        return DEREncoding::NullByteAtRValueStart;
288    }
289
290    // Check whether the S element is an integer.
291    if sig[usize::from(len_r + 4)] != 0x02 {
292        return DEREncoding::SElementNotAnInteger;
293    }
294
295    // Zero-length integers are not allowed for S.
296    if len_s == 0 {
297        return DEREncoding::SLengthIsZero;
298    }
299
300    // Negative numbers are not allowed for S.
301    if sig[usize::from(len_r + 6)] & 0x80 > 0 {
302        return DEREncoding::NegativeSValue;
303    }
304
305    // Null bytes at the start of R are not allowed, unless R would
306    // otherwise be interpreted as a negative number.
307    #[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// Contains information about a Bitcoin signature.
319#[derive(PartialEq, Eq, Debug, Clone)]
320pub struct SignatureInfo {
321    /// The actual signature wrapped in a type enum.
322    pub signature: SignatureType,
323    /// Inidcates if a ECDSA signature is strictly DER encoded. A
324    /// Schnorr signature will never be DER encoded always be [DEREncoding::NotApplicable].
325    pub der_encoded: DEREncoding,
326    /// SigHash flag of the signature.
327    pub sig_hash: u8,
328    /// length of the encoded signature.
329    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    /// Returns Some(SignatureInfo) if the Instruction is a Bitcoin ECDSA Signature,
361    /// otherwise None is returned.
362    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    /// Returns Some(SignatureInfo) if the Instruction is a Bitcoin Schnorr Signature,
375    /// otherwise None is returned.
376    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    /// Returns Some(SignatureInfo) if the Instruction is a Bitcoin ECDSA Signature,
389    /// otherwise None is returned.
390    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    /// Returns Some(SignatureInfo) if the Instruction is a Bitcoin Schnorr Signature,
420    /// otherwise None is returned.
421    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    /// Constructs a vector of SignatureInfo for all Signatures in the input. If
449    /// the inputs script_sig and witness don't contain any signatures, an empty
450    /// vector is returned.
451    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                // a P2PK script_sig consists of a single signature. This means
459                // the first byte is a PUSH_BYTES_XX followed by the bytes of the
460                // signature.
461                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                // a P2MS script_sig contains up to three signatures after an
470                // initial OP_FALSE.
471                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                // P2PKH inputs have a signature as the first element of the
482                // script_sig.
483                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                // P2SH wrapped P2WPKH inputs contain the signature as the
492                // first element of the witness.
493                signature_infos
494                    .push(SignatureInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[0]).unwrap());
495            }
496            InputType::P2wpkh => {
497                // P2WPKH inputs contain the signature as the first element of
498                // the witness.
499                signature_infos
500                    .push(SignatureInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[0]).unwrap())
501            }
502            InputType::P2sh => {
503                // P2SH inputs can contain zero or multiple signatures in
504                // the script sig. It's very uncommon that signatures are placed
505                // in the redeem script.
506                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                // P2SH wrapped P2WSH inputs can contain zero or multiple signatures in
519                // the witness. It's very uncommon that signatures are placed
520                // in the witness (redeem) script.
521                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                // P2WSH inputs can contain zero or multiple signatures in
529                // the witness. It's very uncommon that signatures are placed
530                // in the witness (redeem) script.
531                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                // P2TR key-path spends contain exactly one Schnorr signature in the
539                // witness.
540                signature_infos
541                    .push(SignatureInfo::from_u8_slice_schnorr(&input.witness.to_vec()[0]).unwrap())
542            }
543            InputType::P2trsp => {
544                // P2TR script-path spends contain zero or multiple signatures in the witness.
545                // There can't be any signatures in the annex, control block or script part.
546                // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#script-validation-rules
547                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            // exhaustive so the compiler warns us if we forget to add an input
554            // type here.
555            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    /// Tests if the script is OP_CHECKMULTISIG conform.
572    /// Expected: <OP_PUSHNUM_N>   M * <pubkey>   <OP_PUSHNUM_M> <OP_CHECKMULTISIG>
573    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    /// Returns a tuple of `(n, m)` (`n-of-m`) of the OP_CHECKMULTISIG script.
583    /// If the script is not a OP_CHECKMULTISIG script `Ok(None)` is returned.
584    /// n: number of signatures required
585    /// m: number of possible public keys
586    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        // <OP_PUSHNUM_N>   M * <pubkey>   <OP_PUSHNUM_M> <OP_CHECKMULTISIG>
600        let instructions = instructions_as_vec(self)?;
601
602        if instructions.len() < 4 {
603            return Ok(None);
604        }
605
606        let n: u8; // number of signatures required
607        let m: u8; // number of possible public keys
608
609        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        // check that there is space for exactly M public keys between
634        // OP_PUSHNUM_N and OP_PUSHNUM_M.
635        if instructions.len() - 2 - 1 != m as usize {
636            return Ok(None);
637        }
638
639        // Normally, the instructions between OP_PUSHNUM_N and OP_PUSHNUM_M should be public keys.
640        // However, these data pushes are sometimes used to store arbitraity data in P2MS output.
641        // They are still multisig n-of-m's.
642        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 is larger than m, the output is not considered a multisig here.
650        // This happens, for example, in the the following testnet transaction:
651        // 157c8495334e86b9422e656aa2c1a2fe977ed91fd27e2db71f6f64576f0456d9
652        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/// Information about a Pubkey
671#[derive(PartialEq, Eq, Debug, Clone)]
672pub struct PubKeyInfo {
673    /// If the pubkey is compressed. Only ECDSA pubkey can be uncompressed.
674    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            // a P2PK script_sig consists of a single signature. No public key.
705            InputType::P2pk | InputType::P2pkLaxDer => (),
706            // a P2MS script_sig contains up to three signatures after an
707            // initial OP_FALSE. No public key.
708            InputType::P2ms | InputType::P2msLaxDer => (),
709            InputType::P2pkh | InputType::P2pkhLaxDer => {
710                // P2PKH inputs have a signature as the first element and a public key as the second element of the script_sig.
711                // If we can't parse the the script, we assume there are no pubkeys in there..
712                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                // P2SH wrapped P2WPKH inputs contain the signature as the first and
719                // the pubkey as the second element of the witness.
720                pubkey_infos
721                    .push(PubKeyInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[1]).unwrap());
722            }
723            InputType::P2wpkh => {
724                // P2WPKH inputs contain the signature as the first and
725                // the pubkey as the second element of the witness.
726                pubkey_infos
727                    .push(PubKeyInfo::from_u8_slice_ecdsa(&input.witness.to_vec()[1]).unwrap())
728            }
729            InputType::P2sh => {
730                // P2SH inputs usually contain public keys in the witness redeem script
731                if let Some(redeem_script) = input.redeem_script().unwrap() {
732                    // If we can't parse the the script, we assume there are no pubkeys in there..
733                    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                // P2SH wrapped P2WSH inputs usually contain public keys in the witness redeem script
746                if let Some(redeem_script) = input.redeem_script().unwrap() {
747                    // If we can't parse the the script, we assume there are no pubkeys in there..
748                    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                // P2WSH inputs usually contain public keys in the witness redeem script
761                if let Some(redeem_script) = input.redeem_script().unwrap() {
762                    // If we can't parse the the script, we assume there are no pubkeys in there..
763                    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            // P2TR key-path spends do not contain a public key.
775            InputType::P2trkp => (),
776            // TODO: there could be multiple public keys in the script path spent
777            // However, this is currently not implemented here.
778            InputType::P2trsp => (),
779            // exhaustive so the compiler warns us if we forget to add an input
780            // type here.
781            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                // There can be up to three ECDSA public keys in P2MS outputs
807                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                // mainnet f779186cfea652ef661c21a83c90ad680402ea12d440e18f86e3e5f55355c497
854                // inputs: P2PKH, P2TR; outputs: P2PKH, P2TR
855                // The P2PKH input has an ECDSA pubkey and the P2TR output has a
856                // Schnorr pubkey
857                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                // mainnet 581d30e2a73a2db683ac2f15d53590bd0cd72de52555c2722d9d6a78e9fea510
864                // inputs: P2PKH; outputs: P2MS
865                // The P2PKH input has one uncompressed pubkey and the P2MS
866                // output has three uncompressed pubkeys.
867                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                // mainnet 60a20bd93aa49ab4b28d514ec10b06e1829ce6818ec06cd3aabd013ebcdc4bb1
874                // inputs: P2PKH, P2PKH, P2PKH; outputs: P2MS, P2PKH, P2PKH
875                // The three P2PKH inputs have one uncompressed pubkey each and
876                // the P2MS output has two uncompressed pubkeys while the P2PKH
877                // outputs have none.
878                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                // mainnet f91d0a8a78462bc59398f2c5d7a84fcff491c26ba54c4833478b202796c8aafd
885                // inputs: P2WPKH; outputs: P2WPKH, P2WPKH, OP_RETURN
886                // The P2WPKH input has one compressed pubkey and the P2WPKH outputs
887                // have none. OP_RETURN neither.
888                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                // mainnet b10c0000004da5a9d1d9b4ae32e09f0b3e62d21a5cce5428d4ad714fb444eb5d
895                // inputs: all of them; outputs: all of them
896                rawtx: hex::decode("0100000000010a9b9653ae4536f14723f5e7fdd730af3e41536a0c57807de8d67b1d2c96246ad40000000048473044022004f027ae0b19bb7a7aa8fcdf135f1da769d087342020359ef4099a9f0f0ba4ec02206a83a9b78df3fed89a3b6052e69963e1fb08d8f6d17d945e43b51b5214aa41e601f78c3201e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb000000006946304302204dc2939be89ab6626457fff40aec2cc4e6213e64bcb4d2c43bf6b49358ff638c021f33d2f8fdf6d54a2c82bb7cddc62becc2cbbaca6fd7f3ec927ea975f29ad8510221028b98707adfd6f468d56c1a6067a6f0c7fef43afbacad45384017f8be93a18d4087693201e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb010000008300453042021e4f6ff73d7b304a5cbf3bb7738abb5f81a4af6335962134ce27a1cc45fec702201b95e3acb7db93257b20651cdcb79af66bf0bb86a8ae5b4e0a5df4e3f86787e2033b303802153b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63021f34793e2878497561e7616291ebdda3024b681cdacc8b863b5b0804cd30c2a481685e2d01d6ca61cbfb1bab46294999b8f5650861d71bb701c8cc101807cb9a5933cbe14500000000fdaa0100443041021d1313459a48bd1d0628eec635495f793e970729684394f9b814d2b24012022050be6d9918444e283da0136884f8311ec465d0fed2f8d24b75a8485ebdc13aea013a303702153b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63021e78644ba72eab69fefb5fe50700671bfb91dda699f72ffbb325edc6a3c4ef8239303602153b78ce563f89a0ed9414f5aa28ad0d96d6795f9c63021d2c2db104e70720c39af43b6ba3edd930c26e0818aa59ff9c886281d8ba834ced532103e0a220d36f6f7ed5f3f58c279d055707c454135baf18fd00d798fec3cb52dfbc2103cf689db9313b9f7fc0b984dd9cac750be76041b392919b06f6bf94813da34cd421027f8af2eb6e904deddaa60d5af393d430575eb35e4dfd942a8a5882734b078906410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a34104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c55ae10000000e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb02000000171600149b27f072e4b972927c445d1946162a550b0914d88d000000e36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb0300000023220020a18160de7291554f349c7d5cbee4ab97fb542e94cf302ce8d7e9747e4188ca75efbeaddee36747271635683f16a2c9574cd79fac51f34af08e3a63fb293b0204ac479bcb0400000000406f400101d08b572974320b3d3650d672b1e53776ee38c72df9812bb2b18f8a92b37d630000000000d9b4bef9412c98a33b0d9ae72b87c117287b5722ffeb89d1ac5baeeb625012df02db2000000000000055010000b4061297f91ef316f4ed912e45f6d48f97d317d02d8d1c4bb131c4f9ec41577900000000005601000009400200000000000023210261542eb020b36c1da48e2e607b90a8c1f2ccdbd06eaf5fb4bb0d7cc34293d32aac22020000000000001976a9140240539af6c68431e4ce9cc5ef464f12c1741b3c88ac4602000000000000255121028b45a50f795be0413680036665d17a3eca099648ea80637bc3a70a7d2b52ae2851ae1c0200000000000017a91449ed2c96e33b6134408af8484508bcc3248c8dbd872601000000000000160014c8e51cf6891c0a2101aecea8cd5ce9bbbfaf7bba4a01000000000000220020c485bbb80c4be276e77eac3a983a391cc8b1a1b5f160995a36c3dff18296385a4a01000000000000225120a7a42b268957a06c9de4d7260f1df392ce4d6e7b743f5adc27415ce2afceb3b9f0000000000000000451024e730000000000000000356a224e6f7420796f757220696e707574732c206e6f7420796f7572206f7574707574732e005152535455565758595a5b5c5d5e5f600000000002433040021c23902a01d4c5cff2c33c8bdb778a5aadea78a9a0d6d4db60aaa0fba1022069237d9dbf2db8cff9c260ba71250493682d01a746f4a45c5c7ea386e56d2bc902210240187acd3e2fd3d8e1acffefa85907b6550730c24f78dfd3301c829fc4daf3cc0342303f021c65aee6696e80be6e14545cfd64b44f17b0514c150eefdb090c0f0bd9021f3fef4aa95c252a225622aba99e4d5af5a6fe40d177acd593e64cf2f8557ccc032103b55c6f0749e0f3e2caeca05f68e3699f1b3c62a550730f704985a6a9aae437a18576a914db865fd920959506111079995f1e4017b489bfe38763ac6721024d560f7f5d28aae5e1a8aa2b7ba615d7fc48e4ea27e5d27336e6a8f5fa0f5c8c7c820120876475527c2103443e8834fa7d79d7b5e95e0e9d0847f6b03ac3ea977979858b4104947fca87ca52ae67a91446c3747322b220fdb925c9802f0e949c1feab99988ac68680241303e021c11f60486afd0f5d6573603fb2076ef2f676455b92ada257d2f25558a021e317719c946f951d49bf4df4285a618629cd9e554fcbf787c319a0c4dd2260121032467f24cc31664f0cf34ff8d5cbb590888ddc1dcfec724a32ae3dd5338b8508e0340303d021c32f9454db85cb1a4ca63a9883d4347c5e13f3654e884ae44e9efa3c8021d62f07fe452c06b084bc3e09afd3aac4039136549a465533bc1ca6696790201014c632102fd6db4de50399b2aa086edb23f8e140bbc823d6651e024a0eb871288068789cd67012ab27521034134a2bb35c3f83dab2489d96160741888b8b5589bb694dea6e7bc24486e9c6f68ac0140d822f203827852998cad370232e8c57294540a5da51107fa26cf466bdd2b8b0b3d161999cc80aed8de7386a2bd5d5313aea159a231cc26fa53aaa702b7fa21ed0940fe6eb715dceffefc067fdc787d250a9a9116682d216f6356ea38fc1f112bd74995faa90315e81981d2c2260b7eaca3c41a16b280362980f0d8faf4c05ebb82c541e34ad0ad33885a473831f8ba8d9339123cb19d0e642e156d8e0d6e2ab2691aedb30e55a35637a806927225e1aa72223d41e59f92c6579b819e7d331a7ada9d2e01412a4861fb4cb951c791bf6c93859ef65abccd90034f91b9b77abb918e13b6fce75d5fa3e2d2f6eeeae105315178c2cb9db2ef238fe89b282f691c06db43bc71ca0241fc97bb2be673c3bf388aaf58178ef14d354caf83c92aca8ef1831d619b8511e928f4f5fdea3962067b11e7cecfe094cd0f66a4ea9af9ec836d70d18f2b37df028141a5781a0adaa80ab7f7f164172dd1a1cb127e523daa0d6949aba074a15c589f12dfb8183182afec9230cb7947b7422a4abc1bb78173550d66274ea19f6c9dd92c820000f0205f4237bd7dae576b34abc8a9c6fa4f0e4787c04234ca963e9e96c8f9b67b56d1ac205f4237bd7f93c69403a30c6b641f27ccf5201090152fcf1596474221307831c3ba205ac8ff25ce63564963d1148b84627f614af1f3c77d7caa23adc61264fa5e4996ba20b210c83e6f5b3f866837112d023d9ae8da2a6412168d54968ab87860ab970690ba20d3ee3b7a8b8149122b3c886330b3241538ba4b935c4040f4a73ddab917241bc5ba20cdfabb9d0e5c8f09a83f19e36e100d8f5e882f1b60aa60dacd9e6d072c117bc0ba20aab038c238e95fb54cdd0a6705dc1b1f8d135a9e9b20ab9c7ff96eef0e9bf545ba559cfdc102c0b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f5534a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33bf4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e166f7cf9580f1c2dfb3c4d5d043cdbb128c640e3f20161245aa7372e9666168516a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48dd5d27987d2a3dfc724e359870c6644b40e497bdc0589a033220fe15429d88599e3bf3d07d4b0375638d5f1db5255fe07ba2c4cb067cd81b84ee974b6585fb46829a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf54e48e5f5c656b26c3bca14a8c95aa583d07ebe84dde3b7dd4a78f4e4186e713d29c9c0e8e4d2a9790922af73f0b8d51f0bd4bb19940d9cf910ead8fbe85bc9bbb41a757f405890fb0f5856228e23b715702d714d59bf2b1feb70d8b2b4e3e089fdbcf0ef9d8d00f66e47917f67cc5d78aec1ac786e2abb8d2facb4e4790aad6cc455ae816e6cdafdb58d54e35d4f46d860047458eacf1c7405dc634631c570d8d31992805518fd62daa3bdd2a5c4fd2cd3054c9b3dca1d78055e9528cff6adc8f907925d2ebe48765103e6845c06f1f2bb77c6adc1cc002865865eb5cfd5c1cb10c007c60e14f9d087e0291d4d0c7869697c6681d979c6639dbd960792b4d4133e794d097969002ee05d336686fc03c9e15a597c1b9827669460fac9879903637777defed8717c581b4c0509329550e344bdc14ac38f71fc050096887e535c8fd456524104a6674693c29946543f8a0befccce5a352bda55ec8559fc630f5f37393096d97bfee8660f4100ffd61874d62f9a65de9fb6acf740c4c386990ef7373be398c4bdc43709db7398106609eea2a7841aaf3a4fa2000dc18184faa2a7eb5a2af5845a8d3796308ff9840e567b14cf6bb158ff26c999e6f9a1f5448f9aa29ab5f49").unwrap(),
897                pkinfos_input: vec![
898                    vec![], // P2PK
899                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2PKH
900                    vec![], // P2MS
901                    vec![   // 3-of-5 P2SH
902                        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 }], // P2SH
904                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2WPKH
905                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2WPKH
906                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }, PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2WSH
907                    vec![], // P2TR key-path
908                    vec![]  // P2TR script-path TODO: this tx pushes pubkey-like data, but that's not implemented here...
909                ],
910                pkinfos_output: vec![
911                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2PK
912                    vec![], // P2PKH
913                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2MS
914                    vec![], // P2SH
915                    vec![], // P2WPKH
916                    vec![], // P2WSH
917                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::Schnorr }], // P2TR
918                    vec![], // Unknown!
919                    vec![], // OP_RETURN
920                ],
921            },
922
923            TestCase{
924                // signet 9becb93a15ab4b47eaca85c852d4309f82a28677a0a557df3c0fa6b82ef9293d
925                // inputs: P2WPKH; outputs: 1-of-3 P2MS (2x uncompressed, not-on-curve 'pubkeys' and a compressed pk), P2PKH
926                rawtx: hex::decode("01000000000101cd49818bb48793b0f5a2445517f64665805dd934d5bb5fe3f2443db18c3f63450000000000ffffffff02a00f000000000000a95141044a6572656d6961682032390d0a0d0a313120466f722049206b6e6f77207468652074686f756768747320746861742049207468696e6b20746f7761726420796f4104752c20736169746820746865204c6f72642c2074686f7567687473206f662070656163652c20616e64206e6f74206f66206576696c2c20746f206769766520792103c0e0bf0bbcdc53be9542359aeb1dde7c6289743b7b3460c12e2d57a478c6e48953aef7300f00000000001976a9148c51ed42f050b1bde974fb6649e25b782d168f4088ac0247304402206b41bae6dcec9129276d3df71e7d1f1f41097b338111a8932e034ca8747fdfaa0220447ab24f6d8f66fc0cc5a8a8c61928b63469bb28d6f51b7fc55f987509c57460012103c0e0bf0bbcdc53be9542359aeb1dde7c6289743b7b3460c12e2d57a478c6e48900000000").unwrap(),
927                pkinfos_input: vec![
928                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2WPKH
929                ],
930                pkinfos_output: vec![
931                    vec![PubKeyInfo { compressed: true, pubkey_type: PubkeyType::ECDSA }], // P2MS (the two uncompressed pubkeys aren't on the curve..)
932                    vec![], // P2PKH
933                ],
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 { // not on curve (PK in P2MS output of signet 9becb93a15ab4b47eaca85c852d4309f82a28677a0a557df3c0fa6b82ef9293d)
964                raw: hex::decode("044a6572656d6961682032390d0a0d0a313120466f722049206b6e6f77207468652074686f756768747320746861742049207468696e6b20746f7761726420796f").unwrap(),
965                expected: false,
966            },
967            Testcase { // not on curve (PK in P2MS output of signet 9becb93a15ab4b47eaca85c852d4309f82a28677a0a557df3c0fa6b82ef9293d)
968                raw: hex::decode("04752c20736169746820746865204c6f72642c2074686f7567687473206f662070656163652c20616e64206e6f74206f66206576696c2c20746f20676976652079").unwrap(),
969                expected: false,
970            },
971            Testcase { // (PK in P2MS output of signet 9becb93a15ab4b47eaca85c852d4309f82a28677a0a557df3c0fa6b82ef9293d)
972                raw: hex::decode("03c0e0bf0bbcdc53be9542359aeb1dde7c6289743b7b3460c12e2d57a478c6e489").unwrap(),
973                expected: true,
974            },
975            Testcase { // Hal Finney's pubkey from f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
976                raw: hex::decode("04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c").unwrap(),
977                expected: true,
978            },
979            Testcase { // Satoshis pubkey from f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
980                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        // from mainnet f72d52eaae494da7c438a8456a9b20d2791fdf2b1c818825458f8f707d7b8011 input 0
993        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        // from mainnet d5a02fd4d7e3cf5ca02d2a4c02c8124ba00907eb85801dddfe984428714e3946 output 0
1003        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        // from mainnet 6e45ba2e4f71497291170c40e7161fb47675ff0a7d6c67c1fda485832ed7c923 output 1
1010        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        // from mainnet 391f812dcce57d1b60669dfdc538d34fe25eec27134122d1fec8cd3208cb3ad4 input 0
1017        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        // A 2-of-2 script modified to have n = 2 and m = 3, but only 2 PubKeys
1036        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        // A 2-of-1 script e.g. found twice in the testnet transaction 157c8495334e86b9422e656aa2c1a2fe977ed91fd27e2db71f6f64576f0456d9
1046        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            // #0 from https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv
1080            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            // #0 from https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv with explicit sighash flag
1089            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            // #0 from https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv with non-default sighash flag
1098            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            // just above r-threshold (not-low r) & half curve order (-> low s)
1107            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            // just below r-threshold (low r) & half curve order + 1(-> not-low s)
1116            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            // BIP 66 example 1 (DER encoded)
1148            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            // Input 0 of 39baeb3b2579dac22cec858be3a4d70d8d229206127b43fa4133ed63fb7b1b40
1165            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            // Input 0 of f4597ab5b6d45ba3a04486f3edf1a27f9f2cc3ab23300eb16d6b7067b8cf47dd
1174            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            // Input 0 of fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d
1183            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            // Input 0 of 23befff6eea3dded0e34574af65c266c9398e7d7d9d07022bf1cd526c5cdbc94
1192            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            // Input 0 of bf4bc335d1a76dd47aaa8fe21b22c18ba22018c7ca8f27b21f943e9004ffa335
1201            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}