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:
Kian Paimani
2019-09-25 11:21:05 +02:00
committed by GitHub
parent 87688aadaa
commit 1c15ca6ad1
19 changed files with 1909 additions and 961 deletions
+24 -10
View File
@@ -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,
+80 -66
View File
@@ -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
+21 -6
View File
@@ -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
}
+220 -4
View File
@@ -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);
}
+1
View File
@@ -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"]
+3 -3
View File
@@ -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 {
+45 -486
View File
@@ -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
+1 -1
View File
@@ -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.