Skip to content

Commit 95b271f

Browse files
committed
add fuzztest for equivalence between new and old transaction decoding
1 parent 426722b commit 95b271f

2 files changed

Lines changed: 81 additions & 0 deletions

File tree

fuzz/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ allowed_duplicates = [
3333
"wasi",
3434
]
3535

36+
[[bin]]
37+
name = "decode_equiv_transaction"
38+
path = "fuzz_targets/decode_equiv_transaction.rs"
39+
test = false
40+
doc = false
41+
bench = false
42+
3643
[[bin]]
3744
name = "deserialize_block"
3845
path = "fuzz_targets/deserialize_block.rs"
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Fuzz test for equivalent decoding between the legacy and consensus-encoding decoding schemes.
4+
5+
#![cfg_attr(fuzzing, no_main)]
6+
#![cfg_attr(not(fuzzing), allow(unused))]
7+
use libfuzzer_sys::fuzz_target;
8+
9+
use elements::bitcoin::hex::DisplayHex as _;
10+
11+
type Target = elements::Transaction;
12+
13+
#[cfg(not(fuzzing))]
14+
fn main() {}
15+
16+
struct DisplayError<'e>(&'e dyn std::error::Error);
17+
18+
impl core::fmt::Display for DisplayError<'_> {
19+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20+
write!(f, "{}\n", self.0)?;
21+
let mut e = self.0;
22+
while let Some(source) = e.source() {
23+
write!(f, "caused by {}\n", source)?;
24+
e = source;
25+
}
26+
Ok(())
27+
}
28+
}
29+
30+
fn do_test(data: &[u8]) {
31+
let old: Result<Target, _> = elements::encode::deserialize(data);
32+
let new: Result<Target, _> = elements::encoding::decode_from_slice(data);
33+
34+
match (old, new) {
35+
(Err(_), Err(_)) => {},
36+
(Err(e), Ok(new)) => {
37+
let e = DisplayError(&e);
38+
panic!("Decodable trait failed with {e}; Decode parsed {:?}", new);
39+
}
40+
(Ok(old), Err(e)) => {
41+
let e = DisplayError(&e);
42+
panic!("Decode trait failed with {e}; Decodable parsed {:?}", old);
43+
}
44+
(Ok(old), Ok(new)) => {
45+
assert_eq!(
46+
old, new,
47+
"Decodable (left) did not match Decode (right)",
48+
);
49+
50+
let reser_old = elements::encode::serialize(&old);
51+
let reser_new = elements::encoding::encode_to_vec(&new);
52+
assert_eq!(
53+
reser_old, reser_new,
54+
"Encodable (left) did not match Encode (right)\nOld hex: {}\nNew hex: {}\nTransaction: {:?}",
55+
reser_old.as_hex(),
56+
reser_new.as_hex(),
57+
old,
58+
);
59+
}
60+
}
61+
}
62+
63+
fuzz_target!(|data: &[u8]| {
64+
do_test(data);
65+
});
66+
67+
#[cfg(test)]
68+
mod tests {
69+
#[test]
70+
fn duplicate_crash() {
71+
let v = elements::hex::decode_to_vec("abcd");
72+
super::do_test(&v);
73+
}
74+
}

0 commit comments

Comments
 (0)