mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 08:11:04 +00:00
Refactor: fixed point arithmetic for SRML. (#3456)
* Macro-ify perthings. * Refactor fixed64 * Half-workign phragmen refactor. * Finalize phragmen refactor. * Fix creation of perquintill * Fix build errors * Line-width * Fix more build errors. * Line-width * Fix offence test * Resolve all TODOs. * Apply suggestions from code review Co-Authored-By: Gavin Wood <gavin@parity.io> Co-Authored-By: thiolliere <gui.thiolliere@gmail.com> * Fix most of the review comments. * Updates to multiply by rational * Fxi build * Fix abs issue with Fixed64 * Fix tests and improvements. * Fix build * Remove more tests from staking. * Review comments. * Add fuzzing stuff. * Better fuzzing * Better doc. * Bump. * Master.into() * A bit more hardening. * Final nits. * Update lock * Fix indent. * Revert lock file. * Bump.
This commit is contained in:
@@ -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|
|
||||
<TestCurrencyToVote as Convert<Balance, u128>>::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 = <SupportMap<u64>>::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<PhragmenStakedAssignment<AccountId>>)>
|
||||
= Vec::with_capacity(assignments.len());
|
||||
for (n, assignment) in assignments.iter() {
|
||||
let mut staked_assignment
|
||||
: Vec<PhragmenStakedAssignment<AccountId>>
|
||||
= 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,
|
||||
|
||||
@@ -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<AccountId> {
|
||||
/// 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<AccountId> {
|
||||
/// 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<AccountId> {
|
||||
/// 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> = (AccountId, ExtendedBalance);
|
||||
/// Means a particular `AccountId` was backed by `Perbill`th of a nominator's stake.
|
||||
pub type PhragmenAssignment<AccountId> = (AccountId, Perbill);
|
||||
|
||||
/// Means a particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake.
|
||||
pub type PhragmenStakedAssignment<AccountId> = (AccountId, ExtendedBalance);
|
||||
|
||||
/// Final result of the phragmen election.
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
@@ -131,7 +125,7 @@ pub struct Support<AccountId> {
|
||||
/// Total support.
|
||||
pub total: ExtendedBalance,
|
||||
/// Support from voters.
|
||||
pub others: Vec<PhragmenAssignment<AccountId>>,
|
||||
pub others: Vec<PhragmenStakedAssignment<AccountId>>,
|
||||
}
|
||||
|
||||
/// A linkage from a candidate and its [`Support`].
|
||||
@@ -164,8 +158,7 @@ pub fn elect<AccountId, Balance, FS, C>(
|
||||
for<'r> FS: Fn(&'r AccountId) -> Balance,
|
||||
C: Convert<Balance, u64> + Convert<u128, Balance>,
|
||||
{
|
||||
let to_votes = |b: Balance|
|
||||
<C as Convert<Balance, u64>>::convert(b) as ExtendedBalance;
|
||||
let to_votes = |b: Balance| <C as Convert<Balance, u64>>::convert(b) as ExtendedBalance;
|
||||
|
||||
// return structures
|
||||
let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>;
|
||||
@@ -192,7 +185,7 @@ pub fn elect<AccountId, Balance, FS, C>(
|
||||
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<AccountId, Balance, FS, C>(
|
||||
who,
|
||||
edges: edges,
|
||||
budget: to_votes(voter_stake),
|
||||
load: Fraction::zero(),
|
||||
load: Rational128::zero(),
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -245,24 +238,29 @@ pub fn elect<AccountId, Balance, FS, C>(
|
||||
// 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<AccountId, Balance, FS, C>(
|
||||
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<AccountId, Balance, FS, C>(
|
||||
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::<ExtendedBalance>();
|
||||
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::<u32>();
|
||||
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<AccountId, Balance, FS, C>(
|
||||
/// * `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<Balance, AccountId, FS, C>(
|
||||
mut assignments: Vec<(AccountId, Vec<PhragmenAssignment<AccountId>>)>,
|
||||
pub fn equalize<Balance, AccountId, C, FS>(
|
||||
mut assignments: Vec<(AccountId, Vec<PhragmenStakedAssignment<AccountId>>)>,
|
||||
supports: &mut SupportMap<AccountId>,
|
||||
tolerance: ExtendedBalance,
|
||||
iterations: usize,
|
||||
@@ -399,7 +413,7 @@ pub fn equalize<Balance, AccountId, FS, C>(
|
||||
fn do_equalize<Balance, AccountId, C>(
|
||||
voter: &AccountId,
|
||||
budget_balance: Balance,
|
||||
elected_edges: &mut Vec<(AccountId, ExtendedBalance)>,
|
||||
elected_edges: &mut Vec<PhragmenStakedAssignment<AccountId>>,
|
||||
support_map: &mut SupportMap<AccountId>,
|
||||
tolerance: ExtendedBalance
|
||||
) -> ExtendedBalance where
|
||||
|
||||
@@ -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<Balance, u64> 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<PhragmenAssignment<AccountId>>)>) {
|
||||
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<AccountId>,
|
||||
voters: Vec<(AccountId, Vec<AccountId>)>,
|
||||
@@ -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<FS>(
|
||||
@@ -414,6 +430,5 @@ pub(crate) fn build_support_map<FS>(
|
||||
*r = other_stake;
|
||||
}
|
||||
}
|
||||
|
||||
supports
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<N>(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<N> ops::Mul<N> for Permill
|
||||
where
|
||||
N: Clone + From<u32> + UniqueSaturatedInto<u32> + ops::Rem<N, Output=N>
|
||||
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>,
|
||||
{
|
||||
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::<u32>();
|
||||
|
||||
// `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<f64> for Permill {
|
||||
fn from(x: f64) -> Permill {
|
||||
Permill::from_fraction(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<f32> 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<N>(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<N> ops::Mul<N> for Perbill
|
||||
where
|
||||
N: Clone + From<u32> + UniqueSaturatedInto<u32> + ops::Rem<N, Output=N>
|
||||
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>,
|
||||
{
|
||||
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::<u32>();
|
||||
|
||||
// `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<f64> for Perbill {
|
||||
fn from(x: f64) -> Perbill {
|
||||
Perbill::from_fraction(x)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<f32> 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::<u32>();
|
||||
// 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<u32> 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<Self> {
|
||||
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<Self> {
|
||||
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::<U256>::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> = Permill(n).into();
|
||||
let encoded = compact.encode();
|
||||
assert_eq!(encoded.len(), l);
|
||||
let decoded = <crate::codec::Compact<Permill>>::decode(&mut & encoded[..]).unwrap();
|
||||
let permill: Permill = decoded.into();
|
||||
assert_eq!(permill, Permill(n));
|
||||
|
||||
let compact: crate::codec::Compact<Perbill> = Perbill(n).into();
|
||||
let encoded = compact.encode();
|
||||
assert_eq!(encoded.len(), l);
|
||||
let decoded = <crate::codec::Compact<Perbill>>::decode(&mut & encoded[..]).unwrap();
|
||||
let perbill: Perbill = decoded.into();
|
||||
assert_eq!(perbill, Perbill(n));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Encode, Decode, PartialEq, Eq, Debug)]
|
||||
struct WithCompact<T: crate::codec::HasCompact> {
|
||||
data: T,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_compact_permill() {
|
||||
let data = WithCompact { data: Permill(1) };
|
||||
let encoded = data.encode();
|
||||
assert_eq!(data, WithCompact::<Permill>::decode(&mut &encoded[..]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_compact_perbill() {
|
||||
let data = WithCompact { data: Perbill(1) };
|
||||
let encoded = data.encode();
|
||||
assert_eq!(data, WithCompact::<Perbill>::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::<U256>::into(std::u128::MAX) * 999_999_999u32) / 1_000_000_000u32).as_u128()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Permill::from_parts(999_999) * std::u128::MAX,
|
||||
((Into::<U256>::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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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<Offender: Clone> Offence<Offender> for UnresponsivenessOffence<Offender> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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<T: Trait> Module<T> {
|
||||
|
||||
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<T: Trait> Module<T> {
|
||||
);
|
||||
|
||||
if let Some(phragmen_result) = maybe_phragmen_result {
|
||||
let elected_stashes = phragmen_result.winners.iter().map(|(s, _)| s.clone()).collect::<Vec<T::AccountId>>();
|
||||
let mut assignments = phragmen_result.assignments;
|
||||
let elected_stashes = phragmen_result.winners.iter()
|
||||
.map(|(s, _)| s.clone())
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
let assignments = phragmen_result.assignments;
|
||||
|
||||
// helper closure.
|
||||
let to_balance = |b: ExtendedBalance|
|
||||
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(b);
|
||||
let to_votes = |b: BalanceOf<T>|
|
||||
<T::CurrencyToVote as Convert<BalanceOf<T>, 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<u64>` is also safe.
|
||||
let ratio_of = |b, r: ExtendedBalance| r.saturating_mul(to_votes(b)) / ACCURACY;
|
||||
let to_balance = |e: ExtendedBalance|
|
||||
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(e);
|
||||
|
||||
// Initialize the support of each candidate.
|
||||
let mut supports = <SupportMap<T::AccountId>>::new();
|
||||
@@ -1278,29 +1273,42 @@ impl<T: Trait> Module<T> {
|
||||
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<PhragmenStakedAssignment<T::AccountId>>)>
|
||||
= Vec::with_capacity(assignments.len());
|
||||
for (n, assignment) in assignments.iter() {
|
||||
let mut staked_assignment
|
||||
: Vec<PhragmenStakedAssignment<T::AccountId>>
|
||||
= 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<T: Trait + authorship::Trait> authorship::EventHandler<T::AccountId, T::Blo
|
||||
}
|
||||
}
|
||||
|
||||
// This is guarantee not to overflow on whatever values.
|
||||
// `num` must be inferior to `den` otherwise it will be reduce to `den`.
|
||||
fn multiply_by_rational<N>(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::<u32>();
|
||||
|
||||
// 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<T>(rstd::marker::PhantomData<T>);
|
||||
|
||||
@@ -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<Test, Staking>;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 <MinValidators>
|
||||
// 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<BalanceOf<Test>>>(),
|
||||
vec![599, 400]
|
||||
vec![600, 400]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(11).others.iter().map(|e| e.who).collect::<Vec<u64>>(),
|
||||
@@ -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<BalanceOf<Test>>>(),
|
||||
vec![400, 599]
|
||||
vec![400, 600]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(21).others.iter().map(|e| e.who).collect::<Vec<u64>>(),
|
||||
@@ -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<BalanceOf<Test>>>(),
|
||||
vec![599, 599]
|
||||
vec![600, 600]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(21).others.iter().map(|e| e.who).collect::<Vec<u64>>(),
|
||||
@@ -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!(<Nominators<Test>>::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<BalanceOf<Test>>>(), vec![3, 1]);
|
||||
assert_eq!(Staking::stakers(21).others.iter().map(|e| e.who).collect::<Vec<BalanceOf<Test>>>(), vec![3, 1]);
|
||||
|
||||
if cfg!(feature = "equalize") {
|
||||
assert_eq_uvec!(
|
||||
Staking::stakers(11).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(),
|
||||
vec![333, 166]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(11).others.iter().map(|e| e.value).sum::<BalanceOf<Test>>(),
|
||||
499
|
||||
);
|
||||
assert_eq_uvec!(
|
||||
Staking::stakers(21).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(),
|
||||
vec![333, 166]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(21).others.iter().map(|e| e.value).sum::<BalanceOf<Test>>(),
|
||||
499
|
||||
);
|
||||
} else {
|
||||
assert_eq_uvec!(
|
||||
Staking::stakers(11).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(),
|
||||
vec![166, 166]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(11).others.iter().map(|e| e.value).sum::<BalanceOf<Test>>(),
|
||||
332
|
||||
);
|
||||
assert_eq_uvec!(
|
||||
Staking::stakers(21).others.iter().map(|e| e.value).collect::<Vec<BalanceOf<Test>>>(),
|
||||
vec![333, 333]
|
||||
);
|
||||
assert_eq!(
|
||||
Staking::stakers(21).others.iter().map(|e| e.value).sum::<BalanceOf<Test>>(),
|
||||
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()
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user