Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Generic Normalize impl for arithmetic and npos-elections #6374

Merged
10 commits merged into from
Jun 24, 2020
Merged
17 changes: 10 additions & 7 deletions frame/staking/fuzzer/src/submit_solution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ enum Mode {
}

pub fn new_test_ext(iterations: u32) -> sp_io::TestExternalities {
let mut ext: sp_io::TestExternalities = frame_system::GenesisConfig::default().build_storage::<mock::Test>().map(Into::into)
let mut ext: sp_io::TestExternalities = frame_system::GenesisConfig::default()
.build_storage::<mock::Test>()
.map(Into::into)
.expect("Failed to create test externalities.");

let (offchain, offchain_state) = TestOffchainExt::new();
Expand All @@ -70,38 +72,41 @@ fn main() {
loop {
fuzz!(|data: (u32, u32, u32, u32, u32)| {
let (mut num_validators, mut num_nominators, mut edge_per_voter, mut to_elect, mode_u32) = data;
// always run with 5 iterations.
let mut ext = new_test_ext(5);
let mode: Mode = unsafe { std::mem::transmute(mode_u32) };
num_validators = to_range(num_validators, 50, 1000);
num_nominators = to_range(num_nominators, 50, 2000);
edge_per_voter = to_range(edge_per_voter, 1, 16);
to_elect = to_range(to_elect, 20, num_validators);

let do_reduce = true;

println!("+++ instance with params {} / {} / {} / {:?}({}) / {}",
println!("+++ instance with params {} / {} / {} / {} / {:?}({})",
num_nominators,
num_validators,
edge_per_voter,
to_elect,
mode,
mode_u32,
to_elect,
);

ext.execute_with(|| {
// initial setup
init_active_era();

assert_ok!(create_validators_with_nominators_for_era::<Test>(
num_validators,
num_nominators,
edge_per_voter as usize,
true,
None,
));

<EraElectionStatus<Test>>::put(ElectionStatus::Open(1));
assert!(<Staking<Test>>::create_stakers_snapshot().0);
let origin = RawOrigin::Signed(create_funded_user::<Test>("fuzzer", 0, 100));

println!("++ Chain setup done.");
let origin = RawOrigin::Signed(create_funded_user::<Test>("fuzzer", 0, 100));

// stuff to submit
let (winners, compact, score, size) = match mode {
Expand Down Expand Up @@ -141,8 +146,6 @@ fn main() {
}
};

println!("++ Submission ready. Score = {:?}", score);

// must have chosen correct number of winners.
assert_eq!(winners.len() as u32, <Staking<Test>>::validator_count());

Expand Down
3 changes: 2 additions & 1 deletion frame/staking/src/offchain_election.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,8 @@ pub fn prepare_submission<T: Trait>(
}

// Convert back to ratio assignment. This takes less space.
let low_accuracy_assignment = sp_npos_elections::assignment_staked_to_ratio(staked);
let low_accuracy_assignment = sp_npos_elections::assignment_staked_to_ratio_normalized(staked)
.map_err(|e| OffchainElectionError::from(e))?;

// convert back to staked to compute the score in the receiver's accuracy. This can be done
// nicer, for now we do it as such since this code is not time-critical. This ensure that the
Expand Down
7 changes: 2 additions & 5 deletions frame/staking/src/testing_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,8 @@ pub fn get_weak_solution<T: Trait>(
};

// convert back to ratio assignment. This takes less space.
let low_accuracy_assignment: Vec<Assignment<T::AccountId, OffchainAccuracy>> =
staked_assignments
.into_iter()
.map(|sa| sa.into_assignment(true))
.collect();
let low_accuracy_assignment = assignment_staked_to_ratio_normalized(staked_assignments)
.expect("Failed to normalize");

// re-calculate score based on what the chain will decode.
let score = {
Expand Down
4 changes: 4 additions & 0 deletions primitives/arithmetic/fuzzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ num-traits = "0.2"
name = "biguint"
path = "src/biguint.rs"

[[bin]]
name = "normalize"
path = "src/normalize.rs"

[[bin]]
name = "per_thing_rational"
path = "src/per_thing_rational.rs"
Expand Down
62 changes: 62 additions & 0 deletions primitives/arithmetic/fuzzer/src/normalize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// This file is part of Substrate.

// Copyright (C) 2020 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.


//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run normalize`. `honggfuzz` CLI options can
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug normalize hfuzz_workspace/normalize/*.fuzz`.

use honggfuzz::fuzz;
use sp_arithmetic::Normalizable;
use std::convert::TryInto;

fn main() {
let sum_limit = u32::max_value() as u128;
let len_limit: usize = u32::max_value().try_into().unwrap();

loop {
fuzz!(|data: (Vec<u32>, u32)| {
let (data, norm) = data;
if data.len() == 0 { return; }
let pre_sum: u128 = data.iter().map(|x| *x as u128).sum();

let normalized = data.normalize(norm);
// error cases.
if pre_sum > sum_limit || data.len() > len_limit {
assert!(normalized.is_err())
} else {
if let Ok(normalized) = normalized {
// if sum goes beyond u128, panic.
let sum: u128 = normalized.iter().map(|x| *x as u128).sum();

// if this function returns Ok(), then it will ALWAYS be accurate.
assert_eq!(
sum,
norm as u128,
"sums don't match {:?}, {}",
normalized,
norm,
);
}
}
})
}
}
2 changes: 1 addition & 1 deletion primitives/arithmetic/fuzzer/src/per_thing_rational.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fn main() {
}
}

fn assert_per_thing_equal_error<T: PerThing>(a: T, b: T, err: u128) {
fn assert_per_thing_equal_error<P: PerThing>(a: P, b: P, err: u128) {
let a_abs = a.deconstruct().saturated_into::<u128>();
let b_abs = b.deconstruct().saturated_into::<u128>();
let diff = a_abs.max(b_abs) - a_abs.min(b_abs);
Expand Down
Loading