diff --git a/Cargo.lock b/Cargo.lock index 741f2ba7c48c4..81fcee59cda9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8250,6 +8250,7 @@ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ "honggfuzz", + "parity-scale-codec", "rand 0.7.3", "sp-npos-elections", "sp-runtime", diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index f0c9442aade5f..4d262bc50074e 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -19,6 +19,7 @@ sp-std = { version = "2.0.0-rc6", path = "../../std" } sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } honggfuzz = "0.5" rand = { version = "0.7.3", features = ["std", "small_rng"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [[bin]] name = "reduce" @@ -27,3 +28,7 @@ path = "src/reduce.rs" [[bin]] name = "balance_solution" path = "src/balance_solution.rs" + +[[bin]] +name = "compact" +path = "src/compact.rs" diff --git a/primitives/npos-elections/fuzzer/src/compact.rs b/primitives/npos-elections/fuzzer/src/compact.rs new file mode 100644 index 0000000000000..91f734bb5b7cb --- /dev/null +++ b/primitives/npos-elections/fuzzer/src/compact.rs @@ -0,0 +1,34 @@ +use honggfuzz::fuzz; +use sp_npos_elections::generate_solution_type; +use sp_npos_elections::sp_arithmetic::Percent; +use sp_runtime::codec::{Encode, Error}; + +fn main() { + generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::(16)); + loop { + fuzz!(|fuzzer_data: &[u8]| { + let result_decoded: Result = + ::decode(&mut &fuzzer_data[..]); + // Ignore errors as not every random sequence of bytes can be decoded as InnerTestSolutionCompact + if let Ok(decoded) = result_decoded { + // Decoding works, let's re-encode it and compare results. + let reencoded: std::vec::Vec = decoded.encode(); + // The reencoded value may or may not be equal to the original fuzzer output. However, the + // original decoder should be optimal (in the sense that there is no shorter encoding of + // the same object). So let's see if the fuzzer can find something shorter: + if fuzzer_data.len() < reencoded.len() { + panic!("fuzzer_data.len() < reencoded.len()"); + } + // The reencoded value should definitely be decodable (if unwrap() fails that is a valid + // panic/finding for the fuzzer): + let decoded2: InnerTestSolutionCompact = + ::decode( + &mut reencoded.as_slice(), + ).unwrap(); + // And it should be equal to the original decoded object (resulting from directly + // decoding fuzzer_data): + assert_eq!(decoded, decoded2); + } + }); + } +}