diff --git a/substrate/core/phragmen/benches/phragmen.rs b/substrate/core/phragmen/benches/phragmen.rs index ae1daac468..fec849ab93 100644 --- a/substrate/core/phragmen/benches/phragmen.rs +++ b/substrate/core/phragmen/benches/phragmen.rs @@ -24,7 +24,7 @@ use test::Bencher; use rand::{self, Rng}; extern crate substrate_phragmen as phragmen; -use phragmen::{Support, SupportMap, ACCURACY}; +use phragmen::{Support, SupportMap, PhragmenStakedAssignment}; use std::collections::BTreeMap; use sr_primitives::traits::{Convert, SaturatedConversion}; @@ -100,11 +100,10 @@ fn do_phragmen( // Do the benchmarking with equalize. if eq_iters > 0 { let elected_stashes = r.winners; - let mut assignments = r.assignments; + let assignments = r.assignments; let to_votes = |b: Balance| >::convert(b) as u128; - let ratio_of = |b, r: u128| r.saturating_mul(to_votes(b)) / ACCURACY; // Initialize the support of each candidate. let mut supports = >::new(); @@ -116,22 +115,37 @@ fn do_phragmen( supports.insert(e.clone(), item); }); - for (n, assignment) in assignments.iter_mut() { - for (c, r) in assignment.iter_mut() { - let nominator_stake = slashable_balance(n); - let other_stake = ratio_of(nominator_stake, *r); + // build support struct. + for (n, assignment) in assignments.iter() { + for (c, per_thing) in assignment.iter() { + let nominator_stake = to_votes(slashable_balance(n)); + let other_stake = *per_thing * nominator_stake; if let Some(support) = supports.get_mut(c) { support.total = support.total.saturating_add(other_stake); support.others.push((n.clone(), other_stake)); } - *r = other_stake; } } + let mut staked_assignments + : Vec<(AccountId, Vec>)> + = Vec::with_capacity(assignments.len()); + for (n, assignment) in assignments.iter() { + let mut staked_assignment + : Vec> + = Vec::with_capacity(assignment.len()); + for (c, per_thing) in assignment.iter() { + let nominator_stake = to_votes(slashable_balance(n)); + let other_stake = *per_thing * nominator_stake; + staked_assignment.push((c.clone(), other_stake)); + } + staked_assignments.push((n.clone(), staked_assignment)); + } + let tolerance = 0_u128; let iterations = 2_usize; - phragmen::equalize::<_, _, _, TestCurrencyToVote>( - assignments, + phragmen::equalize::<_, _, TestCurrencyToVote, _>( + staked_assignments, &mut supports, tolerance, iterations, diff --git a/substrate/core/phragmen/src/lib.rs b/substrate/core/phragmen/src/lib.rs index 2d4580d2e0..8ea3d55552 100644 --- a/substrate/core/phragmen/src/lib.rs +++ b/substrate/core/phragmen/src/lib.rs @@ -34,15 +34,12 @@ #![cfg_attr(not(feature = "std"), no_std)] use rstd::{prelude::*, collections::btree_map::BTreeMap}; -use sr_primitives::PerU128; -use sr_primitives::traits::{Zero, Convert, Member, SimpleArithmetic}; +use sr_primitives::{helpers_128bit::multiply_by_rational_best_effort, Perbill, Rational128}; +use sr_primitives::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating}; mod mock; mod tests; -/// Type used as the fraction. -type Fraction = PerU128; - /// A type in which performing operations on balances and stakes of candidates and voters are safe. /// /// This module's functions expect a `Convert` type to convert all balances to u64. Hence, u128 is @@ -51,16 +48,10 @@ type Fraction = PerU128; /// Balance types converted to `ExtendedBalance` are referred to as `Votes`. pub type ExtendedBalance = u128; -// this is only used while creating the candidate score. Due to reasons explained below -// The more accurate this is, the less likely we choose a wrong candidate. -// TODO: can be removed with proper use of per-things #2908 -const SCALE_FACTOR: ExtendedBalance = u32::max_value() as ExtendedBalance + 1; - -/// These are used to expose a fixed accuracy to the caller function. The bigger they are, -/// the more accurate we get, but the more likely it is for us to overflow. The case of overflow -/// is handled but accuracy will be lost. 32 or 16 are reasonable values. -// TODO: can be removed with proper use of per-things #2908 -pub const ACCURACY: ExtendedBalance = u32::max_value() as ExtendedBalance + 1; +/// The denominator used for loads. Since votes are collected as u64, the smallest ratio that we +/// might collect is `1/approval_stake` where approval stake is the sum of votes. Hence, some number +/// bigger than u64::max_value() is needed. For maximum accuracy we simply use u128; +const DEN: u128 = u128::max_value(); /// A candidate entity for phragmen election. #[derive(Clone, Default)] @@ -69,7 +60,7 @@ pub struct Candidate { /// Identifier. pub who: AccountId, /// Intermediary value used to sort candidates. - pub score: Fraction, + pub score: Rational128, /// Sum of the stake of this candidate based on received votes. approval_stake: ExtendedBalance, /// Flag for being elected. @@ -87,7 +78,7 @@ pub struct Voter { /// The stake of this voter. budget: ExtendedBalance, /// Incremented each time a candidate that this voter voted for has been elected. - load: Fraction, + load: Rational128, } /// A candidate being backed by a voter. @@ -97,13 +88,16 @@ pub struct Edge { /// Identifier. who: AccountId, /// Load of this vote. - load: Fraction, + load: Rational128, /// Index of the candidate stored in the 'candidates' vector. candidate_index: usize, } -/// Means a particular `AccountId` was backed by a ratio of `ExtendedBalance / ACCURACY`. -pub type PhragmenAssignment = (AccountId, ExtendedBalance); +/// Means a particular `AccountId` was backed by `Perbill`th of a nominator's stake. +pub type PhragmenAssignment = (AccountId, Perbill); + +/// Means a particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake. +pub type PhragmenStakedAssignment = (AccountId, ExtendedBalance); /// Final result of the phragmen election. #[cfg_attr(feature = "std", derive(Debug))] @@ -131,7 +125,7 @@ pub struct Support { /// Total support. pub total: ExtendedBalance, /// Support from voters. - pub others: Vec>, + pub others: Vec>, } /// A linkage from a candidate and its [`Support`]. @@ -164,8 +158,7 @@ pub fn elect( for<'r> FS: Fn(&'r AccountId) -> Balance, C: Convert + Convert, { - let to_votes = |b: Balance| - >::convert(b) as ExtendedBalance; + let to_votes = |b: Balance| >::convert(b) as ExtendedBalance; // return structures let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>; @@ -192,7 +185,7 @@ pub fn elect( who: c.who.clone(), edges: vec![Edge { who: c.who.clone(), candidate_index: i, ..Default::default() }], budget: c.approval_stake, - load: Fraction::zero(), + load: Rational128::zero(), }); c_idx_cache.insert(c.who.clone(), i); c @@ -229,7 +222,7 @@ pub fn elect( who, edges: edges, budget: to_votes(voter_stake), - load: Fraction::zero(), + load: Rational128::zero(), } })); @@ -245,24 +238,29 @@ pub fn elect( // loop 1: initialize score for c in &mut candidates { if !c.elected { - c.score = Fraction::from_xth(c.approval_stake); + // 1 / approval_stake == (DEN / approval_stake) / DEN. If approval_stake is zero, + // then the ratio should be as large as possible, essentially `infinity`. + if c.approval_stake.is_zero() { + c.score = Rational128::from_unchecked(DEN, 0); + } else { + c.score = Rational128::from(DEN / c.approval_stake, DEN); + } } } + // loop 2: increment score for n in &voters { for e in &n.edges { let c = &mut candidates[e.candidate_index]; if !c.elected && !c.approval_stake.is_zero() { - // Basic fixed-point shifting by 32. - // `n.budget.saturating_mul(SCALE_FACTOR)` will never saturate - // since n.budget cannot exceed u64,despite being stored in u128. yet, - // `*n.load / SCALE_FACTOR` might collapse to zero. Hence, 32 or 16 bits are - // better scale factors. Note that left-associativity in operators precedence is - // crucially important here. - let temp = - n.budget.saturating_mul(SCALE_FACTOR) / c.approval_stake - * (*n.load / SCALE_FACTOR); - c.score = Fraction::from_parts((*c.score).saturating_add(temp)); + let temp_n = multiply_by_rational_best_effort( + n.load.n(), + n.budget, + c.approval_stake, + ); + let temp_d = n.load.d(); + let temp = Rational128::from(temp_n, temp_d); + c.score = c.score.lazy_saturating_add(temp); } } } @@ -271,14 +269,14 @@ pub fn elect( if let Some(winner) = candidates .iter_mut() .filter(|c| !c.elected) - .min_by_key(|c| *c.score) + .min_by_key(|c| c.score) { // loop 3: update voter and edge load winner.elected = true; for n in &mut voters { for e in &mut n.edges { if e.who == winner.who { - e.load = Fraction::from_parts(*winner.score - *n.load); + e.load = winner.score.lazy_saturating_sub(n.load); n.load = winner.score; } } @@ -296,48 +294,64 @@ pub fn elect( for e in &mut n.edges { if let Some(c) = elected_candidates.iter().cloned().find(|(c, _)| *c == e.who) { if c.0 != n.who { - let ratio = { - // Full support. No need to calculate. - if *n.load == *e.load { ACCURACY } - else { - // This should not saturate. Safest is to just check - if let Some(r) = ACCURACY.checked_mul(*e.load) { - r / n.load.max(1) + let per_bill_parts = + { + if n.load == e.load { + // Full support. No need to calculate. + Perbill::accuracy().into() + } else { + if e.load.d() == n.load.d() { + // return e.load / n.load. + let desired_scale: u128 = Perbill::accuracy().into(); + multiply_by_rational_best_effort( + desired_scale, + e.load.n(), + n.load.n(), + ) } else { - // Just a simple trick. - *e.load / (n.load.max(1) / ACCURACY) + // defensive only. Both edge and nominator loads are built from + // scores, hence MUST have the same denominator. + Zero::zero() } } }; - assignment.1.push((e.who.clone(), ratio)); + // safer to .min() inside as well to argue as u32 is safe. + let per_thing = Perbill::from_parts( + per_bill_parts.min(Perbill::accuracy().into()) as u32 + ); + assignment.1.push((e.who.clone(), per_thing)); } } } if assignment.1.len() > 0 { - // To ensure an assertion indicating: no stake from the voter going to waste, we add - // a minimal post-processing to equally assign all of the leftover stake ratios. - let vote_count = assignment.1.len() as ExtendedBalance; - let l = assignment.1.len(); - let sum = assignment.1.iter().map(|a| a.1).sum::(); - let diff = ACCURACY.checked_sub(sum).unwrap_or(0); - let diff_per_vote= diff / vote_count; + // To ensure an assertion indicating: no stake from the nominator going to waste, + // we add a minimal post-processing to equally assign all of the leftover stake ratios. + let vote_count = assignment.1.len() as u32; + let len = assignment.1.len(); + let sum = assignment.1.iter() + .map(|a| a.1.deconstruct()) + .sum::(); + let accuracy = Perbill::accuracy(); + let diff = accuracy.checked_sub(sum).unwrap_or(0); + let diff_per_vote = (diff / vote_count).min(accuracy); if diff_per_vote > 0 { - for i in 0..l { - assignment.1[i%l].1 = - assignment.1[i%l].1 - .saturating_add(diff_per_vote); + for i in 0..len { + let current_ratio = assignment.1[i % len].1; + let next_ratio = current_ratio + .saturating_add(Perbill::from_parts(diff_per_vote)); + assignment.1[i % len].1 = next_ratio; } } - // `remainder` is set to be less than maximum votes of a voter (currently 16). + // `remainder` is set to be less than maximum votes of a nominator (currently 16). // safe to cast it to usize. let remainder = diff - diff_per_vote * vote_count; for i in 0..remainder as usize { - assignment.1[i%l].1 = - assignment.1[i%l].1 - .saturating_add(1); + let current_ratio = assignment.1[i % len].1; + let next_ratio = current_ratio.saturating_add(Perbill::from_parts(1)); + assignment.1[i % len].1 = next_ratio; } assigned.push(assignment); } @@ -360,8 +374,8 @@ pub fn elect( /// * `tolerance`: maximum difference that can occur before an early quite happens. /// * `iterations`: maximum number of iterations that will be processed. /// * `stake_of`: something that can return the stake stake of a particular candidate or voter. -pub fn equalize( - mut assignments: Vec<(AccountId, Vec>)>, +pub fn equalize( + mut assignments: Vec<(AccountId, Vec>)>, supports: &mut SupportMap, tolerance: ExtendedBalance, iterations: usize, @@ -399,7 +413,7 @@ pub fn equalize( fn do_equalize( voter: &AccountId, budget_balance: Balance, - elected_edges: &mut Vec<(AccountId, ExtendedBalance)>, + elected_edges: &mut Vec>, support_map: &mut SupportMap, tolerance: ExtendedBalance ) -> ExtendedBalance where diff --git a/substrate/core/phragmen/src/mock.rs b/substrate/core/phragmen/src/mock.rs index e4f220affc..ee4e1eb4bb 100644 --- a/substrate/core/phragmen/src/mock.rs +++ b/substrate/core/phragmen/src/mock.rs @@ -18,10 +18,12 @@ #![cfg(test)] -use crate::{elect, ACCURACY, PhragmenResult}; -use sr_primitives::traits::{Convert, Member, SaturatedConversion}; +use crate::{elect, PhragmenResult, PhragmenAssignment}; +use sr_primitives::{ + assert_eq_error_rate, Perbill, + traits::{Convert, Member, SaturatedConversion} +}; use rstd::collections::btree_map::BTreeMap; -use support::assert_eq_error_rate; pub(crate) struct TestCurrencyToVote; impl Convert for TestCurrencyToVote { @@ -343,6 +345,14 @@ pub(crate) fn create_stake_of(stakes: &[(AccountId, Balance)]) Box::new(stake_of) } + +pub fn check_assignments(assignments: Vec<(AccountId, Vec>)>) { + for (_, a) in assignments { + let sum: u32 = a.iter().map(|(_, p)| p.deconstruct()).sum(); + assert_eq_error_rate!(sum, Perbill::accuracy(), 5); + } +} + pub(crate) fn run_and_compare( candidates: Vec, voters: Vec<(AccountId, Vec)>, @@ -375,9 +385,13 @@ pub(crate) fn run_and_compare( for (nominator, assigned) in assignments.clone() { if let Some(float_assignments) = truth_value.assignments.iter().find(|x| x.0 == nominator) { - for (candidate, ratio) in assigned { + for (candidate, per_thingy) in assigned { if let Some(float_assignment) = float_assignments.1.iter().find(|x| x.0 == candidate ) { - assert_eq_error_rate!((float_assignment.1 * ACCURACY as f64).round() as u128, ratio, 1); + assert_eq_error_rate!( + Perbill::from_fraction(float_assignment.1).deconstruct(), + per_thingy.deconstruct(), + 1, + ); } else { panic!("candidate mismatch. This should never happen.") } @@ -386,6 +400,8 @@ pub(crate) fn run_and_compare( panic!("nominator mismatch. This should never happen.") } } + + check_assignments(assignments); } pub(crate) fn build_support_map( @@ -414,6 +430,5 @@ pub(crate) fn build_support_map( *r = other_stake; } } - supports } diff --git a/substrate/core/phragmen/src/tests.rs b/substrate/core/phragmen/src/tests.rs index 750adb49ce..d3c5b1168c 100644 --- a/substrate/core/phragmen/src/tests.rs +++ b/substrate/core/phragmen/src/tests.rs @@ -19,8 +19,9 @@ #![cfg(test)] use crate::mock::*; -use crate::{elect, ACCURACY, PhragmenResult}; +use crate::{elect, PhragmenResult}; use support::assert_eq_uvec; +use sr_primitives::Perbill; #[test] fn float_phragmen_poc_works() { @@ -90,9 +91,9 @@ fn phragmen_poc_works() { assert_eq_uvec!( assignments, vec![ - (10, vec![(2, ACCURACY)]), - (20, vec![(3, ACCURACY)]), - (30, vec![(2, ACCURACY/2), (3, ACCURACY/2)]), + (10, vec![(2, Perbill::from_percent(100))]), + (20, vec![(3, Perbill::from_percent(100))]), + (30, vec![(2, Perbill::from_percent(100/2)), (3, Perbill::from_percent(100/2))]), ] ); } @@ -133,3 +134,218 @@ fn phragmen_poc_3_works() { run_and_compare(candidates, voters, stake_of, 2, 2, true); } + +#[test] +fn phragmen_accuracy_on_large_scale_only_validators() { + // because of this particular situation we had per_u128 and now rational128. In practice, a + // candidate can have the maximum amount of tokens, and also supported by the maximum. + let candidates = vec![1, 2, 3, 4, 5]; + let stake_of = create_stake_of(&[ + (1, (u64::max_value() - 1).into()), + (2, (u64::max_value() - 4).into()), + (3, (u64::max_value() - 5).into()), + (4, (u64::max_value() - 3).into()), + (5, (u64::max_value() - 2).into()), + ]); + + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + 2, + 2, + candidates, + vec![], + stake_of, + true, + ).unwrap(); + + assert_eq_uvec!(winners, vec![(1, 18446744073709551614u128), (5, 18446744073709551613u128)]); + assert_eq!(assignments.len(), 0); + check_assignments(assignments); +} + +#[test] +fn phragmen_accuracy_on_large_scale_validators_and_nominators() { + let candidates = vec![1, 2, 3, 4, 5]; + let voters = vec![ + (13, vec![1, 3, 5]), + (14, vec![2, 4]), + ]; + let stake_of = create_stake_of(&[ + (1, (u64::max_value() - 1).into()), + (2, (u64::max_value() - 4).into()), + (3, (u64::max_value() - 5).into()), + (4, (u64::max_value() - 3).into()), + (5, (u64::max_value() - 2).into()), + (13, (u64::max_value() - 10).into()), + (14, u64::max_value().into()), + ]); + + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + 2, + 2, + candidates, + voters, + stake_of, + true, + ).unwrap(); + + assert_eq_uvec!(winners, vec![(2, 36893488147419103226u128), (1, 36893488147419103219u128)]); + assert_eq!( + assignments, + vec![(13, vec![(1, Perbill::one())]), (14, vec![(2, Perbill::one())])] + ); + check_assignments(assignments); +} + +#[test] +fn phragmen_accuracy_on_small_scale_self_vote() { + let candidates = vec![40, 10, 20, 30]; + let voters = vec![]; + let stake_of = create_stake_of(&[ + (40, 0), + (10, 1), + (20, 2), + (30, 1), + ]); + + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>( + 3, + 3, + candidates, + voters, + stake_of, + true, + ).unwrap(); + + assert_eq_uvec!(winners, vec![(20, 2), (10, 1), (30, 1)]); +} + +#[test] +fn phragmen_accuracy_on_small_scale_no_self_vote() { + let candidates = vec![40, 10, 20, 30]; + let voters = vec![ + (1, vec![10]), + (2, vec![20]), + (3, vec![30]), + (4, vec![40]), + ]; + let stake_of = create_stake_of(&[ + (40, 1000), // don't care + (10, 1000), // don't care + (20, 1000), // don't care + (30, 1000), // don't care + (4, 0), + (1, 1), + (2, 2), + (3, 1), + ]); + + let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>( + 3, + 3, + candidates, + voters, + stake_of, + false, + ).unwrap(); + + assert_eq_uvec!(winners, vec![(20, 2), (10, 1), (30, 1)]); +} + +#[test] +fn phragmen_large_scale_test() { + let candidates = vec![2, 4, 6, 8, 10, 12, 14, 16 ,18, 20, 22, 24]; + let voters = vec![ + (50, vec![2, 4, 6, 8, 10, 12, 14, 16 ,18, 20, 22, 24]), + ]; + let stake_of = create_stake_of(&[ + (2, 1), + (4, 100), + (6, 1000000), + (8, 100000000001000), + (10, 100000000002000), + (12, 100000000003000), + (14, 400000000000000), + (16, 400000000001000), + (18, 18000000000000000), + (20, 20000000000000000), + (22, 500000000000100000), + (24, 500000000000200000), + (50, 990000000000000000), + ]); + + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + 2, + 2, + candidates, + voters, + stake_of, + true, + ).unwrap(); + + assert_eq_uvec!(winners, vec![(24, 1490000000000200000u128), (22, 1490000000000100000u128)]); + check_assignments(assignments); +} + +#[test] +fn phragmen_large_scale_test_2() { + let nom_budget: u64 = 1_000_000_000_000_000_000; + let c_budget: u64 = 4_000_000; + + let candidates = vec![2, 4]; + let voters = vec![(50, vec![2, 4])]; + + let stake_of = create_stake_of(&[ + (2, c_budget.into()), + (4, c_budget.into()), + (50, nom_budget.into()), + ]); + + let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>( + 2, + 2, + candidates, + voters, + stake_of, + true, + ).unwrap(); + + assert_eq_uvec!(winners, vec![(2, 1000000000004000000u128), (4, 1000000000004000000u128)]); + assert_eq!( + assignments, + vec![(50, vec![(2, Perbill::from_parts(500000001)), (4, Perbill::from_parts(499999999))])], + ); + check_assignments(assignments); +} + +#[test] +fn phragmen_linear_equalize() { + let candidates = vec![11, 21, 31, 41, 51, 61, 71]; + let voters = vec![ + (2, vec![11]), + (4, vec![11, 21]), + (6, vec![21, 31]), + (8, vec![31, 41]), + (110, vec![41, 51]), + (120, vec![51, 61]), + (130, vec![61, 71]), + ]; + let stake_of = create_stake_of(&[ + (11, 1000), + (21, 1000), + (31, 1000), + (41, 1000), + (51, 1000), + (61, 1000), + (71, 1000), + + (2, 2000), + (4, 1000), + (6, 1000), + (8, 1000), + (110, 1000), + (120, 1000), + (130, 1000), + ]); + + run_and_compare(candidates, voters, stake_of, 2, 2, true); +} diff --git a/substrate/core/sr-primitives/Cargo.toml b/substrate/core/sr-primitives/Cargo.toml index 5369674dca..db1819c3e6 100644 --- a/substrate/core/sr-primitives/Cargo.toml +++ b/substrate/core/sr-primitives/Cargo.toml @@ -21,6 +21,7 @@ impl-trait-for-tuples = "0.1.1" [dev-dependencies] serde_json = "1.0" primitive-types = "0.5.0" +rand = "0.7.2" [features] default = ["std"] diff --git a/substrate/core/sr-primitives/src/curve.rs b/substrate/core/sr-primitives/src/curve.rs index 447c57ee32..dc6316bd47 100644 --- a/substrate/core/sr-primitives/src/curve.rs +++ b/substrate/core/sr-primitives/src/curve.rs @@ -59,13 +59,13 @@ impl<'a> PiecewiseLinear<'a> { let delta_y = multiply_by_rational_saturating( abs_sub(n.clone(), prev.0 * d.clone()), - abs_sub(next.1.into_parts(), prev.1.into_parts()), + abs_sub(next.1.deconstruct(), prev.1.deconstruct()), // Must not saturate as prev abscissa > next abscissa - next.0.into_parts().saturating_sub(prev.0.into_parts()), + next.0.deconstruct().saturating_sub(prev.0.deconstruct()), ); // If both substration are same sign then result is positive - if (n > prev.0 * d.clone()) == (next.1.into_parts() > prev.1.into_parts()) { + if (n > prev.0 * d.clone()) == (next.1.deconstruct() > prev.1.deconstruct()) { (prev.1 * d).saturating_add(delta_y) // Otherwise result is negative } else { diff --git a/substrate/core/sr-primitives/src/lib.rs b/substrate/core/sr-primitives/src/lib.rs index 4d3bfb2c92..ddb935867e 100644 --- a/substrate/core/sr-primitives/src/lib.rs +++ b/substrate/core/sr-primitives/src/lib.rs @@ -17,7 +17,6 @@ //! Runtime Modules shared primitive types. #![warn(missing_docs)] - #![cfg_attr(not(feature = "std"), no_std)] #[doc(hidden)] @@ -37,10 +36,10 @@ pub use app_crypto; #[cfg(feature = "std")] pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; -use rstd::{prelude::*, ops, convert::{TryInto, TryFrom}}; +use rstd::prelude::*; +use rstd::convert::TryFrom; use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}}; -use codec::{Encode, Decode, CompactAs}; -use traits::{SaturatedConversion, UniqueSaturatedInto, Saturating, Bounded, CheckedSub, CheckedAdd}; +use codec::{Encode, Decode}; #[cfg(feature = "std")] pub mod testing; @@ -51,6 +50,7 @@ pub mod curve; pub mod generic; pub mod transaction_validity; +pub mod sr_arithmetic; /// Re-export these since they're only "kind of" generic. pub use generic::{DigestItem, Digest}; @@ -59,6 +59,14 @@ pub use generic::{DigestItem, Digest}; pub use primitives::crypto::{key_types, KeyTypeId, CryptoType}; pub use app_crypto::RuntimeAppPublic; +/// Re-export arithmetic stuff. +pub use sr_arithmetic::{ + Perquintill, Perbill, Permill, Percent, + Rational128, Fixed64 +}; +/// Re-export 128 bit helpers from sr_arithmetic +pub use sr_arithmetic::helpers_128bit; + /// An abstraction over justification for a block's validity under a consensus algorithm. /// /// Essentially a finality proof. The exact formulation will vary between consensus @@ -152,360 +160,6 @@ impl BuildStorage for (StorageOverlay, ChildrenStorageOverlay) { /// Consensus engine unique ID. pub type ConsensusEngineId = [u8; 4]; -/// Permill is parts-per-million (i.e. after multiplying by this, divide by 1000000). -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))] -#[derive(Encode, Decode, CompactAs, Default, Copy, Clone, PartialEq, Eq)] -pub struct Permill(u32); - -impl Permill { - /// Nothing. - pub fn zero() -> Self { Self(0) } - - /// `true` if this is nothing. - pub fn is_zero(&self) -> bool { self.0 == 0 } - - /// Everything. - pub fn one() -> Self { Self(1_000_000) } - - /// create a new raw instance. This can be called at compile time. - pub const fn from_const_parts(parts: u32) -> Self { - Self([parts, 1_000_000][(parts > 1_000_000) as usize]) - } - - /// From an explicitly defined number of parts per maximum of the type. - pub fn from_parts(parts: u32) -> Self { Self::from_const_parts(parts) } - - /// Converts from a percent. Equal to `x / 100`. - pub const fn from_percent(x: u32) -> Self { Self([x, 100][(x > 100) as usize] * 10_000) } - - /// Converts a fraction into `Permill`. - #[cfg(feature = "std")] - pub fn from_fraction(x: f64) -> Self { Self((x * 1_000_000.0) as u32) } - - /// Approximate the fraction `p/q` into a per million fraction - pub fn from_rational_approximation(p: N, q: N) -> Self - where N: traits::SimpleArithmetic + Clone - { - let p = p.min(q.clone()); - let factor = (q.clone() / 1_000_000u32.into()).max(1u32.into()); - - // Conversion can't overflow as p < q so ( p / (q/million)) < million - let p_reduce: u32 = (p / factor.clone()).try_into().unwrap_or_else(|_| panic!()); - let q_reduce: u32 = (q / factor.clone()).try_into().unwrap_or_else(|_| panic!()); - let part = p_reduce as u64 * 1_000_000u64 / q_reduce as u64; - - Permill(part as u32) - } -} - -impl ops::Mul for Permill -where - N: Clone + From + UniqueSaturatedInto + ops::Rem - + ops::Div + ops::Mul + ops::Add, -{ - type Output = N; - fn mul(self, b: N) -> Self::Output { - let million: N = 1_000_000.into(); - let part: N = self.0.into(); - - let rem_multiplied_divided = { - let rem = b.clone().rem(million.clone()); - - // `rem` is inferior to one million, thus it fits into u32 - let rem_u32 = rem.saturated_into::(); - - // `self` and `rem` are inferior to one million, thus the product is less than 10^12 - // and fits into u64 - let rem_multiplied_u64 = rem_u32 as u64 * self.0 as u64; - - // `rem_multiplied_u64` is less than 10^12 therefore divided by a million it fits into - // u32 - let rem_multiplied_divided_u32 = (rem_multiplied_u64 / 1_000_000) as u32; - - // `rem_multiplied_divided` is inferior to b, thus it can be converted back to N type - rem_multiplied_divided_u32.into() - }; - - (b / million) * part + rem_multiplied_divided - } -} - -#[cfg(feature = "std")] -impl From for Permill { - fn from(x: f64) -> Permill { - Permill::from_fraction(x) - } -} - -#[cfg(feature = "std")] -impl From for Permill { - fn from(x: f32) -> Permill { - Permill::from_fraction(x as f64) - } -} - -/// Perbill is parts-per-billion. It stores a value between 0 and 1 in fixed point and -/// provides a means to multiply some other value by that. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, CompactAs, Default, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] -pub struct Perbill(u32); - -impl Perbill { - /// Nothing. - pub fn zero() -> Self { Self(0) } - - /// `true` if this is nothing. - pub fn is_zero(&self) -> bool { self.0 == 0 } - - /// Everything. - pub fn one() -> Self { Self(1_000_000_000) } - - /// create a new raw instance. This can be called at compile time. - pub const fn from_const_parts(parts: u32) -> Self { - Self([parts, 1_000_000_000][(parts > 1_000_000_000) as usize]) - } - - /// From an explicitly defined number of parts per maximum of the type. - pub fn from_parts(parts: u32) -> Self { Self::from_const_parts(parts) } - - /// Converts from a percent. Equal to `x / 100`. - pub const fn from_percent(x: u32) -> Self { Self([x, 100][(x > 100) as usize] * 10_000_000) } - - /// Construct new instance where `x` is in millionths. Value equivalent to `x / 1,000,000`. - pub fn from_millionths(x: u32) -> Self { Self(x.min(1_000_000) * 1000) } - - #[cfg(feature = "std")] - /// Construct new instance whose value is equal to `x` (between 0 and 1). - pub fn from_fraction(x: f64) -> Self { Self((x.max(0.0).min(1.0) * 1_000_000_000.0) as u32) } - - /// Approximate the fraction `p/q` into a per billion fraction - pub fn from_rational_approximation(p: N, q: N) -> Self - where N: traits::SimpleArithmetic + Clone - { - let p = p.min(q.clone()); - let factor = (q.clone() / 1_000_000_000u32.into()).max(1u32.into()); - - // Conversion can't overflow as p < q so ( p / (q/billion)) < billion - let p_reduce: u32 = (p / factor.clone()).try_into().unwrap_or_else(|_| panic!()); - let q_reduce: u32 = (q / factor.clone()).try_into().unwrap_or_else(|_| panic!()); - let part = p_reduce as u64 * 1_000_000_000u64 / q_reduce as u64; - - Perbill(part as u32) - } - - /// Return the product of multiplication of this value by itself. - pub fn square(self) -> Self { - let p: u64 = self.0 as u64 * self.0 as u64; - let q: u64 = 1_000_000_000 * 1_000_000_000; - Self::from_rational_approximation(p, q) - } - - /// Take out the raw parts-per-billions. - pub fn into_parts(self) -> u32 { - self.0 - } -} - -impl ops::Mul for Perbill -where - N: Clone + From + UniqueSaturatedInto + ops::Rem - + ops::Div + ops::Mul + ops::Add, -{ - type Output = N; - fn mul(self, b: N) -> Self::Output { - let billion: N = 1_000_000_000.into(); - let part: N = self.0.into(); - - let rem_multiplied_divided = { - let rem = b.clone().rem(billion.clone()); - - // `rem` is inferior to one billion, thus it fits into u32 - let rem_u32 = rem.saturated_into::(); - - // `self` and `rem` are inferior to one billion, thus the product is less than 10^18 - // and fits into u64 - let rem_multiplied_u64 = rem_u32 as u64 * self.0 as u64; - - // `rem_multiplied_u64` is less than 10^18 therefore divided by a billion it fits into - // u32 - let rem_multiplied_divided_u32 = (rem_multiplied_u64 / 1_000_000_000) as u32; - - // `rem_multiplied_divided` is inferior to b, thus it can be converted back to N type - rem_multiplied_divided_u32.into() - }; - - (b / billion) * part + rem_multiplied_divided - } -} - -#[cfg(feature = "std")] -impl From for Perbill { - fn from(x: f64) -> Perbill { - Perbill::from_fraction(x) - } -} - -#[cfg(feature = "std")] -impl From for Perbill { - fn from(x: f32) -> Perbill { - Perbill::from_fraction(x as f64) - } -} - -/// A fixed point number by the scale of 1 billion. -/// -/// cannot hold a value larger than +-`9223372036854775807 / 1_000_000_000` (~9 billion). -#[cfg_attr(feature = "std", derive(Debug))] -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Fixed64(i64); - -/// The maximum value of the `Fixed64` type -const DIV: i64 = 1_000_000_000; - -impl Fixed64 { - /// creates self from a natural number. - /// - /// Note that this might be lossy. - pub fn from_natural(int: i64) -> Self { - Self(int.saturating_mul(DIV)) - } - - /// Return the accuracy of the type. Given that this function returns the value `X`, it means - /// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`. - pub fn accuracy() -> i64 { - DIV - } - - /// creates self from a rational number. Equal to `n/d`. - /// - /// Note that this might be lossy. - pub fn from_rational(n: i64, d: u64) -> Self { - Self((n as i128 * DIV as i128 / (d as i128).max(1)).try_into().unwrap_or(Bounded::max_value())) - } - - /// Performs a saturated multiply and accumulate. - /// - /// Returns a saturated `n + (self * n)`. - /// TODO: generalize this to any weight type. #3189 - pub fn saturated_multiply_accumulate(&self, int: u32) -> u32 { - let parts = self.0; - let positive = parts > 0; - - // natural parts might overflow. - let natural_parts = self.clone().saturated_into::(); - // fractional parts can always fit into u32. - let perbill_parts = (parts.abs() % DIV) as u32; - - let n = int.saturating_mul(natural_parts); - let p = Perbill::from_parts(perbill_parts) * int; - // everything that needs to be either added or subtracted from the original weight. - let excess = n.saturating_add(p); - - if positive { - int.saturating_add(excess) - } else { - int.saturating_sub(excess) - } - } - - /// Raw constructor. Equal to `parts / 1_000_000_000`. - pub fn from_parts(parts: i64) -> Self { - Self(parts) - } -} - -impl UniqueSaturatedInto for Fixed64 { - /// Note that the maximum value of Fixed64 might be more than what can fit in u32. This is hence, - /// expected to be lossy. - fn unique_saturated_into(self) -> u32 { - (self.0.abs() / DIV).try_into().unwrap_or(Bounded::max_value()) - } -} - -impl Saturating for Fixed64 { - fn saturating_add(self, rhs: Self) -> Self { - Self(self.0.saturating_add(rhs.0)) - } - fn saturating_mul(self, rhs: Self) -> Self { - Self(self.0.saturating_mul(rhs.0) / DIV) - } - fn saturating_sub(self, rhs: Self) -> Self { - Self(self.0.saturating_sub(rhs.0)) - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe addition. -impl ops::Add for Fixed64 { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe subtraction. -impl ops::Sub for Fixed64 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } -} - -impl CheckedSub for Fixed64 { - fn checked_sub(&self, rhs: &Self) -> Option { - if let Some(v) = self.0.checked_sub(rhs.0) { - Some(Self(v)) - } else { - None - } - } -} - -impl CheckedAdd for Fixed64 { - fn checked_add(&self, rhs: &Self) -> Option { - if let Some(v) = self.0.checked_add(rhs.0) { - Some(Self(v)) - } else { - None - } - } -} - -/// PerU128 is parts-per-u128-max-value. It stores a value between 0 and 1 in fixed point. -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -#[derive(Encode, Decode, CompactAs, Default, Copy, Clone, PartialEq, Eq)] -pub struct PerU128(u128); - -const U128: u128 = u128::max_value(); - -impl PerU128 { - /// Nothing. - pub fn zero() -> Self { Self(0) } - - /// `true` if this is nothing. - pub fn is_zero(&self) -> bool { self.0 == 0 } - - /// Everything. - pub fn one() -> Self { Self(U128) } - - /// From an explicitly defined number of parts per maximum of the type. - pub fn from_parts(x: u128) -> Self { Self(x) } - - /// Construct new instance where `x` is denominator and the nominator is 1. - pub fn from_xth(x: u128) -> Self { Self(U128/x.max(1)) } -} - -impl ::rstd::ops::Deref for PerU128 { - type Target = u128; - - fn deref(&self) -> &u128 { - &self.0 - } -} - /// Signature verify that can work with any known signature types.. #[derive(Eq, PartialEq, Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] @@ -869,6 +523,37 @@ macro_rules! impl_outer_config { } } +/// Checks that `$x` is equal to `$y` with an error rate of `$error`. +/// +/// # Example +/// +/// ```rust +/// # fn main() { +/// sr_primitives::assert_eq_error_rate!(10, 10, 0); +/// sr_primitives::assert_eq_error_rate!(10, 11, 1); +/// sr_primitives::assert_eq_error_rate!(12, 10, 2); +/// # } +/// ``` +/// +/// ```rust,should_panic +/// # fn main() { +/// sr_primitives::assert_eq_error_rate!(12, 10, 1); +/// # } +/// ``` +#[macro_export] +#[cfg(feature = "std")] +macro_rules! assert_eq_error_rate { + ($x:expr, $y:expr, $error:expr $(,)?) => { + assert!( + ($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)), + "{:?} != {:?} (with error rate {:?})", + $x, + $y, + $error, + ); + }; +} + /// Simple blob to hold an extrinsic without committing to its format and ensure it is serialized /// correctly. #[derive(PartialEq, Eq, Clone, Default, Encode, Decode)] @@ -909,41 +594,8 @@ pub fn print(print: impl traits::Printable) { #[cfg(test)] mod tests { - use super::DispatchError; - use crate::codec::{Encode, Decode}; - use super::{Perbill, Permill}; - - macro_rules! per_thing_upper_test { - ($num_type:tt, $per:tt) => { - // multiplication from all sort of from_percent - assert_eq!($per::from_percent(100) * $num_type::max_value(), $num_type::max_value()); - assert_eq!( - $per::from_percent(99) * $num_type::max_value(), - ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type - ); - assert_eq!($per::from_percent(50) * $num_type::max_value(), $num_type::max_value() / 2); - assert_eq!($per::from_percent(1) * $num_type::max_value(), $num_type::max_value() / 100); - assert_eq!($per::from_percent(0) * $num_type::max_value(), 0); - - // multiplication with bounds - assert_eq!($per::one() * $num_type::max_value(), $num_type::max_value()); - assert_eq!($per::zero() * $num_type::max_value(), 0); - - // from_rational_approximation - assert_eq!( - $per::from_rational_approximation(u128::max_value() - 1, u128::max_value()), - $per::one(), - ); - assert_eq!( - $per::from_rational_approximation(u128::max_value()/3, u128::max_value()), - $per::from_parts($per::one().0/3), - ); - assert_eq!( - $per::from_rational_approximation(1, u128::max_value()), - $per::zero(), - ); - } - } + use crate::DispatchError; + use codec::{Encode, Decode}; #[test] fn opaque_extrinsic_serialization() { @@ -951,80 +603,6 @@ mod tests { assert_eq!(serde_json::to_string(&ex).unwrap(), "\"0x1001020304\"".to_owned()); } - #[test] - fn compact_permill_perbill_encoding() { - let tests = [(0u32, 1usize), (63, 1), (64, 2), (16383, 2), (16384, 4), (1073741823, 4), (1073741824, 5), (u32::max_value(), 5)]; - for &(n, l) in &tests { - let compact: crate::codec::Compact = Permill(n).into(); - let encoded = compact.encode(); - assert_eq!(encoded.len(), l); - let decoded = >::decode(&mut & encoded[..]).unwrap(); - let permill: Permill = decoded.into(); - assert_eq!(permill, Permill(n)); - - let compact: crate::codec::Compact = Perbill(n).into(); - let encoded = compact.encode(); - assert_eq!(encoded.len(), l); - let decoded = >::decode(&mut & encoded[..]).unwrap(); - let perbill: Perbill = decoded.into(); - assert_eq!(perbill, Perbill(n)); - } - } - - #[derive(Encode, Decode, PartialEq, Eq, Debug)] - struct WithCompact { - data: T, - } - - #[test] - fn test_has_compact_permill() { - let data = WithCompact { data: Permill(1) }; - let encoded = data.encode(); - assert_eq!(data, WithCompact::::decode(&mut &encoded[..]).unwrap()); - } - - #[test] - fn test_has_compact_perbill() { - let data = WithCompact { data: Perbill(1) }; - let encoded = data.encode(); - assert_eq!(data, WithCompact::::decode(&mut &encoded[..]).unwrap()); - } - - #[test] - fn per_things_should_work() { - use super::{Perbill, Permill}; - use primitive_types::U256; - - per_thing_upper_test!(u32, Perbill); - per_thing_upper_test!(u64, Perbill); - per_thing_upper_test!(u128, Perbill); - - per_thing_upper_test!(u32, Permill); - per_thing_upper_test!(u64, Permill); - per_thing_upper_test!(u128, Permill); - - } - - #[test] - fn per_things_operate_in_output_type() { - assert_eq!(Perbill::one() * 255_u64, 255); - } - - #[test] - fn per_things_one_minus_one_part() { - use primitive_types::U256; - - assert_eq!( - Perbill::from_parts(999_999_999) * std::u128::MAX, - ((Into::::into(std::u128::MAX) * 999_999_999u32) / 1_000_000_000u32).as_u128() - ); - - assert_eq!( - Permill::from_parts(999_999) * std::u128::MAX, - ((Into::::into(std::u128::MAX) * 999_999u32) / 1_000_000u32).as_u128() - ); - } - #[test] fn dispatch_error_encoding() { let error = DispatchError { @@ -1044,23 +622,4 @@ mod tests { }, ); } - - #[test] - fn per_bill_square() { - const FIXTURES: &[(u32, u32)] = &[ - (0, 0), - (1250000, 1562), // (0.00125, 0.000001562) - (255300000, 65178090), // (0.2553, 0.06517809) - (500000000, 250000000), // (0.5, 0.25) - (999995000, 999990000), // (0.999995, 0.999990000, but ideally 0.99999000002) - (1000000000, 1000000000), - ]; - - for &(x, r) in FIXTURES { - assert_eq!( - Perbill::from_parts(x).square(), - Perbill::from_parts(r), - ); - } - } } diff --git a/substrate/core/sr-primitives/src/sr_arithmetic.rs b/substrate/core/sr-primitives/src/sr_arithmetic.rs new file mode 100644 index 0000000000..20a7f51c1f --- /dev/null +++ b/substrate/core/sr-primitives/src/sr_arithmetic.rs @@ -0,0 +1,1373 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Minimal fixed point arithmetic primitives and types for runtime. + +#[cfg(feature = "std")] +use crate::serde::{Serialize, Deserialize}; + +use rstd::{ + ops, prelude::*, + convert::{TryInto, TryFrom}, + cmp::Ordering, +}; +use codec::{Encode, Decode}; +use crate::traits::{ + SaturatedConversion, CheckedSub, CheckedAdd, Bounded, UniqueSaturatedInto, Saturating, Zero, +}; + +macro_rules! implement_per_thing { + ($name:ident, $test_mod:ident, [$($test_units:tt),+], $max:tt, $type:ty, $upper_type:ty, $title:expr $(,)?) => { + /// A fixed point representation of a number between in the range [0, 1]. + /// + #[doc = $title] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))] + #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] + pub struct $name($type); + + impl $name { + /// Nothing. + pub fn zero() -> Self { Self(0) } + + /// `true` if this is nothing. + pub fn is_zero(&self) -> bool { self.0 == 0 } + + /// Everything. + pub fn one() -> Self { Self($max) } + + /// Consume self and deconstruct into a raw numeric type. + pub fn deconstruct(self) -> $type { self.0 } + + /// Return the scale at which this per-thing is working. + pub const fn accuracy() -> $type { $max } + + /// From an explicitly defined number of parts per maximum of the type. + /// + /// This can be called at compile time. + pub const fn from_parts(parts: $type) -> Self { + Self([parts, $max][(parts > $max) as usize]) + } + + /// Converts from a percent. Equal to `x / 100`. + /// + /// This can be created at compile time. + pub const fn from_percent(x: $type) -> Self { + Self([x, 100][(x > 100) as usize] * ($max / 100)) + } + + /// Return the product of multiplication of this value by itself. + pub fn square(self) -> Self { + // both can be safely casted and multiplied. + let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type; + let q: $upper_type = $max as $upper_type * $max as $upper_type; + Self::from_rational_approximation(p, q) + } + + /// Converts a fraction into `Permill`. + #[cfg(feature = "std")] + pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) } + + /// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow. + /// + /// The computation of this approximation is performed in the generic type `N`. Given + /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for + /// perbill), this can only work if `N == M` or `N: From + TryInto`. + pub fn from_rational_approximation(p: N, q: N) -> Self + where N: Clone + Ord + From<$type> + TryInto<$type> + ops::Div + { + // q cannot be zero. + let q = q.max((1 as $type).into()); + // p should not be bigger than q. + let p = p.min(q.clone()); + + let factor = (q.clone() / $max.into()).max((1 as $type).into()); + + // q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow. + // this implies that $type must be able to fit 2 * $max. + let q_reduce: $type = (q / factor.clone()) + .try_into() + .map_err(|_| "Failed to convert") + .expect( + "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ + does not satisfy this; qed" + ); + let p_reduce: $type = (p / factor.clone()) + .try_into() + .map_err(|_| "Failed to convert") + .expect( + "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ + does not satisfy this; qed" + ); + + // `p_reduced` and `q_reduced` are withing $type. Mul by another $max will always + // fit in $upper_type. This is guaranteed by the macro tests. + let part = + p_reduce as $upper_type + * ($max as $upper_type) + / q_reduce as $upper_type; + + $name(part as $type) + } + } + + impl Saturating for $name { + fn saturating_add(self, rhs: Self) -> Self { + // defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow. + Self::from_parts(self.0.saturating_add(rhs.0)) + } + fn saturating_sub(self, rhs: Self) -> Self { + Self::from_parts(self.0.saturating_sub(rhs.0)) + } + fn saturating_mul(self, rhs: Self) -> Self { + let a = self.0 as $upper_type; + let b = rhs.0 as $upper_type; + let m = $max as $upper_type; + let parts = a * b / m; + // This will always fit into $type. + Self::from_parts(parts as $type) + } + } + + impl ops::Div for $name { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + let p = self.0; + let q = rhs.0; + Self::from_rational_approximation(p, q) + } + } + + /// Overflow-prune multiplication. + /// + /// tailored to be used with a balance type. + impl ops::Mul for $name + where + N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem + + ops::Div + ops::Mul + ops::Add, + { + type Output = N; + fn mul(self, b: N) -> Self::Output { + let maximum: N = $max.into(); + let upper_max: $upper_type = $max.into(); + let part: N = self.0.into(); + + let rem_multiplied_divided = { + let rem = b.clone().rem(maximum.clone()); + + // `rem_sized` is inferior to $max, thus it fits into $type. This is assured by + // a test. + let rem_sized = rem.saturated_into::<$type>(); + + // `self` and `rem_sized` are inferior to $max, thus the product is less than + // $max^2 and fits into $upper_type. This is assured by a test. + let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type; + + // `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits + // in $type. remember that $type always fits $max. + let mut rem_multiplied_divided_sized = + (rem_multiplied_upper / upper_max) as $type; + // fix a tiny rounding error + if rem_multiplied_upper % upper_max > upper_max / 2 { + rem_multiplied_divided_sized += 1; + } + + // `rem_multiplied_divided_sized` is inferior to b, thus it can be converted + // back to N type + rem_multiplied_divided_sized.into() + }; + + (b / maximum) * part + rem_multiplied_divided + } + } + + impl codec::CompactAs for $name { + type As = $type; + fn encode_as(&self) -> &$type { + &self.0 + } + fn decode_from(x: $type) -> Self { + Self(x) + } + } + + impl From> for $name { + fn from(x: codec::Compact<$name>) -> Self { + x.0 + } + } + + #[cfg(test)] + mod $test_mod { + use codec::{Encode, Decode}; + use super::{$name, Saturating}; + use crate::{assert_eq_error_rate, traits::Zero}; + + + #[test] + fn macro_expanded_correctly() { + // needed for the `from_percent` to work. + assert!($max >= 100); + assert!($max % 100 == 0); + + // needed for `from_rational_approximation` + assert!(2 * $max < <$type>::max_value()); + assert!(($max as $upper_type) < <$upper_type>::max_value()); + + // for something like percent they can be the same. + assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value()); + assert!(($max as $upper_type).checked_mul($max.into()).is_some()); + } + + #[derive(Encode, Decode, PartialEq, Eq, Debug)] + struct WithCompact { + data: T, + } + + #[test] + fn has_compact() { + let data = WithCompact { data: $name(1) }; + let encoded = data.encode(); + assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap()); + } + + #[test] + fn compact_encoding() { + let tests = [ + // assume all per_things have the size u8 at least. + (0 as $type, 1usize), + (1 as $type, 1usize), + (63, 1), + (64, 2), + (65, 2), + (<$type>::max_value(), <$type>::max_value().encode().len() + 1) + ]; + for &(n, l) in &tests { + let compact: crate::codec::Compact<$name> = $name(n).into(); + let encoded = compact.encode(); + assert_eq!(encoded.len(), l); + let decoded = >::decode(&mut & encoded[..]) + .unwrap(); + let per_thingy: $name = decoded.into(); + assert_eq!(per_thingy, $name(n)); + } + } + + #[test] + fn per_thing_api_works() { + // some really basic stuff + assert_eq!($name::zero(), $name::from_parts(Zero::zero())); + assert_eq!($name::one(), $name::from_parts($max)); + assert_eq!($name::accuracy(), $max); + assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero())); + assert_eq!($name::from_percent(10), $name::from_parts($max / 10)); + assert_eq!($name::from_percent(100), $name::from_parts($max)); + } + + macro_rules! per_thing_mul_test { + ($num_type:tt) => { + // multiplication from all sort of from_percent + assert_eq!( + $name::from_percent(100) * $num_type::max_value(), + $num_type::max_value() + ); + assert_eq_error_rate!( + $name::from_percent(99) * $num_type::max_value(), + ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, + 1, + ); + assert_eq!( + $name::from_percent(50) * $num_type::max_value(), + $num_type::max_value() / 2, + ); + assert_eq_error_rate!( + $name::from_percent(1) * $num_type::max_value(), + $num_type::max_value() / 100, + 1, + ); + assert_eq!($name::from_percent(0) * $num_type::max_value(), 0); + + // // multiplication with bounds + assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value()); + assert_eq!($name::zero() * $num_type::max_value(), 0); + } + } + + #[test] + fn per_thing_mul_works() { + use primitive_types::U256; + + // accuracy test + assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10); + + $(per_thing_mul_test!($test_units);)* + } + + #[test] + fn per_thing_mul_rounds_to_nearest_number() { + assert_eq!($name::from_percent(33) * 10u64, 3); + assert_eq!($name::from_percent(34) * 10u64, 3); + assert_eq!($name::from_percent(35) * 10u64, 3); + assert_eq!($name::from_percent(36) * 10u64, 4); + assert_eq!($name::from_percent(36) * 10u64, 4); + } + + #[test] + fn per_thing_multiplication_with_large_number() { + use primitive_types::U256; + let max_minus_one = $max - 1; + assert_eq_error_rate!( + $name::from_parts(max_minus_one) * std::u128::MAX, + ((Into::::into(std::u128::MAX) * max_minus_one) / $max).as_u128(), + 1, + ); + } + + macro_rules! per_thing_from_rationale_approx_test { + ($num_type:tt) => { + // within accuracy boundary + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 0), + $name::one(), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 1), + $name::one(), + ); + assert_eq_error_rate!( + $name::from_rational_approximation(1 as $num_type, 3).0, + $name::from_parts($max / 3).0, + 2 + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 10), + $name::from_percent(10), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 4), + $name::from_percent(25), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 4), + $name::from_rational_approximation(2 as $num_type, 8), + ); + // no accurate anymore but won't overflow. + assert_eq!( + $name::from_rational_approximation( + $num_type::max_value() - 1, + $num_type::max_value() + ), + $name::one(), + ); + assert_eq_error_rate!( + $name::from_rational_approximation( + $num_type::max_value() / 3, + $num_type::max_value() + ).0, + $name::from_parts($max / 3).0, + 2 + ); + assert_eq!( + $name::from_rational_approximation(1, $num_type::max_value()), + $name::zero(), + ); + }; + } + + #[test] + fn per_thing_from_rationale_approx_works() { + // This is just to make sure something like Percent which _might_ get built from a + // u8 does not overflow in the context of this test. + let max_value = $max as $upper_type; + // almost at the edge + assert_eq!( + $name::from_rational_approximation($max - 1, $max + 1), + $name::from_parts($max - 2), + ); + assert_eq!( + $name::from_rational_approximation(1, $max-1), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(1, $max), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(2, 2 * $max - 1), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(1, $max+1), + $name::zero(), + ); + assert_eq!( + $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), + $name::from_percent(50), + ); + $(per_thing_from_rationale_approx_test!($test_units);)* + } + + #[test] + fn per_things_mul_operates_in_output_type() { + // assert_eq!($name::from_percent(50) * 100u32, 50u32); + assert_eq!($name::from_percent(50) * 100u64, 50u64); + assert_eq!($name::from_percent(50) * 100u128, 50u128); + } + + #[test] + fn per_thing_saturating_op_works() { + assert_eq!( + $name::from_percent(50).saturating_add($name::from_percent(40)), + $name::from_percent(90) + ); + assert_eq!( + $name::from_percent(50).saturating_add($name::from_percent(50)), + $name::from_percent(100) + ); + assert_eq!( + $name::from_percent(60).saturating_add($name::from_percent(50)), + $name::from_percent(100) + ); + + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(50)), + $name::from_percent(10) + ); + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(60)), + $name::from_percent(0) + ); + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(70)), + $name::from_percent(0) + ); + + assert_eq!( + $name::from_percent(50).saturating_mul($name::from_percent(50)), + $name::from_percent(25) + ); + assert_eq!( + $name::from_percent(20).saturating_mul($name::from_percent(20)), + $name::from_percent(4) + ); + assert_eq!( + $name::from_percent(10).saturating_mul($name::from_percent(10)), + $name::from_percent(1) + ); + } + + #[test] + fn per_thing_square_works() { + assert_eq!($name::from_percent(100).square(), $name::from_percent(100)); + assert_eq!($name::from_percent(50).square(), $name::from_percent(25)); + assert_eq!($name::from_percent(10).square(), $name::from_percent(1)); + assert_eq!( + $name::from_percent(2).square(), + $name::from_parts((4 * ($max as $upper_type) / 100 / 100) as $type) + ); + } + + #[test] + fn per_things_div_works() { + // normal + assert_eq!($name::from_percent(10) / $name::from_percent(20), + $name::from_percent(50) + ); + assert_eq!($name::from_percent(10) / $name::from_percent(10), + $name::from_percent(100) + ); + assert_eq!($name::from_percent(10) / $name::from_percent(0), + $name::from_percent(100) + ); + + // will not overflow + assert_eq!($name::from_percent(10) / $name::from_percent(5), + $name::from_percent(100) + ); + assert_eq!($name::from_percent(100) / $name::from_percent(50), + $name::from_percent(100) + ); + } + } + }; +} + +implement_per_thing!( + Percent, + test_per_cent, + [u32, u64, u128], + 100u8, + u8, + u16, + "_Percent_", +); +implement_per_thing!( + Permill, + test_permill, + [u32, u64, u128], + 1_000_000u32, + u32, + u64, + "_Parts per Million_", +); +implement_per_thing!( + Perbill, + test_perbill, + [u32, u64, u128], + 1_000_000_000u32, + u32, + u64, + "_Parts per Billion_", +); +implement_per_thing!( + Perquintill, + test_perquintill, + [u64, u128], + 1_000_000_000_000_000_000u64, + u64, + u128, + "_Parts per Quintillion_", +); + +/// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036] +/// with fixed point accuracy of one billion. +#[cfg_attr(feature = "std", derive(Debug))] +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Fixed64(i64); + +/// The accuracy of the `Fixed64` type. +const DIV: i64 = 1_000_000_000; + +impl Fixed64 { + /// creates self from a natural number. + /// + /// Note that this might be lossy. + pub fn from_natural(int: i64) -> Self { + Self(int.saturating_mul(DIV)) + } + + /// Return the accuracy of the type. Given that this function returns the value `X`, it means + /// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`. + pub fn accuracy() -> i64 { + DIV + } + + /// Raw constructor. Equal to `parts / 1_000_000_000`. + pub fn from_parts(parts: i64) -> Self { + Self(parts) + } + + /// creates self from a rational number. Equal to `n/d`. + /// + /// Note that this might be lossy. + pub fn from_rational(n: i64, d: u64) -> Self { + Self( + ((n as i128).saturating_mul(DIV as i128) / (d as i128).max(1)) + .try_into() + .unwrap_or(Bounded::max_value()) + ) + } + + /// Performs a saturated multiply and accumulate by unsigned number. + /// + /// Returns a saturated `int + (self * int)`. + pub fn saturated_multiply_accumulate(&self, int: N) -> N + where + N: TryFrom + From + UniqueSaturatedInto + Bounded + Clone + Saturating + + ops::Rem + ops::Div + ops::Mul + + ops::Add, + { + let div = DIV as u64; + let positive = self.0 > 0; + // safe to convert as absolute value. + let parts = self.0.checked_abs().map(|v| v as u64).unwrap_or(i64::max_value() as u64 + 1); + + + // will always fit. + let natural_parts = parts / div; + // might saturate. + let natural_parts: N = natural_parts.saturated_into(); + // fractional parts can always fit into u32. + let perbill_parts = (parts % div) as u32; + + let n = int.clone().saturating_mul(natural_parts); + let p = Perbill::from_parts(perbill_parts) * int.clone(); + + // everything that needs to be either added or subtracted from the original weight. + let excess = n.saturating_add(p); + + if positive { + int.saturating_add(excess) + } else { + int.saturating_sub(excess) + } + } +} + +impl Saturating for Fixed64 { + fn saturating_add(self, rhs: Self) -> Self { + Self(self.0.saturating_add(rhs.0)) + } + fn saturating_mul(self, rhs: Self) -> Self { + Self(self.0.saturating_mul(rhs.0) / DIV) + } + fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } +} + +/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait +/// for safe addition. +impl ops::Add for Fixed64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait +/// for safe subtraction. +impl ops::Sub for Fixed64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl CheckedSub for Fixed64 { + fn checked_sub(&self, rhs: &Self) -> Option { + if let Some(v) = self.0.checked_sub(rhs.0) { + Some(Self(v)) + } else { + None + } + } +} + +impl CheckedAdd for Fixed64 { + fn checked_add(&self, rhs: &Self) -> Option { + if let Some(v) = self.0.checked_add(rhs.0) { + Some(Self(v)) + } else { + None + } + } +} + +/// Some helper functions to work with 128bit numbers. Note that the functionality provided here is +/// only sensible to use with 128bit numbers because for smaller sizes, you can always rely on +/// assumptions of a bigger type (u128) being available, or simply create a per-thing and use the +/// multiplication implementation provided there. +pub mod helpers_128bit { + use super::Perquintill; + use crate::traits::Zero; + use rstd::cmp::{min, max}; + + const ERROR: &'static str = "can not accurately multiply by rational in this type"; + + /// Helper gcd function used in Rational128 implementation. + pub fn gcd(a: u128, b: u128) -> u128 { + match ((a, b), (a & 1, b & 1)) { + ((x, y), _) if x == y => y, + ((0, x), _) | ((x, 0), _) => x, + ((x, y), (0, 1)) | ((y, x), (1, 0)) => gcd(x >> 1, y), + ((x, y), (0, 0)) => gcd(x >> 1, y >> 1) << 1, + ((x, y), (1, 1)) => { + let (x, y) = (min(x, y), max(x, y)); + gcd((y - x) >> 1, x) + }, + _ => unreachable!(), + } + } + + /// Safely and accurately compute `a * b / c`. The approach is: + /// - Simply try `a * b / c`. + /// - Else, swap the operations (divide first) if `a > c` (division is possible) and `b <= c` + /// (overflow cannot happen) + /// - At any point, given an overflow or accuracy loss, return an Error. + /// + /// Invariant: c must be greater than or equal to 1. + /// This might not return Ok even if `b < c`. + pub fn multiply_by_rational(a: u128, b: u128, c: u128) -> Result { + if a.is_zero() || b.is_zero() { return Ok(Zero::zero()); } + + // invariant: C cannot be zero. + let c = c.max(1); + + // a and b are interchangeable by definition in this function. It always helps to assume the + // bigger of which is being multiplied by a `0 < b/c < 1`. Hence, a should be the bigger and + // b the smaller one. + let t = a; + let a = a.max(b); + let b = t.min(b); + + if let Some(x) = a.checked_mul(b) { + // This is the safest way to go. Try it. + Ok(x / c) + } else if a > c { + // if it can be safely swapped and it is a fraction, then swap. + let q = a / c; + let r = a % c; + let r_additional = multiply_by_rational(r, b, c)?; + + let q_part = q.checked_mul(b) + .ok_or(ERROR)?; + let result = q_part.checked_add(r_additional) + .ok_or(ERROR)?; + Ok(result) + } else { + Err(ERROR) + } + } + + /// Performs [`multiply_by_rational`]. In case of failure, if `b < c` it tries to approximate + /// the ratio into a perquintillion and return a lossy result. Otherwise, a best effort approach + /// of shifting both b and c is performed until multiply_by_rational can work. + /// + /// This function can very well be lossy and as the name suggests, perform a best effort in the + /// scope of u128 numbers. In case `b > c` and overflow happens, `a` is returned. + /// + /// c must be greater than or equal to 1. + pub fn multiply_by_rational_best_effort(a: u128, b: u128, c: u128) -> u128 { + if a.is_zero() || b.is_zero() { return Zero::zero(); } + let c = c.max(1); + + // unwrap the loop once. This favours performance over readability. + multiply_by_rational(a, b, c).unwrap_or_else(|_| { + if b <= c { + let per_thing = Perquintill::from_rational_approximation(b, c); + per_thing * a + } else { + let mut shift = 1; + let mut shifted_b = b.checked_shr(shift).unwrap_or(0); + let mut shifted_c = c.checked_shr(shift).unwrap_or(0); + + loop { + if shifted_b.is_zero() || shifted_c.is_zero() { + break a + } + match multiply_by_rational(a, shifted_b, shifted_c) { + Ok(r) => break r, + Err(_) => { + shift = shift + 1; + // by the time we reach here, b >= 1 && c >= 1. Before panic, they have + // to be zero which is prevented to happen by the break. + shifted_b = b.checked_shr(shift) + .expect( + "b >= 1 && c >= 1; break prevents them from ever being zero; \ + panic can only happen after either is zero; qed" + ); + shifted_c = c.checked_shr(shift) + .expect( + "b >= 1 && c >= 1; break prevents them from ever being zero; \ + panic can only happen after either is zero; qed" + ); + } + } + } + } + }) + } +} + +/// A wrapper for any rational number with a 128 bit numerator and denominator. +#[derive(Clone, Copy, Default, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct Rational128(u128, u128); + +impl Rational128 { + /// Nothing. + pub fn zero() -> Self { + Self(0, 1) + } + + /// If it is zero or not + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Build from a raw `n/d`. + pub fn from(n: u128, d: u128) -> Self { + Self(n, d.max(1)) + } + + /// Build from a raw `n/d`. This could lead to / 0 if not properly handled. + pub fn from_unchecked(n: u128, d: u128) -> Self { + Self(n, d) + } + + /// Return the numerator. + pub fn n(&self) -> u128 { + self.0 + } + + /// Return the denominator. + pub fn d(&self) -> u128 { + self.1 + } + + /// Convert `self` to a similar rational number where denominator is the given `den`. + // + /// This only returns if the result is accurate. `Err` is returned if the result cannot be + /// accurately calculated. + pub fn to_den(self, den: u128) -> Result { + if den == self.1 { + Ok(self) + } else { + if den > self.1 { + let n = helpers_128bit::multiply_by_rational(self.0, den, self.1)?; + Ok(Self(n, den)) + } else { + let div = self.1 / den; + Ok(Self(self.0 / div.max(1), den)) + } + } + } + + /// Get the least common divisor of `self` and `other`. + /// + /// This only returns if the result is accurate. `Err` is returned if the result cannot be + /// accurately calculated. + pub fn lcm(&self, other: &Self) -> Result { + // this should be tested better: two large numbers that are almost the same. + if self.1 == other.1 { return Ok(self.1) } + let g = helpers_128bit::gcd(self.1, other.1); + helpers_128bit::multiply_by_rational(self.1 , other.1, g) + } + + /// A saturating add that assumes `self` and `other` have the same denominator. + pub fn lazy_saturating_add(self, other: Self) -> Self { + if other.is_zero() { + self + } else { + Self(self.0.saturating_add(other.0) ,self.1) + } + } + + /// A saturating subtraction that assumes `self` and `other` have the same denominator. + pub fn lazy_saturating_sub(self, other: Self) -> Self { + if other.is_zero() { + self + } else { + Self(self.0.saturating_sub(other.0) ,self.1) + } + } + + /// Addition. Simply tries to unify the denominators and add the numerators. + /// + /// Overflow might happen during any of the steps. Error is returned in such cases. + pub fn checked_add(self, other: Self) -> Result { + let lcm = self.lcm(&other)?; + let self_scaled = self.to_den(lcm)?; + let other_scaled = other.to_den(lcm)?; + let n = self_scaled.0.checked_add(other_scaled.0) + .ok_or("overflow while adding numerators")?; + Ok(Self(n, self_scaled.1)) + } + + /// Subtraction. Simply tries to unify the denominators and subtract the numerators. + /// + /// Overflow might happen during any of the steps. None is returned in such cases. + pub fn checked_sub(self, other: Self) -> Result { + let lcm = self.lcm(&other)?; + let self_scaled = self.to_den(lcm)?; + let other_scaled = other.to_den(lcm)?; + + let n = self_scaled.0.checked_sub(other_scaled.0) + .ok_or("overflow while subtracting numerators")?; + Ok(Self(n, self_scaled.1)) + } +} + +impl PartialOrd for Rational128 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Note that this implementation can very well be lossy. TODO #3670 +impl Ord for Rational128 { + fn cmp(&self, other: &Self) -> Ordering { + // handle some edge cases. + if self.1 == other.1 { + self.0.cmp(&other.0) + } else if self.1.is_zero() { + Ordering::Greater + } else if other.1.is_zero() { + Ordering::Less + } else { + // general lossy case + let saturated_lcm = helpers_128bit::multiply_by_rational_best_effort( + self.1, + other.1, + helpers_128bit::gcd(self.1, other.1) + ); + let self_scaled = self.to_den(saturated_lcm) + .unwrap_or(Self(Bounded::max_value(), self.1)); + let other_scaled = other.to_den(saturated_lcm) + .unwrap_or(Self(Bounded::max_value(), other.1)); + self_scaled.n().cmp(&other_scaled.n()) + } + } +} + +/// Note that this implementation can very well be lossy. TODO #3670 +impl PartialEq for Rational128 { + fn eq(&self, other: &Self) -> bool { + // handle some edge cases. + if self.1 == other.1 { + self.0.eq(&other.0) + } else { + // general lossy case + let saturated_lcm = helpers_128bit::multiply_by_rational_best_effort( + self.1, + other.1, + helpers_128bit::gcd(self.1, other.1) + ); + let self_scaled = self.to_den(saturated_lcm) + .unwrap_or(Self(Bounded::max_value(), self.1)); + let other_scaled = other.to_den(saturated_lcm) + .unwrap_or(Self(Bounded::max_value(), other.1)); + self_scaled.n().eq(&other_scaled.n()) + } + } +} + + +#[cfg(test)] +mod test_rational128 { + use super::*; + use super::helpers_128bit::*; + use crate::assert_eq_error_rate; + use rand::Rng; + + const MAX128: u128 = u128::max_value(); + const MAX64: u128 = u64::max_value() as u128; + const MAX64_2: u128 = 2 * u64::max_value() as u128; + + fn r(p: u128, q: u128) -> Rational128 { + Rational128(p, q) + } + + fn mul_div(a: u128, b: u128, c: u128) -> u128 { + use primitive_types::U256; + if a.is_zero() { return Zero::zero(); } + let c = c.max(1); + + // e for extended + let ae: U256 = a.into(); + let be: U256 = b.into(); + let ce: U256 = c.into(); + + let r = ae * be / ce; + if r > u128::max_value().into() { + a + } else { + r.as_u128() + } + } + + #[test] + fn truth_value_function_works() { + assert_eq!( + mul_div(2u128.pow(100), 8, 4), + 2u128.pow(101) + ); + assert_eq!( + mul_div(2u128.pow(100), 4, 8), + 2u128.pow(99) + ); + + // and it returns a if result cannot fit + assert_eq!(mul_div(MAX128 - 10, 2, 1), MAX128 - 10); + } + + #[test] + fn to_denom_works() { + // simple up and down + assert_eq!(r(1, 5).to_den(10), Ok(r(2, 10))); + assert_eq!(r(4, 10).to_den(5), Ok(r(2, 5))); + + // up and down with large numbers + assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Ok(r(9, 10))); + assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Ok(r(5, 10))); + + // large to perbill. This is very well needed for phragmen. + assert_eq!( + r(MAX128 / 2, MAX128).to_den(1000_000_000), + Ok(r(500_000_000, 1000_000_000)) + ); + + // large to large + assert_eq!(r(MAX128 / 2, MAX128).to_den(MAX128/2), Ok(r(MAX128/4, MAX128/2))); + } + + #[test] + fn gdc_works() { + assert_eq!(gcd(10, 5), 5); + assert_eq!(gcd(7, 22), 1); + } + + #[test] + fn lcm_works() { + // simple stuff + assert_eq!(r(3, 10).lcm(&r(4, 15)).unwrap(), 30); + assert_eq!(r(5, 30).lcm(&r(1, 7)).unwrap(), 210); + assert_eq!(r(5, 30).lcm(&r(1, 10)).unwrap(), 30); + + // large numbers + assert_eq!( + r(1_000_000_000, MAX128).lcm(&r(7_000_000_000, MAX128-1)), + Err("can not accurately multiply by rational in this type"), + ); + assert_eq!( + r(1_000_000_000, MAX64).lcm(&r(7_000_000_000, MAX64-1)), + Ok(340282366920938463408034375210639556610), + ); + assert!(340282366920938463408034375210639556610 < MAX128); + assert!(340282366920938463408034375210639556610 == MAX64 * (MAX64 - 1)); + } + + #[test] + fn add_works() { + // works + assert_eq!(r(3, 10).checked_add(r(1, 10)).unwrap(), r(2, 5)); + assert_eq!(r(3, 10).checked_add(r(3, 7)).unwrap(), r(51, 70)); + + // errors + assert_eq!( + r(1, MAX128).checked_add(r(1, MAX128-1)), + Err("can not accurately multiply by rational in this type"), + ); + assert_eq!( + r(7, MAX128).checked_add(r(MAX128, MAX128)), + Err("overflow while adding numerators"), + ); + assert_eq!( + r(MAX128, MAX128).checked_add(r(MAX128, MAX128)), + Err("overflow while adding numerators"), + ); + } + + #[test] + fn sub_works() { + // works + assert_eq!(r(3, 10).checked_sub(r(1, 10)).unwrap(), r(1, 5)); + assert_eq!(r(6, 10).checked_sub(r(3, 7)).unwrap(), r(12, 70)); + + // errors + assert_eq!( + r(2, MAX128).checked_sub(r(1, MAX128-1)), + Err("can not accurately multiply by rational in this type"), + ); + assert_eq!( + r(7, MAX128).checked_sub(r(MAX128, MAX128)), + Err("overflow while subtracting numerators"), + ); + assert_eq!( + r(1, 10).checked_sub(r(2,10)), + Err("overflow while subtracting numerators"), + ); + } + + #[test] + fn ordering_and_eq_works() { + assert!(r(1, 2) > r(1, 3)); + assert!(r(1, 2) > r(2, 6)); + + assert!(r(1, 2) < r(6, 6)); + assert!(r(2, 1) > r(2, 6)); + + assert!(r(5, 10) == r(1, 2)); + assert!(r(1, 2) == r(1, 2)); + + assert!(r(1, 1490000000000200000) > r(1, 1490000000000200001)); + } + + #[test] + fn multiply_by_rational_works() { + assert_eq!(multiply_by_rational(7, 2, 3).unwrap(), 7 * 2 / 3); + assert_eq!(multiply_by_rational(7, 20, 30).unwrap(), 7 * 2 / 3); + assert_eq!(multiply_by_rational(20, 7, 30).unwrap(), 7 * 2 / 3); + + // computed with swap + assert_eq!( + // MAX128 % 3 == 0 + multiply_by_rational(MAX128, 2, 3).unwrap(), + MAX128 / 3 * 2, + ); + assert_eq!( + // MAX128 % 7 == 3 + multiply_by_rational(MAX128, 5, 7).unwrap(), + (MAX128 / 7 * 5) + (3 * 5 / 7), + ); + assert_eq!( + // MAX128 % 7 == 3 + multiply_by_rational(MAX128, 11 , 13).unwrap(), + (MAX128 / 13 * 11) + (8 * 11 / 13), + ); + assert_eq!( + // MAX128 % 1000 == 455 + multiply_by_rational(MAX128, 555, 1000).unwrap(), + (MAX128 / 1000 * 555) + (455 * 555 / 1000), + ); + + assert_eq!( + multiply_by_rational(2 * MAX64 - 1, MAX64, MAX64).unwrap(), + 2 * MAX64 - 1, + ); + assert_eq!( + multiply_by_rational(2 * MAX64 - 1, MAX64 - 1, MAX64).unwrap(), + 2 * MAX64 - 3, + ); + + + assert_eq!( + multiply_by_rational(MAX64 + 100, MAX64_2, MAX64_2 / 2).unwrap(), + (MAX64 + 100) * 2, + ); + assert_eq!( + multiply_by_rational(MAX64 + 100, MAX64_2 / 100, MAX64_2 / 200).unwrap(), + (MAX64 + 100) * 2, + ); + + // fails to compute. have to use the greedy, lossy version here + assert!(multiply_by_rational(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)).is_err()); + assert!(multiply_by_rational(1_000_000_000, MAX128 / 8, MAX128 / 2).is_err()); + } + + #[test] + fn multiply_by_rational_a_b_are_interchangeable() { + assert_eq!( + multiply_by_rational(10, MAX128, MAX128 / 2), + Ok(20), + ); + assert_eq!( + multiply_by_rational(MAX128, 10, MAX128 / 2), + Ok(20), + ); + } + + #[test] + fn multiply_by_rational_best_effort_works() { + assert_eq_error_rate!( + multiply_by_rational_best_effort(MAX64 + 100, MAX64_2, MAX64_2 / 2), + (MAX64 + 100) * 2, + 10, + ); + assert_eq_error_rate!( + multiply_by_rational_best_effort(MAX64 + 100, MAX64_2 * 100, MAX64_2 * 100 / 2), + (MAX64 + 100) * 2, + 10, + ); + assert_eq_error_rate!( + multiply_by_rational_best_effort(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)), + mul_div(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)), + 10, + ); + assert_eq_error_rate!( + multiply_by_rational_best_effort(1_000_000_000, MAX128 / 8, MAX128 / 2), + 1_000_000_000 / 4, + 10, + ); + + assert_eq!( + multiply_by_rational_best_effort(MAX128, MAX128 - 1, MAX128), + MAX128, + ); + + assert_eq!( + multiply_by_rational_best_effort(MAX64, MAX128 / 2, MAX128), + MAX64 / 2, + ); + } + + fn do_fuzz_multiply_by_rational( + iters: usize, + bits: u32, + maximum_error: u128, + do_print: bool, + bounded: bool, + mul_fn: F, + ) where F: Fn(u128, u128, u128) -> u128 { + let mut rng = rand::thread_rng(); + let mut average_diff = 0.0; + let upper_bound = 2u128.pow(bits); + + for _ in 0..iters { + let a = rng.gen_range(0u128, upper_bound); + let c = rng.gen_range(0u128, upper_bound); + let b = rng.gen_range( + 0u128, + if bounded { c } else { upper_bound } + ); + + let a: u128 = a.into(); + let b: u128 = b.into(); + let c: u128 = c.into(); + + let truth = mul_div(a, b, c); + let result = mul_fn(a, b, c); + let diff = truth.max(result) - truth.min(result); + let loss_ratio = diff as f64 / truth as f64; + average_diff += loss_ratio; + + if do_print && diff > maximum_error { + println!("++ Computed with more loss than expected: {} * {} / {}", a, b, c); + println!("++ Expected {}", truth); + println!("+++++++ Got {}", result); + } + } + + // print report + println!( + "## Fuzzed with {} numbers. Average error was {}", + iters, + average_diff / (iters as f64), + ); + } + + #[test] + fn fuzz_multiply_by_rational_best_effort_32() { + let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_best_effort_64() { + let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_best_effort_96() { + let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); + // println!("\nInvariant: b < c"); + // do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, true, f); + println!("every possibility"); + // do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, false, f); + do_fuzz_multiply_by_rational(10, 96, 0, true, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_best_effort_128() { + let f = |a, b, c| multiply_by_rational_best_effort(a, b, c); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_32() { + // if Err just return the truth value. We don't care about such cases. The point of this + // fuzzing is to make sure: if `multiply_by_rational` returns `Ok`, it must be 100% accurate + // returning `Err` is fine. + let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 32, 0, false, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_64() { + let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 64, 0, false, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_96() { + let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 96, 0, false, false, f); + } + + #[test] + fn fuzz_multiply_by_rational_128() { + let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); + println!("\nInvariant: b < c"); + do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, true, f); + println!("every possibility"); + do_fuzz_multiply_by_rational(1_000_000, 127, 0, false, false, f); + } +} + + +#[cfg(test)] +mod tests_fixed64 { + use super::*; + + fn max() -> Fixed64 { + Fixed64::from_parts(i64::max_value()) + } + + #[test] + fn fixed64_semantics() { + assert_eq!(Fixed64::from_rational(5, 2).0, 5 * 1_000_000_000 / 2); + assert_eq!(Fixed64::from_rational(5, 2), Fixed64::from_rational(10, 4)); + assert_eq!(Fixed64::from_rational(5, 0), Fixed64::from_rational(5, 1)); + + // biggest value that can be created. + assert_ne!(max(), Fixed64::from_natural(9_223_372_036)); + assert_eq!(max(), Fixed64::from_natural(9_223_372_037)); + } + + macro_rules! saturating_mul_acc_test { + ($num_type:tt) => { + assert_eq!( + Fixed64::from_rational(100, 1).saturated_multiply_accumulate(10 as $num_type), + 1010, + ); + assert_eq!( + Fixed64::from_rational(100, 2).saturated_multiply_accumulate(10 as $num_type), + 510, + ); + assert_eq!( + Fixed64::from_rational(100, 3).saturated_multiply_accumulate(0 as $num_type), + 0, + ); + assert_eq!( + Fixed64::from_rational(5, 1).saturated_multiply_accumulate($num_type::max_value()), + $num_type::max_value() + ); + assert_eq!( + max().saturated_multiply_accumulate($num_type::max_value()), + $num_type::max_value() + ); + } + } + + #[test] + fn fixed64_multiply_accumulate_works() { + saturating_mul_acc_test!(u32); + saturating_mul_acc_test!(u64); + saturating_mul_acc_test!(u128); + } +} diff --git a/substrate/core/sr-primitives/src/weights.rs b/substrate/core/sr-primitives/src/weights.rs index 45ac59e0d5..28f1b2fe0c 100644 --- a/substrate/core/sr-primitives/src/weights.rs +++ b/substrate/core/sr-primitives/src/weights.rs @@ -188,7 +188,7 @@ impl WeightMultiplier { /// build self from raw parts per billion. #[cfg(feature = "std")] pub fn from_parts(parts: i64) -> Self { - Self(Fixed64(parts)) + Self(Fixed64::from_parts(parts)) } /// build self from a fixed64 value. diff --git a/substrate/node/executor/src/lib.rs b/substrate/node/executor/src/lib.rs index 6c4fdc5897..864b51d6c7 100644 --- a/substrate/node/executor/src/lib.rs +++ b/substrate/node/executor/src/lib.rs @@ -40,7 +40,7 @@ mod tests { use runtime_io; use codec::{Encode, Decode, Joiner}; use runtime_support::{ - Hashable, StorageValue, StorageMap, assert_eq_error_rate, traits::Currency, + Hashable, StorageValue, StorageMap, traits::Currency, }; use state_machine::TestExternalities as CoreTestExternalities; use primitives::{ @@ -48,6 +48,7 @@ mod tests { traits::{CodeExecutor, Externalities}, storage::well_known_keys, }; use sr_primitives::{ + assert_eq_error_rate, traits::{Header as HeaderT, Hash as HashT, Convert}, ApplyResult, transaction_validity::InvalidTransaction, weights::{WeightMultiplier, GetDispatchInfo}, }; diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 306521b2cf..cc3a886e97 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -84,7 +84,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 163, + spec_version: 164, impl_version: 164, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/srml/im-online/src/lib.rs b/substrate/srml/im-online/src/lib.rs index b09b24df5f..611a2aaa16 100644 --- a/substrate/srml/im-online/src/lib.rs +++ b/substrate/srml/im-online/src/lib.rs @@ -76,7 +76,7 @@ use primitives::offchain::{OpaqueNetworkState, StorageKind}; use rstd::prelude::*; use session::historical::IdentificationTuple; use sr_primitives::{ - traits::{Convert, Member, Printable}, Perbill, + traits::{Convert, Member, Printable, Saturating}, Perbill, transaction_validity::{ TransactionValidity, TransactionLongevity, ValidTransaction, InvalidTransaction, }, @@ -573,12 +573,6 @@ impl Offence for UnresponsivenessOffence { fn slash_fraction(offenders: u32, validator_set_count: u32) -> Perbill { // the formula is min((3 * (k - 1)) / n, 1) * 0.05 let x = Perbill::from_rational_approximation(3 * (offenders - 1), validator_set_count); - - // _ * 0.05 - // For now, Perbill doesn't support multiplication other than an integer so we perform - // a manual scaling. - // TODO: #3189 should fix this. - let p = (x.into_parts() as u64 * 50_000_000u64) / 1_000_000_000u64; - Perbill::from_parts(p as u32) + x.saturating_mul(Perbill::from_percent(5)) } } diff --git a/substrate/srml/offences/src/lib.rs b/substrate/srml/offences/src/lib.rs index 5c1d04d0c0..801b8b5fca 100644 --- a/substrate/srml/offences/src/lib.rs +++ b/substrate/srml/offences/src/lib.rs @@ -33,7 +33,7 @@ use support::{ }; use sr_primitives::{ Perbill, - traits::Hash, + traits::{Hash, Saturating}, }; use sr_staking_primitives::{ offence::{Offence, ReportOffence, Kind, OnOffenceHandler, OffenceDetails}, @@ -131,12 +131,9 @@ where offenders_count.saturating_sub(previous_offenders_count), validator_set_count, ); - let numerator = new_fraction - .into_parts() - .saturating_sub(previous_fraction.into_parts()); - let denominator = - Perbill::from_parts(Perbill::one().into_parts() - previous_fraction.into_parts()); - Perbill::from_parts(denominator * numerator) + let numerator = new_fraction.saturating_sub(previous_fraction); + let denominator = Perbill::one().saturating_sub(previous_fraction); + denominator.saturating_mul(numerator) } else { new_fraction.clone() }; diff --git a/substrate/srml/staking/reward-curve/src/lib.rs b/substrate/srml/staking/reward-curve/src/lib.rs index 637ba10b9e..7e1f1e6aa9 100644 --- a/substrate/srml/staking/reward-curve/src/lib.rs +++ b/substrate/srml/staking/reward-curve/src/lib.rs @@ -337,8 +337,8 @@ fn generate_piecewise_linear(points: Vec<(u32, u32)>) -> TokenStream2 { points_tokens.extend(quote!( ( - _sr_primitives::Perbill::from_const_parts(#x_perbill), - _sr_primitives::Perbill::from_const_parts(#y_perbill), + _sr_primitives::Perbill::from_parts(#x_perbill), + _sr_primitives::Perbill::from_parts(#y_perbill), ), )); } diff --git a/substrate/srml/staking/src/inflation.rs b/substrate/srml/staking/src/inflation.rs index a1d45b3db1..89326b92c0 100644 --- a/substrate/srml/staking/src/inflation.rs +++ b/substrate/srml/staking/src/inflation.rs @@ -59,9 +59,10 @@ mod test { #[test] fn npos_curve_is_sensible() { const YEAR: u64 = 365 * 24 * 60 * 60 * 1000; + //super::I_NPOS.calculate_for_fraction_times_denominator(25, 100) assert_eq!(super::compute_total_payout(&I_NPOS, 0, 100_000u64, YEAR), 2_498); - assert_eq!(super::compute_total_payout(&I_NPOS, 5_000, 100_000u64, YEAR), 3_247); - assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, YEAR), 6_245); + assert_eq!(super::compute_total_payout(&I_NPOS, 5_000, 100_000u64, YEAR), 3_248); + assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, YEAR), 6_246); assert_eq!(super::compute_total_payout(&I_NPOS, 40_000, 100_000u64, YEAR), 8_494); assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, YEAR), 9_993); assert_eq!(super::compute_total_payout(&I_NPOS, 60_000, 100_000u64, YEAR), 4_379); @@ -76,8 +77,8 @@ mod test { const SIX_HOURS: u64 = 6 * 60 * 60 * 1000; assert_eq!(super::compute_total_payout(&I_NPOS, 25_000, 100_000u64, SIX_HOURS), 4); - assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, SIX_HOURS), 6); - assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, SIX_HOURS), 1); + assert_eq!(super::compute_total_payout(&I_NPOS, 50_000, 100_000u64, SIX_HOURS), 7); + assert_eq!(super::compute_total_payout(&I_NPOS, 75_000, 100_000u64, SIX_HOURS), 2); const HOUR: u64 = 60 * 60 * 1000; assert_eq!( diff --git a/substrate/srml/staking/src/lib.rs b/substrate/srml/staking/src/lib.rs index bb7368affc..4c73b64bd4 100644 --- a/substrate/srml/staking/src/lib.rs +++ b/substrate/srml/staking/src/lib.rs @@ -270,7 +270,6 @@ use sr_primitives::{ SaturatedConversion, } }; -use phragmen::{elect, equalize, Support, SupportMap, ExtendedBalance, ACCURACY}; use sr_staking_primitives::{ SessionIndex, offence::{OnOffenceHandler, OffenceDetails, Offence, ReportOffence}, @@ -279,6 +278,8 @@ use sr_staking_primitives::{ use sr_primitives::{Serialize, Deserialize}; use system::{ensure_signed, ensure_root}; +use phragmen::{elect, equalize, ExtendedBalance, Support, SupportMap, PhragmenStakedAssignment}; + const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const MAX_NOMINATIONS: usize = 16; const MAX_UNLOCKING_CHUNKS: usize = 32; @@ -1190,7 +1191,7 @@ impl Module { for (v, p) in validators.iter().zip(points.individual.into_iter()) { if p != 0 { - let reward = multiply_by_rational(total_payout, p, points.total); + let reward = Perbill::from_rational_approximation(p, points.total) * total_payout; total_imbalance.subsume(Self::reward_validator(v, reward)); } } @@ -1252,21 +1253,15 @@ impl Module { ); if let Some(phragmen_result) = maybe_phragmen_result { - let elected_stashes = phragmen_result.winners.iter().map(|(s, _)| s.clone()).collect::>(); - let mut assignments = phragmen_result.assignments; + let elected_stashes = phragmen_result.winners.iter() + .map(|(s, _)| s.clone()) + .collect::>(); + let assignments = phragmen_result.assignments; - // helper closure. - let to_balance = |b: ExtendedBalance| - >>::convert(b); let to_votes = |b: BalanceOf| , u64>>::convert(b) as ExtendedBalance; - - // The return value of this is safe to be converted to u64. - // The original balance, `b` is within the scope of u64. It is just extended to u128 - // to be properly multiplied by a ratio, which will lead to another value - // less than u64 for sure. The result can then be safely passed to `to_balance`. - // For now the backward convert is used. A simple `TryFrom` is also safe. - let ratio_of = |b, r: ExtendedBalance| r.saturating_mul(to_votes(b)) / ACCURACY; + let to_balance = |e: ExtendedBalance| + >>::convert(e); // Initialize the support of each candidate. let mut supports = >::new(); @@ -1278,29 +1273,42 @@ impl Module { supports.insert(e.clone(), item); }); - // convert the ratio in-place (and replace) to the balance but still in the extended - // balance type. - for (n, assignment) in assignments.iter_mut() { - for (c, r) in assignment.iter_mut() { - let nominator_stake = Self::slashable_balance_of(n); - let other_stake = ratio_of(nominator_stake, *r); + // build support struct. + for (n, assignment) in assignments.iter() { + for (c, per_thing) in assignment.iter() { + let nominator_stake = to_votes(Self::slashable_balance_of(n)); + // AUDIT: it is crucially important for the `Mul` implementation of all + // per-things to be sound. + let other_stake = *per_thing * nominator_stake; if let Some(support) = supports.get_mut(c) { - // This for an astronomically rich validator with more astronomically rich + // For an astronomically rich validator with more astronomically rich // set of nominators, this might saturate. support.total = support.total.saturating_add(other_stake); support.others.push((n.clone(), other_stake)); } - // convert the ratio to extended balance - *r = other_stake; } } - #[cfg(feature = "equalize")] - { + if cfg!(feature = "equalize") { + let mut staked_assignments + : Vec<(T::AccountId, Vec>)> + = Vec::with_capacity(assignments.len()); + for (n, assignment) in assignments.iter() { + let mut staked_assignment + : Vec> + = Vec::with_capacity(assignment.len()); + for (c, per_thing) in assignment.iter() { + let nominator_stake = to_votes(Self::slashable_balance_of(n)); + let other_stake = *per_thing * nominator_stake; + staked_assignment.push((c.clone(), other_stake)); + } + staked_assignments.push((n.clone(), staked_assignment)); + } + let tolerance = 0_u128; let iterations = 2_usize; - equalize::<_, _, _, T::CurrencyToVote>( - assignments, + equalize::<_, _, T::CurrencyToVote, _>( + staked_assignments, &mut supports, tolerance, iterations, @@ -1450,31 +1458,6 @@ impl authorship::EventHandler(value: N, num: u32, den: u32) -> N - where N: SimpleArithmetic + Clone -{ - let num = num.min(den); - - let result_divisor_part = value.clone() / den.into() * num.into(); - - let result_remainder_part = { - let rem = value % den.into(); - - // Fits into u32 because den is u32 and remainder < den - let rem_u32 = rem.saturated_into::(); - - // Multiplication fits into u64 as both term are u32 - let rem_part = rem_u32 as u64 * num as u64 / den as u64; - - // Result fits into u32 as num < total_points - (rem_part as u32).into() - }; - - result_divisor_part + result_remainder_part -} - /// A `Convert` implementation that finds the stash of the given controller account, /// if any. pub struct StashOf(rstd::marker::PhantomData); diff --git a/substrate/srml/staking/src/mock.rs b/substrate/srml/staking/src/mock.rs index ba1645e6d7..8263e159c1 100644 --- a/substrate/srml/staking/src/mock.rs +++ b/substrate/srml/staking/src/mock.rs @@ -151,7 +151,7 @@ parameter_types! { pub const Period: BlockNumber = 1; pub const Offset: BlockNumber = 0; pub const UncleGenerations: u64 = 0; - pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(33); + pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(25); } impl session::Trait for Test { type OnSessionEnding = session::historical::NoteHistoricalRoot; @@ -396,7 +396,7 @@ pub fn check_nominator_exposure(stash: u64) { pub fn assert_total_expo(stash: u64, val: u64) { let expo = Staking::stakers(&stash); - assert_eq!(expo.total, val); + assert_eq!(expo.total, val, "total exposure mismatch {:?} != {:?}", expo.total, val); } pub fn assert_is_stash(acc: u64) { diff --git a/substrate/srml/staking/src/tests.rs b/substrate/srml/staking/src/tests.rs index 1d0beb6029..6b51fb9b3f 100644 --- a/substrate/srml/staking/src/tests.rs +++ b/substrate/srml/staking/src/tests.rs @@ -17,11 +17,11 @@ //! Tests for the module. use super::*; +use mock::*; use runtime_io::with_externalities; -use sr_primitives::traits::OnInitialize; +use sr_primitives::{assert_eq_error_rate, traits::OnInitialize}; use sr_staking_primitives::offence::{OffenceDetails, OnOffenceHandler}; use support::{assert_ok, assert_noop, assert_eq_uvec}; -use mock::*; use support::traits::{Currency, ReservableCurrency}; #[test] @@ -30,14 +30,23 @@ fn basic_setup_works() { with_externalities(&mut ExtBuilder::default() .build(), || { - assert_eq!(Staking::bonded(&11), Some(10)); // Account 11 is stashed and locked, and account 10 is the controller - assert_eq!(Staking::bonded(&21), Some(20)); // Account 21 is stashed and locked, and account 20 is the controller - assert_eq!(Staking::bonded(&1), None); // Account 1 is not a stashed + // Account 11 is stashed and locked, and account 10 is the controller + assert_eq!(Staking::bonded(&11), Some(10)); + // Account 21 is stashed and locked, and account 20 is the controller + assert_eq!(Staking::bonded(&21), Some(20)); + // Account 1 is not a stashed + assert_eq!(Staking::bonded(&1), None); // Account 10 controls the stash from account 11, which is 100 * balance_factor units - assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] })); + assert_eq!( + Staking::ledger(&10), + Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }) + ); // Account 20 controls the stash from account 21, which is 200 * balance_factor units - assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] })); + assert_eq!( + Staking::ledger(&20), + Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] }) + ); // Account 1 does not control any stash assert_eq!(Staking::ledger(&1), None); @@ -48,8 +57,10 @@ fn basic_setup_works() { (11, ValidatorPrefs::default()) ]); - // Account 100 is the default nominator - assert_eq!(Staking::ledger(100), Some(StakingLedger { stash: 101, total: 500, active: 500, unlocking: vec![] })); + assert_eq!( + Staking::ledger(100), + Some(StakingLedger { stash: 101, total: 500, active: 500, unlocking: vec![] }) + ); assert_eq!(Staking::nominators(101), vec![11, 21]); if cfg!(feature = "equalize") { @@ -194,9 +205,9 @@ fn rewards_should_work() { assert_eq!(Staking::current_era(), 1); assert_eq!(Session::current_index(), 3); - // 11 validator has 2 / 3 of the total rewards and half half for it and its nominator - assert_eq!(Balances::total_balance(&2), init_balance_2 + total_payout / 3); - assert_eq!(Balances::total_balance(&10), init_balance_10 + total_payout / 3); + // 11 validator has 2/3 of the total rewards and half half for it and its nominator + assert_eq_error_rate!(Balances::total_balance(&2), init_balance_2 + total_payout / 3, 1); + assert_eq_error_rate!(Balances::total_balance(&10), init_balance_10 + total_payout / 3, 1); assert_eq!(Balances::total_balance(&11), init_balance_11); }); } @@ -338,8 +349,6 @@ fn less_than_needed_candidates_works() { #[test] fn no_candidate_emergency_condition() { - // Test the situation where the number of validators are less than `ValidatorCount` and less than - // The expected behavior is to choose all candidates from the previous era. with_externalities(&mut ExtBuilder::default() .minimum_validator_count(10) .validator_count(15) @@ -373,7 +382,6 @@ fn no_candidate_emergency_condition() { fn nominating_and_rewards_should_work() { // PHRAGMEN OUTPUT: running this test with the reference impl gives: // - // Votes [('10', 1000, ['10']), ('20', 1000, ['20']), ('30', 1000, ['30']), ('40', 1000, ['40']), ('2', 1000, ['10', '20', '30']), ('4', 1000, ['10', '20', '40'])] // Sequential Phragmén gives // 10 is elected with stake 2200.0 and score 0.0003333333333333333 // 20 is elected with stake 1800.0 and score 0.0005555555555555556 @@ -457,11 +465,11 @@ fn nominating_and_rewards_should_work() { if cfg!(feature = "equalize") { // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(11).own, 1000); - assert_eq!(Staking::stakers(11).total, 1000 + 999); + assert_eq_error_rate!(Staking::stakers(11).total, 1000 + 1000, 2); // 2 and 4 supported 10, each with stake 600, according to phragmen. assert_eq!( Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), - vec![599, 400] + vec![600, 400] ); assert_eq!( Staking::stakers(11).others.iter().map(|e| e.who).collect::>(), @@ -469,11 +477,11 @@ fn nominating_and_rewards_should_work() { ); // total expo of 20, with 500 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(21).own, 1000); - assert_eq!(Staking::stakers(21).total, 1000 + 999); + assert_eq_error_rate!(Staking::stakers(21).total, 1000 + 1000, 2); // 2 and 4 supported 20, each with stake 250, according to phragmen. assert_eq!( Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), - vec![400, 599] + vec![400, 600] ); assert_eq!( Staking::stakers(21).others.iter().map(|e| e.who).collect::>(), @@ -494,11 +502,11 @@ fn nominating_and_rewards_should_work() { ); // total expo of 20, with 500 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(21).own, 1000); - assert_eq!(Staking::stakers(21).total, 1000 + 1198); + assert_eq_error_rate!(Staking::stakers(21).total, 1000 + 1200, 2); // 2 and 4 supported 20, each with stake 250, according to phragmen. assert_eq!( Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), - vec![599, 599] + vec![600, 600] ); assert_eq!( Staking::stakers(21).others.iter().map(|e| e.who).collect::>(), @@ -527,30 +535,56 @@ fn nominating_and_rewards_should_work() { let payout_for_20 = 2 * total_payout_1 / 3; if cfg!(feature = "equalize") { // Nominator 2: has [400 / 2000 ~ 1 / 5 from 10] + [600 / 2000 ~ 3 / 10 from 20]'s reward. - assert_eq!(Balances::total_balance(&2), initial_balance + payout_for_10 / 5 + payout_for_20 * 3 / 10); + assert_eq_error_rate!( + Balances::total_balance(&2), + initial_balance + payout_for_10 / 5 + payout_for_20 * 3 / 10, + 2, + ); // Nominator 4: has [400 / 2000 ~ 1 / 5 from 20] + [600 / 2000 ~ 3 / 10 from 10]'s reward. - assert_eq!(Balances::total_balance(&4), initial_balance + payout_for_20 / 5 + payout_for_10 * 3 / 10); + assert_eq_error_rate!( + Balances::total_balance(&4), + initial_balance + payout_for_20 / 5 + payout_for_10 * 3 / 10, + 2, + ); // Validator 10: got 1000 / 2000 external stake. - assert_eq!(Balances::total_balance(&10), initial_balance + payout_for_10 / 2); - // Validator 20: got 1000 / 2000 external stake. - assert_eq!(Balances::total_balance(&20), initial_balance + payout_for_20 / 2); - } else { - // Nominator 2: has [400 / 1800 ~ 2 / 9 from 10] + [600 / 2200 ~ 3 / 11 from 20]'s reward. ==> 2 / 9 + 3 / 11 - assert_eq!( - Balances::total_balance(&2), - initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11) - 2 + assert_eq_error_rate!( + Balances::total_balance(&10), + initial_balance + payout_for_10 / 2, + 1, ); - // Nominator 4: has [400 / 1800 ~ 2 / 9 from 10] + [600 / 2200 ~ 3 / 11 from 20]'s reward. ==> 2 / 9 + 3 / 11 - assert_eq!( + // Validator 20: got 1000 / 2000 external stake. + assert_eq_error_rate!( + Balances::total_balance(&20), + initial_balance + payout_for_20 / 2, + 1, + ); + } else { + // Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 + assert_eq_error_rate!( + Balances::total_balance(&2), + initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), + 1, + ); + // Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11 + assert_eq_error_rate!( Balances::total_balance(&4), - initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11) - 2 + initial_balance + (2 * payout_for_10 / 9 + 3 * payout_for_20 / 11), + 1, ); - // Validator 10: got 800 / 1800 external stake => 8 / 18 =? 4 / 9 => Validator's share = 5 / 9 - assert_eq!(Balances::total_balance(&10), initial_balance + 5*payout_for_10 / 9 - 1); - // Validator 20: got 1200 / 2200 external stake => 12 / 22 =? 6 / 11 => Validator's share = 5 / 11 - assert_eq!(Balances::total_balance(&20), initial_balance + 5*payout_for_20 / 11 + 1); + // Validator 10: got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9 + assert_eq_error_rate!( + Balances::total_balance(&10), + initial_balance + 5 * payout_for_10 / 9, + 1, + ); + // Validator 20: got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11 + assert_eq_error_rate!( + Balances::total_balance(&20), + initial_balance + 5 * payout_for_20 / 11, + 1, + ); } check_exposure_all(); @@ -655,9 +689,15 @@ fn double_controlling_should_fail() { || { let arbitrary_value = 5; // 2 = controller, 1 stashed => ok - assert_ok!(Staking::bond(Origin::signed(1), 2, arbitrary_value, RewardDestination::default())); + assert_ok!( + Staking::bond(Origin::signed(1), 2, arbitrary_value, + RewardDestination::default()) + ); // 2 = controller, 3 stashed (Note that 2 is reused.) => no-op - assert_noop!(Staking::bond(Origin::signed(3), 2, arbitrary_value, RewardDestination::default()), "controller already paired"); + assert_noop!( + Staking::bond(Origin::signed(3), 2, arbitrary_value, RewardDestination::default()), + "controller already paired" + ); }); } @@ -1278,137 +1318,6 @@ fn on_free_balance_zero_stash_removes_nominator() { }); } -#[test] -fn phragmen_poc_works() { - // Tests the POC test of the phragmen, mentioned in the paper and reference implementation. - // Initial votes: - // Votes [ - // ('2', 500, ['10', '20', '30']), - // ('4', 500, ['10', '20', '40']), - // ('10', 1000, ['10']), - // ('20', 1000, ['20']), - // ('30', 1000, ['30']), - // ('40', 1000, ['40'])] - // - // Sequential Phragmén gives - // 10 is elected with stake 1666.6666666666665 and score 0.0005 - // 20 is elected with stake 1333.3333333333333 and score 0.00075 - - // 2 has load 0.00075 and supported - // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 30 with stake 0.0 - // 4 has load 0.00075 and supported - // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0 - // 10 has load 0.0005 and supported - // 10 with stake 1000.0 - // 20 has load 0.00075 and supported - // 20 with stake 1000.0 - // 30 has load 0 and supported - // 30 with stake 0 - // 40 has load 0 and supported - // 40 with stake 0 - - // Sequential Phragmén with post processing gives - // 10 is elected with stake 1500.0 and score 0.0005 - // 20 is elected with stake 1500.0 and score 0.00075 - // - // 10 has load 0.0005 and supported - // 10 with stake 1000.0 - // 20 has load 0.00075 and supported - // 20 with stake 1000.0 - // 30 has load 0 and supported - // 30 with stake 0 - // 40 has load 0 and supported - // 40 with stake 0 - // 2 has load 0.00075 and supported - // 10 with stake 166.66666666666674 20 with stake 333.33333333333326 30 with stake 0 - // 4 has load 0.00075 and supported - // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0 - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .validator_pool(true) - .build(), - || { - // We don't really care about this. At this point everything is even. - assert_eq_uvec!(validator_controllers(), vec![40, 30]); - - // Set payees to Controller - assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller)); - assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller)); - - // no one is a nominator - assert_eq!(>::enumerate().count(), 0 as usize); - - // bond [2,1] / [4,3] a nominator - let _ = Balances::deposit_creating(&1, 1000); - let _ = Balances::deposit_creating(&3, 1000); - - assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(2), vec![11, 21, 31])); - - assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41])); - - // New era => election algorithm will trigger - start_era(1); - - assert_eq_uvec!(validator_controllers(), vec![20, 10]); - - assert_eq!(Staking::stakers(11).own, 1000); - assert_eq!(Staking::stakers(21).own, 1000); - - if cfg!(feature = "equalize") { - assert_eq!(Staking::stakers(11).total, 1000 + 499); - assert_eq!(Staking::stakers(21).total, 1000 + 499); - } else { - assert_eq!(Staking::stakers(11).total, 1000 + 332); - assert_eq!(Staking::stakers(21).total, 1000 + 666); - } - - // Nominator's stake distribution. - assert_eq!(Staking::stakers(11).others.iter().map(|e| e.who).collect::>>(), vec![3, 1]); - assert_eq!(Staking::stakers(21).others.iter().map(|e| e.who).collect::>>(), vec![3, 1]); - - if cfg!(feature = "equalize") { - assert_eq_uvec!( - Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), - vec![333, 166] - ); - assert_eq!( - Staking::stakers(11).others.iter().map(|e| e.value).sum::>(), - 499 - ); - assert_eq_uvec!( - Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), - vec![333, 166] - ); - assert_eq!( - Staking::stakers(21).others.iter().map(|e| e.value).sum::>(), - 499 - ); - } else { - assert_eq_uvec!( - Staking::stakers(11).others.iter().map(|e| e.value).collect::>>(), - vec![166, 166] - ); - assert_eq!( - Staking::stakers(11).others.iter().map(|e| e.value).sum::>(), - 332 - ); - assert_eq_uvec!( - Staking::stakers(21).others.iter().map(|e| e.value).collect::>>(), - vec![333, 333] - ); - assert_eq!( - Staking::stakers(21).others.iter().map(|e| e.value).sum::>(), - 666 - ); - } - check_exposure_all(); - check_nominator_all(); - }); -} #[test] fn switching_roles() { @@ -1638,13 +1547,13 @@ fn phragmen_linear_worse_case_equalize() { assert_eq_uvec!(validator_controllers(), vec![10, 60, 40, 20, 50, 30, 70]); - assert_eq!(Staking::stakers(11).total, 3000); - assert_eq!(Staking::stakers(21).total, 2254); - assert_eq!(Staking::stakers(31).total, 2254); - assert_eq!(Staking::stakers(41).total, 1926); - assert_eq!(Staking::stakers(51).total, 1871); - assert_eq!(Staking::stakers(61).total, 1892); - assert_eq!(Staking::stakers(71).total, 1799); + assert_eq_error_rate!(Staking::stakers(11).total, 3000, 2); + assert_eq_error_rate!(Staking::stakers(21).total, 2255, 2); + assert_eq_error_rate!(Staking::stakers(31).total, 2255, 2); + assert_eq_error_rate!(Staking::stakers(41).total, 1925, 2); + assert_eq_error_rate!(Staking::stakers(51).total, 1870, 2); + assert_eq_error_rate!(Staking::stakers(61).total, 1890, 2); + assert_eq_error_rate!(Staking::stakers(71).total, 1800, 2); check_exposure_all(); check_nominator_all(); @@ -1652,7 +1561,7 @@ fn phragmen_linear_worse_case_equalize() { } #[test] -fn phragmen_chooses_correct_number_of_validators() { +fn new_era_elects_correct_number_of_validators() { with_externalities(&mut ExtBuilder::default() .nominate(true) .validator_pool(true) @@ -1672,26 +1581,6 @@ fn phragmen_chooses_correct_number_of_validators() { }) } - -#[test] -fn phragmen_score_should_be_accurate_on_large_stakes() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .build(), - || { - bond_validator(2, u64::max_value()); - bond_validator(4, u64::max_value()); - bond_validator(6, u64::max_value()-1); - bond_validator(8, u64::max_value()-2); - - start_era(1); - - assert_eq!(validator_controllers(), vec![4, 2]); - check_exposure_all(); - check_nominator_all(); - }) -} - #[test] fn phragmen_should_not_overflow_validators() { with_externalities(&mut ExtBuilder::default() @@ -1765,84 +1654,6 @@ fn phragmen_should_not_overflow_ultimate() { }) } - -#[test] -fn phragmen_large_scale_test() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .minimum_validator_count(1) - .validator_count(20) - .build(), - || { - let _ = Staking::chill(Origin::signed(10)); - let _ = Staking::chill(Origin::signed(20)); - let _ = Staking::chill(Origin::signed(30)); - let prefix = 200; - - bond_validator(prefix + 2, 1); - bond_validator(prefix + 4, 100); - bond_validator(prefix + 6, 1000000); - bond_validator(prefix + 8, 100000000001000); - bond_validator(prefix + 10, 100000000002000); - bond_validator(prefix + 12, 100000000003000); - bond_validator(prefix + 14, 400000000000000); - bond_validator(prefix + 16, 400000000001000); - bond_validator(prefix + 18, 18000000000000000); - bond_validator(prefix + 20, 20000000000000000); - bond_validator(prefix + 22, 500000000000100000); - bond_validator(prefix + 24, 500000000000200000); - - bond_nominator(50, 990000000000000000, vec![ - prefix + 3, - prefix + 5, - prefix + 7, - prefix + 9, - prefix + 11, - prefix + 13, - prefix + 15, - prefix + 17, - prefix + 19, - prefix + 21, - prefix + 23, - prefix + 25] - ); - - start_era(1); - - check_exposure_all(); - check_nominator_all(); - }) -} - -#[test] -fn phragmen_large_scale_test_2() { - with_externalities(&mut ExtBuilder::default() - .nominate(false) - .minimum_validator_count(1) - .validator_count(2) - .build(), - || { - let _ = Staking::chill(Origin::signed(10)); - let _ = Staking::chill(Origin::signed(20)); - let nom_budget: u64 = 1_000_000_000_000_000_000; - let c_budget: u64 = 4_000_000; - - bond_validator(2, c_budget as u64); - bond_validator(4, c_budget as u64); - - bond_nominator(50, nom_budget, vec![3, 5]); - - start_era(1); - - // Each exposure => total == own + sum(others) - check_exposure_all(); - check_nominator_all(); - - assert_total_expo(3, nom_budget / 2 + c_budget); - assert_total_expo(5, nom_budget / 2 + c_budget); - }) -} - #[test] fn reward_validator_slashing_validator_doesnt_overflow() { with_externalities(&mut ExtBuilder::default() diff --git a/substrate/srml/support/src/lib.rs b/substrate/srml/support/src/lib.rs index 6935b4508d..1b8dc11f60 100644 --- a/substrate/srml/support/src/lib.rs +++ b/substrate/srml/support/src/lib.rs @@ -225,37 +225,6 @@ macro_rules! __assert_eq_uvec { } } -/// Checks that `$x` is equal to `$y` with an error rate of `$error`. -/// -/// # Example -/// -/// ```rust -/// # fn main() { -/// srml_support::assert_eq_error_rate!(10, 10, 0); -/// srml_support::assert_eq_error_rate!(10, 11, 1); -/// srml_support::assert_eq_error_rate!(12, 10, 2); -/// # } -/// ``` -/// -/// ```rust,should_panic -/// # fn main() { -/// srml_support::assert_eq_error_rate!(12, 10, 1); -/// # } -/// ``` -#[macro_export] -#[cfg(feature = "std")] -macro_rules! assert_eq_error_rate { - ($x:expr, $y:expr, $error:expr) => { - assert!( - ($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)), - "{:?} != {:?} (with error rate {:?})", - $x, - $y, - $error, - ); - }; -} - /// The void type - it cannot exist. // Oh rust, you crack me up... #[derive(Clone, Eq, PartialEq)]