|
| 1 | +use arbitrary::{Arbitrary, Unstructured}; |
| 2 | +use honggfuzz::fuzz; |
| 3 | + |
| 4 | +use bitcoin::{Network}; |
| 5 | +use bitcoin::address::Address; |
| 6 | +use bitcoin::consensus::serialize; |
| 7 | +use bitcoin::script::{self, ScriptBuf, ScriptExt as _, ScriptPubKeyExt as _}; |
| 8 | + |
| 9 | +fn do_test(data: &[u8]) { |
| 10 | + let mut u = Unstructured::new(data); |
| 11 | + let s = ScriptBuf::arbitrary(&mut u); |
| 12 | + |
| 13 | + if let Ok(script_buf) = s { |
| 14 | + let serialized = serialize(&script_buf); |
| 15 | + let _ : Result<Vec<script::Instruction>, script::Error> = script_buf.instructions().collect(); |
| 16 | + |
| 17 | + let _ = script_buf.to_string(); |
| 18 | + let _ = script_buf.count_sigops(); |
| 19 | + let _ = script_buf.count_sigops_legacy(); |
| 20 | + let _ = script_buf.minimal_non_dust(); |
| 21 | + let _ = script_buf.minimal_non_dust_custom(u.arbitrary().expect("valid arbitrary FeeRate")); |
| 22 | + |
| 23 | + let mut builder = script::Builder::new(); |
| 24 | + for instruction in script_buf.instructions_minimal() { |
| 25 | + if instruction.is_err() { |
| 26 | + return; |
| 27 | + } |
| 28 | + match instruction.ok().unwrap() { |
| 29 | + script::Instruction::Op(op) => { |
| 30 | + builder = builder.push_opcode(op); |
| 31 | + } |
| 32 | + script::Instruction::PushBytes(bytes) => { |
| 33 | + // While we enforce the minimality rule for minimal PUSHDATA opcodes, we don't |
| 34 | + // enforce the minimality of numbers since we don't have a script engine |
| 35 | + // to determine if the number is getting fed into a numeric opcode, which is |
| 36 | + // when the minimality of numbers is required. |
| 37 | + builder = builder.push_slice_non_minimal(bytes) |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + assert_eq!(builder.into_script(), script_buf); |
| 42 | + assert_eq!(serialized, &serialize(&script_buf)[..]); |
| 43 | + |
| 44 | + // Check if valid address and if that address roundtrips. |
| 45 | + if let Ok(addr) = Address::from_script(&script_buf, Network::Bitcoin) { |
| 46 | + assert_eq!(addr.script_pubkey(), script_buf); |
| 47 | + } |
| 48 | + } |
| 49 | +} |
| 50 | + |
| 51 | +fn main() { |
| 52 | + loop { |
| 53 | + fuzz!(|data| { |
| 54 | + do_test(data); |
| 55 | + }); |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +#[cfg(all(test, fuzzing))] |
| 60 | +mod tests { |
| 61 | + fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) { |
| 62 | + let mut b = 0; |
| 63 | + for (idx, c) in hex.as_bytes().iter().enumerate() { |
| 64 | + b <<= 4; |
| 65 | + match *c { |
| 66 | + b'A'..=b'F' => b |= c - b'A' + 10, |
| 67 | + b'a'..=b'f' => b |= c - b'a' + 10, |
| 68 | + b'0'..=b'9' => b |= c - b'0', |
| 69 | + _ => panic!("Bad hex"), |
| 70 | + } |
| 71 | + if (idx & 1) == 1 { |
| 72 | + out.push(b); |
| 73 | + b = 0; |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + #[test] |
| 79 | + fn duplicate_crash() { |
| 80 | + let mut a = Vec::new(); |
| 81 | + extend_vec_from_hex("00", &mut a); |
| 82 | + super::do_test(&a); |
| 83 | + } |
| 84 | +} |
0 commit comments