monero/util/
fuzz_utils.rs

1// Rust Monero Library
2// Written in 2019-2023 by
3//   Monero Rust Contributors
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15
16use crate::blockdata::transaction::{ExtraField, KeyImage, RawExtraField, SubField, TxOutTarget};
17use crate::consensus::encode::Encodable;
18use crate::consensus::{deserialize, serialize};
19use crate::cryptonote::hash::Hashable;
20use crate::util::key::H;
21use crate::util::ringct::{CtKey, EcdhInfo, Key, RctSig, RctSigBase, RctSigPrunable, RctType};
22use crate::{
23    Amount, Block, BlockHeader, Hash, PrivateKey, PublicKey, Transaction, TransactionPrefix, TxIn,
24    TxOut, VarInt, ViewPair,
25};
26use hex::{FromHex, ToHex};
27use std::io;
28use std::str::FromStr;
29
30/// Fuzz for block deserialization, called from the fuzz target
31pub fn fuzz_block_deserialize(fuzz_data: &[u8]) -> bool {
32    let fuzz_bytes = fuzz_data.to_vec();
33
34    // Block
35    if let Ok(val) = deserialize::<Block>(&fuzz_bytes[..]) {
36        assert_eq!(fuzz_bytes, serialize(&val), "\nfuzz_data: {:?}", fuzz_data);
37    }
38
39    true
40}
41
42/// Fuzz for block header deserialization, called from the fuzz target
43pub fn fuzz_block_header_deserialize(fuzz_data: &[u8]) -> bool {
44    let fuzz_bytes = fuzz_data.to_vec();
45
46    // BlockHeader
47    if let Ok(val) = deserialize::<BlockHeader>(&fuzz_bytes[..]) {
48        assert_eq!(fuzz_bytes, serialize(&val), "\nfuzz_data: {:?}", fuzz_data);
49    }
50
51    true
52}
53
54/// Fuzz for transaction prefix deserialization, called from the fuzz target
55pub fn fuzz_transaction_prefix_deserialize(fuzz_data: &[u8]) -> bool {
56    let fuzz_bytes = fuzz_data.to_vec();
57
58    // TransactionPrefix
59    if let Ok(val) = deserialize::<TransactionPrefix>(&fuzz_bytes[..]) {
60        assert_eq!(fuzz_bytes, serialize(&val), "\nfuzz_data: {:?}", fuzz_data);
61    }
62
63    true
64}
65
66/// Padding sub-field position in extra data
67#[derive(Clone, Copy, PartialEq, Eq)]
68pub enum AddPadding {
69    /// Add padding to the front of the extra field
70    ToFront,
71    /// Add padding to the middle of the extra field
72    ToMiddle,
73    /// Add padding to the rear of the extra field
74    ToRear,
75}
76
77fn u64_val_from_fuzz_data(fuzz_data: &[u8]) -> u64 {
78    if fuzz_data.is_empty() {
79        0
80    } else {
81        let mut vec = fuzz_data.to_vec().clone();
82        vec.resize(8, 0);
83        let mut bytes = [0u8; 8];
84        bytes.copy_from_slice(vec.as_slice());
85        u64::from_le_bytes(bytes)
86    }
87}
88
89/// Fuzz helper function to create an extra field, called from the fuzz target
90pub fn fuzz_create_extra_field(fuzz_data: &[u8], add_padding: AddPadding) -> ExtraField {
91    let fuzz_bytes = fuzz_data.to_vec();
92    let hash = Hash::new(fuzz_data);
93
94    // SubField::TxPublicKey
95    let tx_pub_key_field = SubField::TxPublicKey(match PublicKey::from_slice(fuzz_data) {
96        Ok(val) => val,
97        Err(_) => H,
98    });
99    let nonce_field = SubField::Nonce(fuzz_bytes.clone());
100
101    // SubField::Padding
102    let padding_field = SubField::Padding(fuzz_bytes.first().copied().unwrap_or_default());
103
104    // SubField::MergeMining
105    let u64_val = u64_val_from_fuzz_data(fuzz_data);
106    let merge_mining_field = SubField::MergeMining(VarInt(u64_val), hash);
107
108    // SubField::AdditionalPublickKey
109    let additional_public_key_field =
110        SubField::AdditionalPublickKey(match PublicKey::from_slice(fuzz_data) {
111            Ok(val) => {
112                if fuzz_bytes.is_empty() {
113                    vec![]
114                } else {
115                    vec![val; (fuzz_bytes[0] % 10 + 1) as usize]
116                }
117            }
118            Err(_) => vec![H],
119        });
120
121    // SubField::MysteriousMinerGate
122    let mysterious_miner_gate_field = SubField::MysteriousMinerGate(fuzz_bytes);
123
124    let sub_fields = match add_padding {
125        AddPadding::ToFront => vec![
126            padding_field, // This will fail if the padding is not maximum
127            tx_pub_key_field,
128            nonce_field,
129            merge_mining_field,
130            additional_public_key_field,
131            mysterious_miner_gate_field,
132        ],
133        AddPadding::ToMiddle => vec![
134            tx_pub_key_field,
135            nonce_field,
136            merge_mining_field,
137            padding_field, // This will fail if the padding is not maximum
138            additional_public_key_field,
139            mysterious_miner_gate_field,
140        ],
141        AddPadding::ToRear => vec![
142            tx_pub_key_field,
143            nonce_field,
144            merge_mining_field,
145            additional_public_key_field,
146            mysterious_miner_gate_field,
147            padding_field,
148        ],
149    };
150
151    // ExtraField
152    ExtraField(sub_fields)
153}
154
155/// Fuzz for extra field's sub fields parse, called from the fuzz target
156pub fn fuzz_extra_field_parse_sub_fields(extra_field: &ExtraField) -> bool {
157    for sub_field in &extra_field.0 {
158        let ser_sub_field = serialize(sub_field);
159        match deserialize::<SubField>(&ser_sub_field) {
160            Ok(des_sub_field) => {
161                assert_eq!(sub_field, &des_sub_field, "\nsub field: {}", sub_field)
162            }
163            Err(err) => {
164                panic!(
165                    "Deserializing a serialized SubField may not fail\n({})\nsub field: {:?}",
166                    err, sub_field
167                )
168            }
169        }
170    }
171
172    true
173}
174
175/// Fuzz for extra field try parse, called from the fuzz target
176pub fn fuzz_extra_field_try_parse(
177    extra_field: &ExtraField,
178    add_padding: AddPadding,
179    fuzz_data: &[u8],
180) -> bool {
181    let raw_extra_field = RawExtraField::from(extra_field.clone());
182    match ExtraField::try_parse(&raw_extra_field) {
183        Ok(parsed_extra_field) => {
184            assert_eq!(
185                extra_field, &parsed_extra_field,
186                "\nOn 'Ok(_)\noriginal: {:?}\nparsed:   {:?}\n'fuzz_data: {:?}",
187                extra_field, parsed_extra_field, fuzz_data
188            )
189        }
190        Err(parsed_extra_field) => {
191            if parsed_extra_field.0.len() > extra_field.0.len() {
192                panic!(
193                    "On 'Err(_)', parsed extra field has to many sub fields\noriginal: {:?}\nparsed:   {:?}\nfuzz_data: {:?}",
194                    extra_field,
195                    parsed_extra_field,
196                    fuzz_data,
197                );
198            }
199            for parsed_sub_field in parsed_extra_field.0.iter() {
200                match parsed_sub_field {
201                    SubField::Padding(_) => {
202                        // The padding sub-field may be different on error
203                    }
204                    _ => {
205                        // Other sub-fields must be the same on error
206                        extra_field.0.iter().find(|&x| x == parsed_sub_field).unwrap_or_else(|| {
207                            panic!(
208                                "On 'Err(_)', parsed sub field '{}' is not in original extra field\noriginal: {:?}\nparsed:   {:?}\nfuzz_data: {:?}",
209                                parsed_sub_field,
210                                extra_field,
211                                parsed_extra_field,
212                                fuzz_data,
213                            )
214                        });
215                    }
216                }
217            }
218            if add_padding == AddPadding::ToRear {
219                panic!(
220                    "\nOn 'Err(_)', parsing a serialized ExtraField with padding at the rear may not fail\n({:?})\n({:?})\nfuzz_data: {:?}",
221                    extra_field,
222                    parsed_extra_field,
223                    fuzz_data,
224                );
225            }
226        }
227    };
228
229    true
230}
231
232/// Fuzz helper function to create a raw extra field, called from the fuzz target
233pub fn fuzz_create_raw_extra_field(fuzz_data: &[u8]) -> RawExtraField {
234    let add_padding = if fuzz_data.is_empty() {
235        AddPadding::ToMiddle
236    } else {
237        match fuzz_data.len() % 3 {
238            0 => AddPadding::ToFront,
239            1 => AddPadding::ToMiddle,
240            _ => AddPadding::ToRear,
241        }
242    };
243    RawExtraField::from(fuzz_create_extra_field(fuzz_data, add_padding))
244}
245
246/// Fuzz for transaction deserialization, called from the fuzz target
247pub fn fuzz_transaction_deserialize(fuzz_data: &[u8]) -> bool {
248    let fuzz_bytes = fuzz_data.to_vec();
249
250    // Transaction
251    if let Ok(val) = deserialize::<Transaction>(&fuzz_bytes[..]) {
252        assert_eq!(fuzz_bytes, serialize(&val));
253    }
254
255    let raw_extra_field = fuzz_create_raw_extra_field(fuzz_data);
256
257    let transaction = fuzz_create_transaction_alternative_1(fuzz_data, &raw_extra_field);
258    let serialized_tx = serialize(&transaction);
259    let _ = deserialize::<Transaction>(&serialized_tx[..]);
260
261    let transaction = fuzz_create_transaction_alternative_2(fuzz_data, &raw_extra_field);
262    let serialized_tx = serialize(&transaction);
263    let _ = deserialize::<Transaction>(&serialized_tx[..]);
264
265    true
266}
267
268/// Fuzz for transaction serialization and deserialization, called from the fuzz target
269pub fn fuzz_transaction_components(fuzz_data: &[u8]) -> bool {
270    let fuzz_bytes = fuzz_data.to_vec().clone();
271    let (rct_type, inputs, outputs, mixin) = if fuzz_bytes.is_empty() {
272        (RctType::Null, 0, 0, 0)
273    } else {
274        (
275            match fuzz_bytes[0] % 8 {
276                0 => RctType::Null,
277                1 => RctType::Full,
278                3 => RctType::Clsag,
279                4 => RctType::Simple,
280                5 => RctType::Bulletproof,
281                6 => RctType::Bulletproof2,
282                7 => RctType::BulletproofPlus,
283                _ => RctType::Null,
284            },
285            fuzz_bytes[0] % 5,
286            fuzz_bytes[0] % 7,
287            fuzz_bytes[0] % 3,
288        )
289    };
290
291    // TransactionPrefix
292    if let Ok(val) = deserialize::<TransactionPrefix>(&fuzz_bytes[..]) {
293        assert_eq!(fuzz_bytes, serialize(&val));
294    }
295
296    // RctSigBase
297    let fuzz_bytes = fuzz_data.to_vec().clone();
298    let mut decoder = io::Cursor::new(&fuzz_bytes);
299    if let Ok(Some(rct_sig)) =
300        RctSigBase::consensus_decode(&mut decoder, inputs as usize, outputs as usize)
301    {
302        let mut encoder = Vec::new();
303        if rct_sig.consensus_encode(&mut encoder).is_ok() {
304            // This fails! Should it?
305            // assert_eq!(fuzz_bytes, encoder);
306        }
307    }
308
309    // RctSigPrunable
310    let fuzz_bytes = fuzz_data.to_vec().clone();
311    let mut decoder = io::Cursor::new(&fuzz_bytes);
312    if let Ok(Some(rct_sig)) = RctSigPrunable::consensus_decode(
313        &mut decoder,
314        rct_type,
315        inputs as usize,
316        outputs as usize,
317        mixin as usize,
318    ) {
319        let mut encoder = Vec::new();
320        if rct_sig.consensus_encode(&mut encoder, rct_type).is_ok() {
321            // This fails! Should it?
322            // assert_eq!(fuzz_bytes, encoder);
323        }
324    }
325
326    true
327}
328
329/// Fuzz helper function to create a transaction, called from the fuzz target
330pub fn fuzz_create_transaction_alternative_1(
331    fuzz_data: &[u8],
332    raw_extra_field: &RawExtraField,
333) -> Transaction {
334    let hash_1 = Hash::new(fuzz_data);
335    let hash_2 = Hash::new(hash_1.0);
336    let hash_3 = Hash::new(hash_2.0);
337    let hash_4 = Hash::new(hash_3.0);
338    let hash_5 = Hash::new(hash_4.0);
339    let hash_6 = Hash::new(hash_5.0);
340    let hash_7 = Hash::new(hash_6.0);
341    let hash_8 = Hash::new(hash_7.0);
342    let u64_val = u64_val_from_fuzz_data(fuzz_data);
343
344    let prefix = TransactionPrefix {
345        version: VarInt(u64_val),
346        unlock_time: VarInt(u64_val),
347        inputs: vec![
348            TxIn::ToKey {
349                amount: VarInt(u64_val),
350                key_offsets: vec![],
351                k_image: KeyImage { image: hash_1 },
352            },
353            TxIn::Gen {
354                height: VarInt(u64_val),
355            },
356        ],
357        outputs: vec![
358            TxOut {
359                amount: VarInt(u64_val),
360                target: TxOutTarget::ToKey { key: hash_2.0 },
361            },
362            TxOut {
363                amount: VarInt(u64_val),
364                target: TxOutTarget::ToTaggedKey {
365                    key: hash_3.0,
366                    view_tag: hash_1.0[0],
367                },
368            },
369        ],
370        extra: raw_extra_field.clone(),
371    };
372
373    let rct_signatures = RctSig {
374        sig: Option::from(RctSigBase {
375            rct_type: RctType::Full,
376            txn_fee: Amount::from_pico(u64_val),
377            pseudo_outs: vec![
378                Key { key: hash_4.0 },
379                Key { key: hash_8.0 },
380                Key { key: hash_7.0 },
381            ],
382            ecdh_info: vec![
383                EcdhInfo::Standard {
384                    mask: Key { key: hash_5.0 },
385                    amount: Key { key: hash_6.0 },
386                },
387                EcdhInfo::Standard {
388                    mask: Key { key: hash_5.0 },
389                    amount: Key { key: hash_6.0 },
390                },
391            ],
392            out_pk: vec![CtKey {
393                mask: Key { key: hash_7.0 },
394            }],
395        }),
396        p: None,
397    };
398
399    Transaction {
400        prefix,
401        signatures: vec![],
402        rct_signatures,
403    }
404}
405
406/// Fuzz helper function to create a transaction, called from the fuzz target
407pub fn fuzz_create_transaction_alternative_2(
408    fuzz_data: &[u8],
409    raw_extra_field: &RawExtraField,
410) -> Transaction {
411    let hash_1 = Hash::new(fuzz_data);
412    let hash_2 = Hash::new(hash_1.0);
413    let hash_3 = Hash::new(hash_2.0);
414    let hash_4 = Hash::new(hash_3.0);
415    let hash_5 = Hash::new(hash_4.0);
416    let hash_6 = Hash::new(hash_5.0);
417    let hash_7 = Hash::new(hash_6.0);
418    let hash_8 = Hash::new(hash_7.0);
419    let u64_val = u64_val_from_fuzz_data(fuzz_data);
420
421    let prefix = TransactionPrefix {
422        version: VarInt(u64_val),
423        unlock_time: VarInt(u64_val),
424        inputs: vec![
425            // Adding this results in `Error: "attempt to subtract with overflow"`
426            TxIn::ToKey {
427                amount: VarInt(u64_val),
428                key_offsets: vec![],
429                k_image: KeyImage { image: hash_1 },
430            },
431            TxIn::Gen {
432                height: VarInt(u64_val),
433            },
434            TxIn::ToKey {
435                amount: VarInt(u64_val),
436                key_offsets: vec![VarInt(u64_val)],
437                k_image: KeyImage { image: hash_1 },
438            },
439        ],
440        outputs: vec![
441            TxOut {
442                amount: VarInt(u64_val),
443                target: TxOutTarget::ToKey { key: hash_2.0 },
444            },
445            TxOut {
446                amount: VarInt(u64_val),
447                target: TxOutTarget::ToTaggedKey {
448                    key: hash_3.0,
449                    view_tag: hash_1.0[0],
450                },
451            },
452        ],
453        extra: raw_extra_field.clone(),
454    };
455
456    let rct_signatures = RctSig {
457        sig: Option::from(RctSigBase {
458            rct_type: RctType::Full,
459            txn_fee: Amount::from_pico(u64_val),
460            pseudo_outs: vec![Key { key: hash_4.0 }, Key { key: hash_8.0 }],
461            ecdh_info: vec![
462                EcdhInfo::Standard {
463                    mask: Key { key: hash_5.0 },
464                    amount: Key { key: hash_6.0 },
465                },
466                EcdhInfo::Standard {
467                    mask: Key { key: hash_5.0 },
468                    amount: Key { key: hash_6.0 },
469                },
470            ],
471            out_pk: vec![
472                CtKey {
473                    mask: Key { key: hash_7.0 },
474                },
475                CtKey {
476                    mask: Key { key: hash_8.0 },
477                },
478            ],
479        }),
480        p: Option::from(RctSigPrunable {
481            range_sigs: vec![],
482            bulletproofs: vec![],
483            bulletproofplus: vec![],
484            MGs: vec![],
485            Clsags: vec![],
486            pseudo_outs: vec![],
487        }),
488    };
489
490    Transaction {
491        prefix,
492        signatures: vec![],
493        rct_signatures,
494    }
495}
496
497/// Fuzz for hash conversion, called from the fuzz target
498pub fn fuzz_hash_convert(fuzz_data: &[u8]) -> bool {
499    // Hash
500    let hash = Hash::new(fuzz_data);
501
502    let hash_str: String = hash.encode_hex();
503    if let Ok(hash2) = Hash::from_hex(hash_str.clone()) {
504        assert_eq!(hash, hash2);
505    }
506
507    let hash_str_with_0x = format!("0x{hash_str}");
508    if let Ok(hash2) = Hash::from_hex(hash_str_with_0x) {
509        assert_eq!(hash, hash2);
510    }
511
512    assert_eq!(hash.as_scalar(), Hash::hash_to_scalar(fuzz_data));
513
514    true
515}
516
517/// Fuzz for raw extra field deserialize, called from the fuzz target
518pub fn fuzz_raw_extra_field_deserialize(raw_extra_field: &RawExtraField) -> bool {
519    let raw_extra_field_bytes = serialize(raw_extra_field);
520    if let Ok(raw_extra_field_2) = deserialize::<RawExtraField>(&raw_extra_field_bytes) {
521        assert_eq!(raw_extra_field, &raw_extra_field_2);
522    }
523
524    true
525}
526
527/// Fuzz for raw extra field, called from the fuzz target
528pub fn fuzz_raw_extra_field_from(fuzz_data: &[u8]) -> bool {
529    let extra_field = fuzz_create_extra_field(fuzz_data, AddPadding::ToRear);
530    let _ = RawExtraField::from(extra_field.clone());
531    let _ = fuzz_create_raw_extra_field(fuzz_data);
532
533    true
534}
535
536/// Fuzz for transaction hash, called from the fuzz target
537pub fn fuzz_transaction_hash(transaction: &Transaction) -> bool {
538    let _hash = transaction.hash();
539    true
540}
541
542/// Fuzz for transaction check outputs, called from the fuzz target
543pub fn fuzz_transaction_check_outputs(transaction: &Transaction) -> bool {
544    let secret_view = match PrivateKey::from_str(
545        "bcfdda53205318e1c14fa0ddca1a45df363bb427972981d0249d0f4652a7df07",
546    ) {
547        Ok(val) => val,
548        Err(_) => {
549            // This may not fail, otherwise the test cannot continue
550            return true;
551        }
552    };
553    let secret_spend = match PrivateKey::from_str(
554        "e5f4301d32f3bdaef814a835a18aaaa24b13cc76cf01a832a7852faf9322e907",
555    ) {
556        Ok(val) => val,
557        Err(_) => {
558            // This may not fail, otherwise the test cannot continue
559            return true;
560        }
561    };
562    let public_spend = PublicKey::from_private_key(&secret_spend);
563    let viewpair = ViewPair {
564        view: secret_view,
565        spend: public_spend,
566    };
567
568    let _ = transaction.check_outputs(&viewpair, 0..3, 0..3);
569    true
570}
571
572#[cfg(test)]
573mod tests {
574    use crate::{Address, AddressType, Network};
575    use quickcheck::QuickCheck;
576
577    use crate::util::fuzz_utils::{
578        fuzz_block_deserialize, fuzz_block_header_deserialize, fuzz_create_extra_field,
579        fuzz_create_raw_extra_field, fuzz_create_transaction_alternative_1,
580        fuzz_create_transaction_alternative_2, fuzz_extra_field_parse_sub_fields,
581        fuzz_extra_field_try_parse, fuzz_hash_convert, fuzz_raw_extra_field_deserialize,
582        fuzz_raw_extra_field_from, fuzz_transaction_check_outputs, fuzz_transaction_components,
583        fuzz_transaction_deserialize, fuzz_transaction_hash, fuzz_transaction_prefix_deserialize,
584        AddPadding,
585    };
586
587    #[test]
588    fn test_fuzz_block_deserialize() {
589        fn internal(data: Vec<u8>) -> bool {
590            fuzz_block_deserialize(&data)
591        }
592
593        const TESTS: u64 = 10_000;
594
595        QuickCheck::new()
596            .min_tests_passed(TESTS)
597            .tests(TESTS)
598            .max_tests(TESTS)
599            .quickcheck(internal as fn(Vec<u8>) -> bool);
600    }
601
602    #[test]
603    fn test_fuzz_block_header_deserialize() {
604        fn internal(data: Vec<u8>) -> bool {
605            fuzz_block_header_deserialize(&data)
606        }
607
608        const TESTS: u64 = 10_000;
609
610        QuickCheck::new()
611            .min_tests_passed(TESTS)
612            .tests(TESTS)
613            .max_tests(TESTS)
614            .quickcheck(internal as fn(Vec<u8>) -> bool);
615    }
616
617    #[test]
618    fn test_fuzz_transaction_prefix_deserialize() {
619        fn internal(data: Vec<u8>) -> bool {
620            fuzz_transaction_prefix_deserialize(&data)
621        }
622
623        const TESTS: u64 = 10_000;
624
625        QuickCheck::new()
626            .min_tests_passed(TESTS)
627            .tests(TESTS)
628            .max_tests(TESTS)
629            .quickcheck(internal as fn(Vec<u8>) -> bool);
630    }
631
632    #[test]
633    fn test_fuzz_transaction_deserialize() {
634        fn internal(data: Vec<u8>) -> bool {
635            fuzz_transaction_deserialize(&data)
636        }
637
638        const TESTS: u64 = 10_000;
639
640        QuickCheck::new()
641            .min_tests_passed(TESTS)
642            .tests(TESTS)
643            .max_tests(TESTS)
644            .quickcheck(internal as fn(Vec<u8>) -> bool);
645    }
646
647    #[test]
648    fn test_fuzz_transaction_components() {
649        fn internal(data: Vec<u8>) -> bool {
650            fuzz_transaction_components(&data)
651        }
652
653        const TESTS: u64 = 1_000;
654
655        QuickCheck::new()
656            .min_tests_passed(TESTS)
657            .tests(TESTS)
658            .max_tests(TESTS)
659            .quickcheck(internal as fn(Vec<u8>) -> bool);
660    }
661
662    #[test]
663    fn test_fuzz_hash_convert() {
664        fn internal(data: Vec<u8>) -> bool {
665            fuzz_hash_convert(&data)
666        }
667
668        const TESTS: u64 = 10_000;
669
670        QuickCheck::new()
671            .min_tests_passed(TESTS)
672            .tests(TESTS)
673            .max_tests(TESTS)
674            .quickcheck(internal as fn(Vec<u8>) -> bool);
675    }
676
677    #[test]
678    fn test_fuzz_raw_extra_field_from() {
679        fn internal(data: Vec<u8>) -> bool {
680            fuzz_raw_extra_field_from(&data)
681        }
682
683        const TESTS: u64 = 10_000;
684
685        QuickCheck::new()
686            .min_tests_passed(TESTS)
687            .tests(TESTS)
688            .max_tests(TESTS)
689            .quickcheck(internal as fn(Vec<u8>) -> bool);
690    }
691
692    #[test]
693    fn test_fuzz_raw_extra_field_deserialize() {
694        fn internal(data: Vec<u8>) -> bool {
695            let raw_extra_field = fuzz_create_raw_extra_field(&data);
696            fuzz_raw_extra_field_deserialize(&raw_extra_field)
697        }
698
699        const TESTS: u64 = 10_000;
700
701        QuickCheck::new()
702            .min_tests_passed(TESTS)
703            .tests(TESTS)
704            .max_tests(TESTS)
705            .quickcheck(internal as fn(Vec<u8>) -> bool);
706    }
707
708    #[test]
709    fn test_fuzz_extra_field_parse_sub_fields() {
710        fn internal(data: Vec<u8>) -> bool {
711            let add_padding = if data.is_empty() {
712                AddPadding::ToMiddle
713            } else {
714                match data.len() % 3 {
715                    0 => AddPadding::ToFront,
716                    1 => AddPadding::ToMiddle,
717                    _ => AddPadding::ToRear,
718                }
719            };
720            let extra_field = fuzz_create_extra_field(&data, add_padding);
721            fuzz_extra_field_parse_sub_fields(&extra_field)
722        }
723
724        const TESTS: u64 = 10_000;
725
726        QuickCheck::new()
727            .min_tests_passed(TESTS)
728            .tests(TESTS)
729            .max_tests(TESTS)
730            .quickcheck(internal as fn(Vec<u8>) -> bool);
731    }
732
733    #[test]
734    fn test_fuzz_extra_field_try_parse() {
735        fn internal(data: Vec<u8>) -> bool {
736            let add_padding = if data.is_empty() {
737                AddPadding::ToMiddle
738            } else {
739                match data.len() % 3 {
740                    0 => AddPadding::ToFront,
741                    1 => AddPadding::ToMiddle,
742                    _ => AddPadding::ToRear,
743                }
744            };
745            let extra_field = fuzz_create_extra_field(&data, add_padding);
746            fuzz_extra_field_try_parse(&extra_field, add_padding, &data)
747        }
748
749        const TESTS: u64 = 10_000;
750
751        QuickCheck::new()
752            .min_tests_passed(TESTS)
753            .tests(TESTS)
754            .max_tests(TESTS)
755            .quickcheck(internal as fn(Vec<u8>) -> bool);
756    }
757
758    #[test]
759    fn test_fuzz_transaction_hash() {
760        fn internal(data: Vec<u8>) -> bool {
761            let raw_extra_field = fuzz_create_raw_extra_field(&data);
762            let transaction = fuzz_create_transaction_alternative_1(&data, &raw_extra_field);
763            let _ = fuzz_transaction_hash(&transaction);
764            let transaction = fuzz_create_transaction_alternative_2(&data, &raw_extra_field);
765            fuzz_transaction_hash(&transaction)
766        }
767
768        const TESTS: u64 = 10_000;
769
770        QuickCheck::new()
771            .min_tests_passed(TESTS)
772            .tests(TESTS)
773            .max_tests(TESTS)
774            .quickcheck(internal as fn(Vec<u8>) -> bool);
775    }
776
777    #[test]
778    fn test_fuzz_transaction_check_outputs() {
779        fn internal(data: Vec<u8>) -> bool {
780            let raw_extra_field = fuzz_create_raw_extra_field(&data);
781            let transaction = fuzz_create_transaction_alternative_1(&data, &raw_extra_field);
782            let _ = fuzz_transaction_check_outputs(&transaction);
783            let transaction = fuzz_create_transaction_alternative_2(&data, &raw_extra_field);
784            let _ = fuzz_transaction_check_outputs(&transaction);
785            true
786        }
787
788        const TESTS: u64 = 1_000;
789
790        QuickCheck::new()
791            .min_tests_passed(TESTS)
792            .tests(TESTS)
793            .max_tests(TESTS)
794            .quickcheck(internal as fn(Vec<u8>) -> bool);
795    }
796
797    #[test]
798    fn test_fuzz_address_from_bytes() {
799        fn internal(data: Vec<u8>) -> bool {
800            let _ = Address::from_bytes(&data);
801            true
802        }
803
804        const TESTS: u64 = 10_000;
805
806        QuickCheck::new()
807            .min_tests_passed(TESTS)
808            .tests(TESTS)
809            .max_tests(TESTS)
810            .quickcheck(internal as fn(Vec<u8>) -> bool);
811    }
812
813    #[test]
814    fn test_fuzz_address_type_from_slice() {
815        fn internal(data: Vec<u8>) -> bool {
816            let network = if data.is_empty() {
817                Network::Mainnet
818            } else {
819                match data.len() % 3 {
820                    0 => Network::Mainnet,
821                    1 => Network::Testnet,
822                    _ => Network::Stagenet,
823                }
824            };
825            let _ = AddressType::from_slice(&data, network);
826            true
827        }
828
829        const TESTS: u64 = 10_000;
830
831        QuickCheck::new()
832            .min_tests_passed(TESTS)
833            .tests(TESTS)
834            .max_tests(TESTS)
835            .quickcheck(internal as fn(Vec<u8>) -> bool);
836    }
837
838    // ---------------------------------------------------------------------------------------------
839    // Code coverage section
840    // ---------------------------------------------------------------------------------------------
841    #[test]
842    fn test_fuzz_block_deserialize_coverage() {
843        let data = [
844            12, 12, 148, 222, 186, 248, 5, 190, 179, 72, 156, 114, 42, 40, 92, 9, 42, 50, 231, 198,
845            137, 58, 191, 199, 208, 105, 105, 156, 131, 38, 252, 52, 69, 167, 73, 197, 39, 107, 98,
846            0, 0, 0, 0, 2, 155, 137, 34, 1, 255, 223, 136, 34, 1, 182, 153, 212, 200, 177, 236, 2,
847            2, 35, 223, 82, 74, 242, 162, 239, 95, 135, 10, 219, 110, 28, 235, 3, 164, 117, 195,
848            159, 139, 158, 247, 106, 165, 11, 70, 221, 210, 161, 131, 73, 64, 43, 1, 40, 57, 191,
849            161, 155, 117, 36, 236, 116, 136, 145, 119, 20, 194, 22, 202, 37, 75, 56, 237, 4, 36,
850            202, 101, 174, 130, 138, 124, 0, 106, 234, 241, 2, 8, 245, 49, 106, 127, 107, 153, 204,
851            166, 0, 0,
852        ];
853        fuzz_block_deserialize(&data);
854    }
855
856    #[test]
857    fn test_fuzz_block_header_deserialize_coverage() {
858        let data = [
859            12, 12, 148, 222, 186, 248, 5, 190, 179, 72, 156, 114, 42, 40, 92, 9, 42, 50, 231, 198,
860            137, 58, 191, 199, 208, 105, 105, 156, 131, 38, 252, 52, 69, 167, 73, 197, 39, 107, 98,
861            0, 0, 0, 0,
862        ];
863        fuzz_block_header_deserialize(&data);
864    }
865
866    #[test]
867    fn test_fuzz_transaction_prefix_deserialize_coverage() {
868        let data = [
869            2, 155, 137, 34, 1, 255, 223, 136, 34, 1, 182, 153, 212, 200, 177, 236, 2, 2, 35, 223,
870            82, 74, 242, 162, 239, 95, 135, 10, 219, 110, 28, 235, 3, 164, 117, 195, 159, 139, 158,
871            247, 106, 165, 11, 70, 221, 210, 161, 131, 73, 64, 43, 1, 40, 57, 191, 161, 155, 117,
872            36, 236, 116, 136, 145, 119, 20, 194, 22, 202, 37, 75, 56, 237, 4, 36, 202, 101, 174,
873            130, 138, 124, 0, 106, 234, 241, 2, 8, 245, 49, 106, 127, 107, 153, 204, 166,
874        ];
875        fuzz_transaction_prefix_deserialize(&data);
876    }
877
878    #[test]
879    fn test_fuzz_extra_field_parse_sub_fields_coverage() {
880        let extra_field_1 = fuzz_create_extra_field(&[0, 1, 2], AddPadding::ToFront);
881        let extra_field_2 = fuzz_create_extra_field(&[0, 1, 2], AddPadding::ToMiddle);
882        assert_ne!(extra_field_1, extra_field_2);
883
884        let mut extra_field = fuzz_create_extra_field(&[], AddPadding::ToRear);
885        extra_field.0.pop();
886        extra_field.0.pop();
887        fuzz_extra_field_parse_sub_fields(&extra_field);
888    }
889
890    #[test]
891    fn test_fuzz_extra_field_try_parse_coverage() {
892        let extra_field = fuzz_create_extra_field(&[], AddPadding::ToRear);
893        fuzz_extra_field_try_parse(&extra_field, AddPadding::ToRear, &[]);
894        let data = [
895            230, 196, 73, 143, 43, 56, 217, 81, 1, 244, 76, 0, 106, 157, 99, 164, 0, 128, 107, 252,
896            189, 156, 211, 217, 79, 231, 213, 136, 104, 65, 213, 255, 90, 255, 15, 64, 244, 201,
897            135, 97, 135, 0, 21, 174, 185, 65, 184, 27, 229, 84, 182, 255, 236, 217, 32, 1, 63,
898        ];
899        let mut extra_field = fuzz_create_extra_field(&data, AddPadding::ToMiddle);
900        extra_field.0.pop();
901        fuzz_extra_field_try_parse(&extra_field, AddPadding::ToMiddle, &[]);
902    }
903
904    #[test]
905    fn test_fuzz_transaction_deserialize_coverage() {
906        use std::panic;
907        let _result = panic::catch_unwind(|| {
908            let data = [1];
909            fuzz_transaction_deserialize(&data);
910        });
911    }
912
913    #[test]
914    fn test_fuzz_remaining_coverage() {
915        let data = [1];
916        fuzz_transaction_components(&data);
917        fuzz_hash_convert(&data);
918        let raw_extra_field = fuzz_create_raw_extra_field(&data);
919        fuzz_raw_extra_field_deserialize(&raw_extra_field);
920        fuzz_raw_extra_field_from(&data);
921        fuzz_transaction_hash(&fuzz_create_transaction_alternative_2(
922            &data,
923            &raw_extra_field,
924        ));
925        fuzz_transaction_check_outputs(&fuzz_create_transaction_alternative_2(
926            &data,
927            &raw_extra_field,
928        ));
929    }
930}