staking: Flexible generation of reward curve and associated tweaks (#8327)

* Initial abstraction

* Alter rest of APIs

* Fixes

* Some extra getters in Gilt pallet.

* Refactor Gilt to avoid u128 conversions

* Simplify and improve pow in per_things

* Add scalar division to per_things

* Renaming from_fraction -> from_float, drop _approximation

* Fixes

* Fixes

* Fixes

* Fixes

* Make stuff build

* Fixes

* Fixes

* Fixes

* Fixes

* Update .gitignore

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/gilt/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/gilt/src/mock.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Fixes

* Fixes

* Fixes

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Gavin Wood
2021-03-16 13:03:58 +01:00
committed by GitHub
parent b6c626399e
commit 363db4f086
32 changed files with 342 additions and 226 deletions
+1
View File
@@ -24,3 +24,4 @@ rls*.log
.cargo/
.cargo-remote.toml
*.bin
*.iml
+1 -1
View File
@@ -84,7 +84,7 @@ mod multiplier_tests {
let t1 = v * (s/m - ss/m);
let t2 = v.powi(2) * (s/m - ss/m).powi(2) / 2.0;
let next_float = previous_float * (1.0 + t1 + t2);
Multiplier::from_fraction(next_float)
Multiplier::from_float(next_float)
}
fn run_with_system_weight<F>(w: Weight, assertions: F) where F: Fn() -> () {
+5 -4
View File
@@ -471,7 +471,7 @@ parameter_types! {
pub const ElectionLookahead: BlockNumber = EPOCH_DURATION_IN_BLOCKS / 4;
pub const MaxIterations: u32 = 10;
// 0.05%. The higher the value, the more strict solution acceptance becomes.
pub MinSolutionScoreBump: Perbill = Perbill::from_rational_approximation(5u32, 10_000);
pub MinSolutionScoreBump: Perbill = Perbill::from_rational(5u32, 10_000);
pub OffchainSolutionWeightLimit: Weight = RuntimeBlockWeights::get()
.get(DispatchClass::Normal)
.max_extrinsic.expect("Normal extrinsics have a weight limit configured; qed")
@@ -496,7 +496,7 @@ impl pallet_staking::Config for Runtime {
pallet_collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>
>;
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
type ElectionLookahead = ElectionLookahead;
@@ -520,7 +520,7 @@ parameter_types! {
pub const Fallback: pallet_election_provider_multi_phase::FallbackStrategy =
pallet_election_provider_multi_phase::FallbackStrategy::Nothing;
pub SolutionImprovementThreshold: Perbill = Perbill::from_rational_approximation(1u32, 10_000);
pub SolutionImprovementThreshold: Perbill = Perbill::from_rational(1u32, 10_000);
// miner configs
pub const MultiPhaseUnsignedPriority: TransactionPriority = StakingUnsignedPriority::get() - 1u64;
@@ -767,7 +767,7 @@ parameter_types! {
pub const DepositPerContract: Balance = TombstoneDeposit::get();
pub const DepositPerStorageByte: Balance = deposit(0, 1);
pub const DepositPerStorageItem: Balance = deposit(1, 0);
pub RentFraction: Perbill = Perbill::from_rational_approximation(1u32, 30 * DAYS);
pub RentFraction: Perbill = Perbill::from_rational(1u32, 30 * DAYS);
pub const SurchargeReward: Balance = 150 * MILLICENTS;
pub const SignedClaimHandicap: u32 = 2;
pub const MaxDepth: u32 = 32;
@@ -1065,6 +1065,7 @@ parameter_types! {
impl pallet_gilt::Config for Runtime {
type Event = Event;
type Currency = Balances;
type CurrencyBalance = Balance;
type AdminOrigin = frame_system::EnsureRoot<AccountId>;
type Deficit = ();
type Surplus = ();
+1 -1
View File
@@ -290,7 +290,7 @@ impl<FullIdentification: Clone> Offence<FullIdentification>
fn slash_fraction(offenders_count: u32, validator_set_count: u32) -> Perbill {
// the formula is min((3k / n)^2, 1)
let x = Perbill::from_rational_approximation(3 * offenders_count, validator_set_count);
let x = Perbill::from_rational(3 * offenders_count, validator_set_count);
// _ ^ 2
x.square()
}
+1 -1
View File
@@ -784,7 +784,7 @@ impl<T: Config> frame_support::traits::EstimateNextSessionRotation<T::BlockNumbe
let elapsed = CurrentSlot::get().saturating_sub(Self::current_epoch_start()) + 1;
(
Some(Percent::from_rational_approximation(
Some(Percent::from_rational(
*elapsed,
T::EpochDuration::get(),
)),
+1 -1
View File
@@ -205,7 +205,7 @@ impl pallet_staking::Config for Test {
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type SessionInterface = Self;
type UnixTime = pallet_timestamp::Module<Test>;
type RewardCurve = RewardCurve;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
type NextNewSession = Session;
type ElectionLookahead = ElectionLookahead;
+1 -1
View File
@@ -246,7 +246,7 @@ parameter_types! {
pub const DepositPerContract: u64 = 8 * DepositPerStorageByte::get();
pub const DepositPerStorageByte: u64 = 10_000;
pub const DepositPerStorageItem: u64 = 10_000;
pub RentFraction: Perbill = Perbill::from_rational_approximation(4u32, 10_000u32);
pub RentFraction: Perbill = Perbill::from_rational(4u32, 10_000u32);
pub const SurchargeReward: u64 = 500_000;
pub const MaxDepth: u32 = 100;
pub const MaxValueSize: u32 = 16_384;
+48 -12
View File
@@ -78,7 +78,7 @@ pub mod weights;
pub mod pallet {
use sp_std::prelude::*;
use sp_arithmetic::{Perquintill, PerThing};
use sp_runtime::traits::{Zero, Saturating, SaturatedConversion};
use sp_runtime::traits::{Zero, Saturating};
use frame_support::traits::{Currency, OnUnbalanced, ReservableCurrency};
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
@@ -96,7 +96,13 @@ pub mod pallet {
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// Currency type that this works on.
type Currency: ReservableCurrency<Self::AccountId>;
type Currency: ReservableCurrency<Self::AccountId, Balance=Self::CurrencyBalance>;
/// Just the `Currency::Balance` type; we have this item to allow us to constrain it to
/// `From<u64>`.
type CurrencyBalance:
sp_runtime::traits::AtLeast32BitUnsigned + codec::FullCodec + Copy
+ MaybeSerializeDeserialize + sp_std::fmt::Debug + Default + From<u64>;
/// Origin required for setting the target proportion to be under gilt.
type AdminOrigin: EnsureOrigin<Self::Origin>;
@@ -448,11 +454,10 @@ pub mod pallet {
// Multiply the proportion it is by the total issued.
let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
ActiveTotal::<T>::mutate(|totals| {
let nongilt_issuance: u128 = total_issuance.saturating_sub(totals.frozen)
.saturated_into();
let nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
let effective_issuance = totals.proportion.left_from_one()
.saturating_reciprocal_mul(nongilt_issuance);
let gilt_value: BalanceOf<T> = (gilt.proportion * effective_issuance).saturated_into();
let gilt_value = gilt.proportion * effective_issuance;
totals.frozen = totals.frozen.saturating_sub(gilt.amount);
totals.proportion = totals.proportion.saturating_sub(gilt.proportion);
@@ -488,7 +493,40 @@ pub mod pallet {
}
}
/// Issuance information returned by `issuance()`.
pub struct IssuanceInfo<Balance> {
/// The balance held in reserve over all active gilts.
pub reserved: Balance,
/// The issuance not held in reserve for active gilts. Together with `reserved` this sums to
/// `Currency::total_issuance`.
pub non_gilt: Balance,
/// The balance that `reserved` is effectively worth, at present. This is not issued funds
/// and could be less than `reserved` (though in most cases should be greater).
pub effective: Balance,
}
impl<T: Config> Pallet<T> {
/// Get the target amount of Gilts that we're aiming for.
pub fn target() -> Perquintill {
ActiveTotal::<T>::get().target
}
/// Returns information on the issuance of gilts.
pub fn issuance() -> IssuanceInfo<BalanceOf<T>> {
let totals = ActiveTotal::<T>::get();
let total_issuance = T::Currency::total_issuance();
let non_gilt = total_issuance.saturating_sub(totals.frozen);
let effective = totals.proportion.left_from_one()
.saturating_reciprocal_mul(non_gilt);
IssuanceInfo {
reserved: totals.frozen,
non_gilt,
effective,
}
}
/// Attempt to enlarge our gilt-set from bids in order to satisfy our desired target amount
/// of funds frozen into gilts.
pub fn pursue_target(max_bids: u32) -> Weight {
@@ -497,11 +535,10 @@ pub mod pallet {
let missing = totals.target.saturating_sub(totals.proportion);
let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get());
let nongilt_issuance: u128 = total_issuance.saturating_sub(totals.frozen)
.saturated_into();
let nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
let effective_issuance = totals.proportion.left_from_one()
.saturating_reciprocal_mul(nongilt_issuance);
let intake: BalanceOf<T> = (missing * effective_issuance).saturated_into();
let intake = missing * effective_issuance;
let (bids_taken, queues_hit) = Self::enlarge(intake, max_bids);
let first_from_each_queue = T::WeightInfo::pursue_target_per_queue(queues_hit);
@@ -550,13 +587,12 @@ pub mod pallet {
qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount);
// Now to activate the bid...
let nongilt_issuance: u128 = total_issuance.saturating_sub(totals.frozen)
.saturated_into();
let nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
let effective_issuance = totals.proportion.left_from_one()
.saturating_reciprocal_mul(nongilt_issuance);
let n: u128 = amount.saturated_into();
let n = amount;
let d = effective_issuance;
let proportion = Perquintill::from_rational_approximation(n, d);
let proportion = Perquintill::from_rational(n, d);
let who = bid.who;
let index = totals.index;
totals.frozen += bid.amount;
+1
View File
@@ -103,6 +103,7 @@ ord_parameter_types! {
impl pallet_gilt::Config for Test {
type Event = Event;
type Currency = Balances;
type CurrencyBalance = <Self as pallet_balances::Config>::Balance;
type AdminOrigin = frame_system::EnsureSignedBy<One, Self::AccountId>;
type Deficit = ();
type Surplus = ();
+1 -1
View File
@@ -358,7 +358,7 @@ impl<FullIdentification: Clone> Offence<FullIdentification>
fn slash_fraction(offenders_count: u32, validator_set_count: u32) -> Perbill {
// the formula is min((3k / n)^2, 1)
let x = Perbill::from_rational_approximation(3 * offenders_count, validator_set_count);
let x = Perbill::from_rational(3 * offenders_count, validator_set_count);
// _ ^ 2
x.square()
}
+1 -1
View File
@@ -211,7 +211,7 @@ impl pallet_staking::Config for Test {
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type SessionInterface = Self;
type UnixTime = pallet_timestamp::Module<Test>;
type RewardCurve = RewardCurve;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
type NextNewSession = Session;
type ElectionLookahead = ElectionLookahead;
+2 -6
View File
@@ -100,11 +100,7 @@ use frame_support::{
},
Parameter,
};
use frame_system::ensure_none;
use frame_system::offchain::{
SendTransactionTypes,
SubmitTransaction,
};
use frame_system::{ensure_none, offchain::{SendTransactionTypes, SubmitTransaction}};
pub use weights::WeightInfo;
pub mod sr25519 {
@@ -813,7 +809,7 @@ impl<Offender: Clone> Offence<Offender> for UnresponsivenessOffence<Offender> {
// basically, 10% can be offline with no slash, but after that, it linearly climbs up to 7%
// when 13/30 are offline (around 5% when 1/3 are offline).
if let Some(threshold) = offenders.checked_sub(validator_set_count / 10 + 1) {
let x = Perbill::from_rational_approximation(3 * threshold, validator_set_count);
let x = Perbill::from_rational(3 * threshold, validator_set_count);
x.saturating_mul(Perbill::from_percent(7))
} else {
Perbill::default()
@@ -170,7 +170,7 @@ impl pallet_staking::Config for Test {
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = ();
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type ElectionLookahead = ();
type Call = Call;
@@ -175,7 +175,7 @@ impl pallet_staking::Config for Test {
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = ();
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type ElectionLookahead = ();
type Call = Call;
+2 -2
View File
@@ -177,12 +177,12 @@ impl<
// (0% is never returned).
let progress = if now >= offset {
let current = (now - offset) % period.clone() + One::one();
Some(Percent::from_rational_approximation(
Some(Percent::from_rational(
current.clone(),
period.clone(),
))
} else {
Some(Percent::from_rational_approximation(
Some(Percent::from_rational(
now + One::one(),
offset,
))
+1 -1
View File
@@ -185,7 +185,7 @@ impl pallet_staking::Config for Test {
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = ();
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type ElectionLookahead = ();
type Call = Call;
+1 -1
View File
@@ -38,7 +38,7 @@ pub fn compute_total_payout<N>(
// Milliseconds per year for the Julian year (365.25 days).
const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100;
let portion = Perbill::from_rational_approximation(era_duration as u64, MILLISECONDS_PER_YEAR);
let portion = Perbill::from_rational(era_duration as u64, MILLISECONDS_PER_YEAR);
let payout = portion * yearly_inflation.calculate_for_fraction_times_denominator(
npos_token_staked,
total_tokens.clone(),
+54 -14
View File
@@ -782,6 +782,51 @@ impl<T: Config> SessionInterface<<T as frame_system::Config>::AccountId> for T w
}
}
/// Handler for determining how much of a balance should be paid out on the current era.
pub trait EraPayout<Balance> {
/// Determine the payout for this era.
///
/// Returns the amount to be paid to stakers in this era, as well as whatever else should be
/// paid out ("the rest").
fn era_payout(
total_staked: Balance,
total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance);
}
impl<Balance: Default> EraPayout<Balance> for () {
fn era_payout(
_total_staked: Balance,
_total_issuance: Balance,
_era_duration_millis: u64,
) -> (Balance, Balance) {
(Default::default(), Default::default())
}
}
pub struct ConvertCurve<T>(sp_std::marker::PhantomData<T>);
impl<
Balance: AtLeast32BitUnsigned + Clone,
T: Get<&'static PiecewiseLinear<'static>>,
> EraPayout<Balance> for ConvertCurve<T> {
fn era_payout(
total_staked: Balance,
total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance) {
let (validator_payout, max_payout) = inflation::compute_total_payout(
&T::get(),
total_staked,
total_issuance,
// Duration of era; more than u64::MAX is rewarded as u64::MAX.
era_duration_millis,
);
let rest = max_payout.saturating_sub(validator_payout.clone());
(validator_payout, rest)
}
}
pub trait Config: frame_system::Config + SendTransactionTypes<Call<Self>> {
/// The staking balance.
type Currency: LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>;
@@ -838,9 +883,9 @@ pub trait Config: frame_system::Config + SendTransactionTypes<Call<Self>> {
/// Interface for interacting with a session module.
type SessionInterface: self::SessionInterface<Self::AccountId>;
/// The NPoS reward curve used to define yearly inflation.
/// The payout for validators and the system for the current era.
/// See [Era payout](./index.html#era-payout).
type RewardCurve: Get<&'static PiecewiseLinear<'static>>;
type EraPayout: EraPayout<BalanceOf<Self>>;
/// Something that can estimate the next session change, accurately or as a best effort guess.
type NextNewSession: EstimateNextNewSession<Self::BlockNumber>;
@@ -2413,7 +2458,7 @@ impl<T: Config> Module<T> {
// This is the fraction of the total reward that the validator and the
// nominators will get.
let validator_total_reward_part = Perbill::from_rational_approximation(
let validator_total_reward_part = Perbill::from_rational(
validator_reward_points,
total_reward_points,
);
@@ -2428,7 +2473,7 @@ impl<T: Config> Module<T> {
let validator_leftover_payout = validator_total_payout - validator_commission_payout;
// Now let's calculate how this is split to the validator.
let validator_exposure_part = Perbill::from_rational_approximation(
let validator_exposure_part = Perbill::from_rational(
exposure.own,
exposure.total,
);
@@ -2445,7 +2490,7 @@ impl<T: Config> Module<T> {
// Lets now calculate how this is split to the nominators.
// Reward only the clipped exposures. Note this is not necessarily sorted.
for nominator in exposure.others.iter() {
let nominator_exposure_part = Perbill::from_rational_approximation(
let nominator_exposure_part = Perbill::from_rational(
nominator.value,
exposure.total,
);
@@ -2837,15 +2882,10 @@ impl<T: Config> Module<T> {
if let Some(active_era_start) = active_era.start {
let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
let era_duration = now_as_millis_u64 - active_era_start;
let (validator_payout, max_payout) = inflation::compute_total_payout(
&T::RewardCurve::get(),
Self::eras_total_stake(&active_era.index),
T::Currency::total_issuance(),
// Duration of era; more than u64::MAX is rewarded as u64::MAX.
era_duration.saturated_into::<u64>(),
);
let rest = max_payout.saturating_sub(validator_payout);
let era_duration = (now_as_millis_u64 - active_era_start).saturated_into::<u64>();
let staked = Self::eras_total_stake(&active_era.index);
let issuance = T::Currency::total_issuance();
let (validator_payout, rest) = T::EraPayout::era_payout(staked, issuance, era_duration);
Self::deposit_event(RawEvent::EraPayout(active_era.index, validator_payout, rest));
+9 -12
View File
@@ -260,7 +260,7 @@ impl Config for Test {
type SlashCancelOrigin = frame_system::EnsureRoot<Self::AccountId>;
type BondingDuration = BondingDuration;
type SessionInterface = Self;
type RewardCurve = RewardCurve;
type EraPayout = ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type ElectionLookahead = ElectionLookahead;
type Call = Call;
@@ -670,25 +670,22 @@ pub(crate) fn start_active_era(era_index: EraIndex) {
}
pub(crate) fn current_total_payout_for_duration(duration: u64) -> Balance {
let reward = inflation::compute_total_payout(
<Test as Config>::RewardCurve::get(),
let (payout, _rest) = <Test as Config>::EraPayout::era_payout(
Staking::eras_total_stake(active_era()),
Balances::total_issuance(),
duration,
)
.0;
assert!(reward > 0);
reward
);
assert!(payout > 0);
payout
}
pub(crate) fn maximum_payout_for_duration(duration: u64) -> Balance {
inflation::compute_total_payout(
<Test as Config>::RewardCurve::get(),
0,
let (payout, rest) = <Test as Config>::EraPayout::era_payout(
Staking::eras_total_stake(active_era()),
Balances::total_issuance(),
duration,
)
.1
);
payout + rest
}
/// Time it takes to finish a session.
+1 -1
View File
@@ -376,7 +376,7 @@ pub fn create_assignments_for_offchain<T: Config>(
),
&'static str
> {
let ratio = OffchainAccuracy::from_rational_approximation(1, MAX_NOMINATIONS);
let ratio = OffchainAccuracy::from_rational(1, MAX_NOMINATIONS);
let assignments: Vec<Assignment<T::AccountId, OffchainAccuracy>> = <Nominators<T>>::iter()
.take(num_assignments as usize)
.map(|(n, t)| Assignment {
+8 -8
View File
@@ -209,10 +209,10 @@ fn rewards_should_work() {
individual: vec![(11, 100), (21, 50)].into_iter().collect(),
}
);
let part_for_10 = Perbill::from_rational_approximation::<u32>(1000, 1125);
let part_for_20 = Perbill::from_rational_approximation::<u32>(1000, 1375);
let part_for_100_from_10 = Perbill::from_rational_approximation::<u32>(125, 1125);
let part_for_100_from_20 = Perbill::from_rational_approximation::<u32>(375, 1375);
let part_for_10 = Perbill::from_rational::<u32>(1000, 1125);
let part_for_20 = Perbill::from_rational::<u32>(1000, 1375);
let part_for_100_from_10 = Perbill::from_rational::<u32>(125, 1125);
let part_for_100_from_20 = Perbill::from_rational::<u32>(375, 1375);
start_session(2);
start_session(3);
@@ -598,8 +598,8 @@ fn nominators_also_get_slashed_pro_rata() {
let slash_amount = slash_percent * exposed_stake;
let validator_share =
Perbill::from_rational_approximation(exposed_validator, exposed_stake) * slash_amount;
let nominator_share = Perbill::from_rational_approximation(
Perbill::from_rational(exposed_validator, exposed_stake) * slash_amount;
let nominator_share = Perbill::from_rational(
exposed_nominator,
exposed_stake,
) * slash_amount;
@@ -4270,8 +4270,8 @@ fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() {
let init_balance_10 = Balances::total_balance(&10);
let init_balance_100 = Balances::total_balance(&100);
let part_for_10 = Perbill::from_rational_approximation::<u32>(1000, 1125);
let part_for_100 = Perbill::from_rational_approximation::<u32>(125, 1125);
let part_for_10 = Perbill::from_rational::<u32>(1000, 1125);
let part_for_100 = Perbill::from_rational::<u32>(125, 1125);
// Check state
Payee::<Test>::insert(11, RewardDestination::Controller);
+4 -7
View File
@@ -130,11 +130,8 @@
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use codec::{Encode, Decode};
use sp_runtime::{
RuntimeDebug,
traits::SignedExtension,
generic::{CheckedExtrinsic, UncheckedExtrinsic},
};
use sp_runtime::{RuntimeDebug, traits::SignedExtension};
use sp_runtime::generic::{CheckedExtrinsic, UncheckedExtrinsic};
use crate::dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, DispatchError};
use sp_runtime::traits::SaturatedConversion;
use sp_arithmetic::{Perbill, traits::{BaseArithmetic, Saturating, Unsigned}};
@@ -964,13 +961,13 @@ mod tests {
smallvec![
WeightToFeeCoefficient {
coeff_integer: 0,
coeff_frac: Perbill::from_fraction(0.5),
coeff_frac: Perbill::from_float(0.5),
negative: false,
degree: 3
},
WeightToFeeCoefficient {
coeff_integer: 2,
coeff_frac: Perbill::from_rational_approximation(1u32, 3u32),
coeff_frac: Perbill::from_rational(1u32, 3u32),
negative: false,
degree: 2
},
@@ -38,75 +38,75 @@ fn main() {
// peru16
let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1));
let ratio = PerU16::from_rational_approximation(smaller, bigger);
let ratio = PerU16::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64),
PerU16::from_float(smaller as f64 / bigger.max(1) as f64),
1,
);
let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1));
let ratio = PerU16::from_rational_approximation(smaller, bigger);
let ratio = PerU16::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64),
PerU16::from_float(smaller as f64 / bigger.max(1) as f64),
1,
);
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
let ratio = PerU16::from_rational_approximation(smaller, bigger);
let ratio = PerU16::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64),
PerU16::from_float(smaller as f64 / bigger.max(1) as f64),
1,
);
// percent
let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1));
let ratio = Percent::from_rational_approximation(smaller, bigger);
let ratio = Percent::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
Percent::from_fraction(smaller as f64 / bigger.max(1) as f64),
Percent::from_float(smaller as f64 / bigger.max(1) as f64),
1,
);
let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1));
let ratio = Percent::from_rational_approximation(smaller, bigger);
let ratio = Percent::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
Percent::from_fraction(smaller as f64 / bigger.max(1) as f64),
Percent::from_float(smaller as f64 / bigger.max(1) as f64),
1,
);
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
let ratio = Percent::from_rational_approximation(smaller, bigger);
let ratio = Percent::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
Percent::from_fraction(smaller as f64 / bigger.max(1) as f64),
Percent::from_float(smaller as f64 / bigger.max(1) as f64),
1,
);
// perbill
let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1));
let ratio = Perbill::from_rational_approximation(smaller, bigger);
let ratio = Perbill::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
Perbill::from_fraction(smaller as f64 / bigger.max(1) as f64),
Perbill::from_float(smaller as f64 / bigger.max(1) as f64),
100,
);
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
let ratio = Perbill::from_rational_approximation(smaller, bigger);
let ratio = Perbill::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
Perbill::from_fraction(smaller as f64 / bigger.max(1) as f64),
Perbill::from_float(smaller as f64 / bigger.max(1) as f64),
100,
);
// perquintillion
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
let ratio = Perquintill::from_rational_approximation(smaller, bigger);
let ratio = Perquintill::from_rational(smaller, bigger);
assert_per_thing_equal_error(
ratio,
Perquintill::from_fraction(smaller as f64 / bigger.max(1) as f64),
Perquintill::from_float(smaller as f64 / bigger.max(1) as f64),
1000,
);
@@ -376,7 +376,7 @@ macro_rules! implement_fixed {
}
#[cfg(any(feature = "std", test))]
pub fn from_fraction(x: f64) -> Self {
pub fn from_float(x: f64) -> Self {
Self((x * (<Self as FixedPointNumber>::DIV as f64)) as $inner_type)
}
+1 -1
View File
@@ -494,7 +494,7 @@ mod threshold_compare_tests {
fn peru16_rational_does_not_overflow() {
// A historical example that will panic only for per_thing type that are created with
// maximum capacity of their type, e.g. PerU16.
let _ = PerU16::from_rational_approximation(17424870u32, 17424870);
let _ = PerU16::from_rational(17424870u32, 17424870);
}
#[test]
+162 -114
View File
@@ -18,8 +18,9 @@
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use sp_std::{ops, fmt, prelude::*, convert::TryInto};
use sp_std::{ops, fmt, prelude::*, convert::{TryFrom, TryInto}};
use codec::{Encode, CompactAs};
use num_traits::Pow;
use crate::traits::{
SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic, Bounded, Zero, Unsigned,
One,
@@ -36,6 +37,7 @@ pub type UpperOf<P> = <P as PerThing>::Upper;
/// `X`_.
pub trait PerThing:
Sized + Saturating + Copy + Default + Eq + PartialEq + Ord + PartialOrd + Bounded + fmt::Debug
+ ops::Div<Output=Self> + ops::Mul<Output=Self> + Pow<usize, Output=Self>
{
/// The data type used to build this per-thingy.
type Inner: BaseArithmetic + Unsigned + Copy + Into<u128> + fmt::Debug;
@@ -70,14 +72,14 @@ pub trait PerThing:
fn from_percent(x: Self::Inner) -> Self {
let a: Self::Inner = x.min(100.into());
let b: Self::Inner = 100.into();
Self::from_rational_approximation::<Self::Inner>(a, b)
Self::from_rational::<Self::Inner>(a, b)
}
/// Return the product of multiplication of this value by itself.
fn square(self) -> Self {
let p = Self::Upper::from(self.deconstruct());
let q = Self::Upper::from(Self::ACCURACY);
Self::from_rational_approximation::<Self::Upper>(p * p, q * q)
Self::from_rational::<Self::Upper>(p * p, q * q)
}
/// Return the part left when `self` is saturating-subtracted from `Self::one()`.
@@ -204,7 +206,12 @@ pub trait PerThing:
/// Converts a fraction into `Self`.
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self;
fn from_float(x: f64) -> Self;
/// Same as `Self::from_float`.
#[deprecated = "Use from_float instead"]
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self { Self::from_float(x) }
/// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow.
///
@@ -219,16 +226,28 @@ pub trait PerThing:
/// # fn main () {
/// // 989/100 is technically closer to 99%.
/// assert_eq!(
/// Percent::from_rational_approximation(989u64, 1000),
/// Percent::from_rational(989u64, 1000),
/// Percent::from_parts(98),
/// );
/// # }
/// ```
fn from_rational_approximation<N>(p: N, q: N) -> Self
fn from_rational<N>(p: N, q: N) -> Self
where
N: Clone + Ord + TryInto<Self::Inner> + TryInto<Self::Upper> +
ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N> + Unsigned,
Self::Inner: Into<N>;
/// Same as `Self::from_rational`.
#[deprecated = "Use from_rational instead"]
fn from_rational_approximation<N>(p: N, q: N) -> Self
where
N: Clone + Ord + TryInto<Self::Inner> + TryInto<Self::Upper>
+ ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N> + Unsigned
+ Zero + One,
Self::Inner: Into<N>,
{
Self::from_rational(p, q)
}
}
/// The rounding method to use.
@@ -369,11 +388,11 @@ macro_rules! implement_per_thing {
/// NOTE: saturate to 0 or 1 if x is beyond `[0, 1]`
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self {
fn from_float(x: f64) -> Self {
Self::from_parts((x.max(0.).min(1.) * $max as f64) as Self::Inner)
}
fn from_rational_approximation<N>(p: N, q: N) -> Self
fn from_rational<N>(p: N, q: N) -> Self
where
N: Clone + Ord + TryInto<Self::Inner> + TryInto<Self::Upper>
+ ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N> + Unsigned
@@ -471,20 +490,31 @@ macro_rules! implement_per_thing {
PerThing::square(self)
}
/// See [`PerThing::from_fraction`].
/// See [`PerThing::from_float`].
#[cfg(feature = "std")]
pub fn from_fraction(x: f64) -> Self {
<Self as PerThing>::from_fraction(x)
pub fn from_float(x: f64) -> Self {
<Self as PerThing>::from_float(x)
}
/// See [`PerThing::from_rational_approximation`].
/// See [`PerThing::from_rational`].
#[deprecated = "Use `PerThing::from_rational` instead"]
pub fn from_rational_approximation<N>(p: N, q: N) -> Self
where N: Clone + Ord + TryInto<$type> +
TryInto<$upper_type> + ops::Div<N, Output=N> + ops::Rem<N, Output=N> +
ops::Add<N, Output=N> + Unsigned,
$type: Into<N>,
{
<Self as PerThing>::from_rational_approximation(p, q)
<Self as PerThing>::from_rational(p, q)
}
/// See [`PerThing::from_rational`].
pub fn from_rational<N>(p: N, q: N) -> Self
where N: Clone + Ord + TryInto<$type> +
TryInto<$upper_type> + ops::Div<N, Output=N> + ops::Rem<N, Output=N> +
ops::Add<N, Output=N> + Unsigned,
$type: Into<N>,
{
<Self as PerThing>::from_rational(p, q)
}
/// See [`PerThing::mul_floor`].
@@ -561,37 +591,13 @@ macro_rules! implement_per_thing {
/// Saturating multiply. Compute `self * rhs`, saturating at the numeric bounds instead of
/// overflowing. This operation is lossy.
fn saturating_mul(self, rhs: Self) -> Self {
let a = self.0 as $upper_type;
let b = rhs.0 as $upper_type;
let m = <$upper_type>::from($max);
let parts = a * b / m;
// This will always fit into $type.
Self::from_parts(parts as $type)
self * rhs
}
/// Saturating exponentiation. Computes `self.pow(exp)`, saturating at the numeric
/// bounds instead of overflowing. This operation is lossy.
fn saturating_pow(self, exp: usize) -> Self {
if self.is_zero() || self.is_one() {
self
} else {
let p = <$name as PerThing>::Upper::from(self.deconstruct());
let q = <$name as PerThing>::Upper::from(Self::ACCURACY);
let mut s = Self::one();
for _ in 0..exp {
if s.is_zero() {
break;
} else {
// x^2 always fits in Self::Upper if x fits in Self::Inner.
// Verified by a test.
s = Self::from_rational_approximation(
<$name as PerThing>::Upper::from(s.deconstruct()) * p,
q * q,
);
}
}
s
}
self.pow(exp)
}
}
@@ -607,7 +613,7 @@ macro_rules! implement_per_thing {
}
}
impl crate::traits::Bounded for $name {
impl Bounded for $name {
fn min_value() -> Self {
<Self as PerThing>::zero()
}
@@ -617,13 +623,48 @@ macro_rules! implement_per_thing {
}
}
impl ops::Mul for $name {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let a = self.0 as $upper_type;
let b = rhs.0 as $upper_type;
let m = <$upper_type>::from($max);
let parts = a * b / m;
// This will always fit into $type.
Self::from_parts(parts as $type)
}
}
impl Pow<usize> for $name {
type Output = Self;
fn pow(self, exp: usize) -> Self::Output {
if exp == 0 || self.is_one() {
return Self::one()
}
let mut result = self;
let mut exp = exp - 1;
while exp > 0 && !result.is_zero() {
if exp % 2 == 0 {
result = result.square();
exp /= 2;
} else {
result = result * self;
exp -= 1;
}
}
result
}
}
impl ops::Div for $name {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
let p = self.0;
let q = rhs.0;
Self::from_rational_approximation(p, q)
Self::from_rational(p, q)
}
}
@@ -648,6 +689,13 @@ macro_rules! implement_per_thing {
}
}
impl<N> ops::Div<N> for $name where $type: TryFrom<N> {
type Output = Self;
fn div(self, b: N) -> Self::Output {
<$type>::try_from(b).map_or(Self::zero(), |d| Self::from_parts(self.0 / d))
}
}
#[cfg(test)]
mod $test_mod {
use codec::{Encode, Decode};
@@ -657,13 +705,13 @@ macro_rules! implement_per_thing {
#[test]
fn macro_expanded_correctly() {
// needed for the `from_percent` to work. UPDATE: this is no longer needed; yet note
// that tests that use percentage or fractions such as $name::from_fraction(0.2) to
// that tests that use percentage or fractions such as $name::from_float(0.2) to
// create values will most likely be inaccurate when used with per_things that are
// not multiples of 100.
// assert!($max >= 100);
// assert!($max % 100 == 0);
// needed for `from_rational_approximation`
// needed for `from_rational`
assert!(2 * ($max as $upper_type) < <$upper_type>::max_value());
assert!(<$upper_type>::from($max) < <$upper_type>::max_value());
@@ -737,11 +785,11 @@ macro_rules! implement_per_thing {
assert_eq!($name::from_percent(100), $name::from_parts($max));
assert_eq!($name::from_percent(200), $name::from_parts($max));
assert_eq!($name::from_fraction(0.0), $name::from_parts(Zero::zero()));
assert_eq!($name::from_fraction(0.1), $name::from_parts($max / 10));
assert_eq!($name::from_fraction(1.0), $name::from_parts($max));
assert_eq!($name::from_fraction(2.0), $name::from_parts($max));
assert_eq!($name::from_fraction(-1.0), $name::from_parts(Zero::zero()));
assert_eq!($name::from_float(0.0), $name::from_parts(Zero::zero()));
assert_eq!($name::from_float(0.1), $name::from_parts($max / 10));
assert_eq!($name::from_float(1.0), $name::from_parts($max));
assert_eq!($name::from_float(2.0), $name::from_parts($max));
assert_eq!($name::from_float(-1.0), $name::from_parts(Zero::zero()));
}
#[test]
@@ -763,7 +811,7 @@ macro_rules! implement_per_thing {
($num_type:tt) => {
// multiplication from all sort of from_percent
assert_eq!(
$name::from_fraction(1.0) * $num_type::max_value(),
$name::from_float(1.0) * $num_type::max_value(),
$num_type::max_value()
);
if $max % 100 == 0 {
@@ -773,7 +821,7 @@ macro_rules! implement_per_thing {
1,
);
assert_eq!(
$name::from_fraction(0.5) * $num_type::max_value(),
$name::from_float(0.5) * $num_type::max_value(),
$num_type::max_value() / 2,
);
assert_eq_error_rate!(
@@ -783,30 +831,30 @@ macro_rules! implement_per_thing {
);
} else {
assert_eq!(
$name::from_fraction(0.99) * <$num_type>::max_value(),
$name::from_float(0.99) * <$num_type>::max_value(),
(
(
u256ify!($name::from_fraction(0.99).0) *
u256ify!($name::from_float(0.99).0) *
u256ify!(<$num_type>::max_value()) /
u256ify!($max)
).as_u128()
) as $num_type,
);
assert_eq!(
$name::from_fraction(0.50) * <$num_type>::max_value(),
$name::from_float(0.50) * <$num_type>::max_value(),
(
(
u256ify!($name::from_fraction(0.50).0) *
u256ify!($name::from_float(0.50).0) *
u256ify!(<$num_type>::max_value()) /
u256ify!($max)
).as_u128()
) as $num_type,
);
assert_eq!(
$name::from_fraction(0.01) * <$num_type>::max_value(),
$name::from_float(0.01) * <$num_type>::max_value(),
(
(
u256ify!($name::from_fraction(0.01).0) *
u256ify!($name::from_float(0.01).0) *
u256ify!(<$num_type>::max_value()) /
u256ify!($max)
).as_u128()
@@ -814,7 +862,7 @@ macro_rules! implement_per_thing {
);
}
assert_eq!($name::from_fraction(0.0) * $num_type::max_value(), 0);
assert_eq!($name::from_float(0.0) * $num_type::max_value(), 0);
// // multiplication with bounds
assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value());
@@ -828,7 +876,7 @@ macro_rules! implement_per_thing {
// accuracy test
assert_eq!(
$name::from_rational_approximation(1 as $type, 3) * 30 as $type,
$name::from_rational(1 as $type, 3) * 30 as $type,
10,
);
@@ -837,10 +885,10 @@ macro_rules! implement_per_thing {
#[test]
fn per_thing_mul_rounds_to_nearest_number() {
assert_eq!($name::from_fraction(0.33) * 10u64, 3);
assert_eq!($name::from_fraction(0.34) * 10u64, 3);
assert_eq!($name::from_fraction(0.35) * 10u64, 3);
assert_eq!($name::from_fraction(0.36) * 10u64, 4);
assert_eq!($name::from_float(0.33) * 10u64, 3);
assert_eq!($name::from_float(0.34) * 10u64, 3);
assert_eq!($name::from_float(0.35) * 10u64, 3);
assert_eq!($name::from_float(0.36) * 10u64, 4);
}
#[test]
@@ -858,33 +906,33 @@ macro_rules! implement_per_thing {
($num_type:tt) => {
// within accuracy boundary
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 0),
$name::from_rational(1 as $num_type, 0),
$name::one(),
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 1),
$name::from_rational(1 as $num_type, 1),
$name::one(),
);
assert_eq_error_rate!(
$name::from_rational_approximation(1 as $num_type, 3).0,
$name::from_rational(1 as $num_type, 3).0,
$name::from_parts($max / 3).0,
2
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 10),
$name::from_fraction(0.10),
$name::from_rational(1 as $num_type, 10),
$name::from_float(0.10),
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 4),
$name::from_fraction(0.25),
$name::from_rational(1 as $num_type, 4),
$name::from_float(0.25),
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 4),
$name::from_rational_approximation(2 as $num_type, 8),
$name::from_rational(1 as $num_type, 4),
$name::from_rational(2 as $num_type, 8),
);
// no accurate anymore but won't overflow.
assert_eq_error_rate!(
$name::from_rational_approximation(
$name::from_rational(
$num_type::max_value() - 1,
$num_type::max_value()
).0 as $upper_type,
@@ -892,7 +940,7 @@ macro_rules! implement_per_thing {
2,
);
assert_eq_error_rate!(
$name::from_rational_approximation(
$name::from_rational(
$num_type::max_value() / 3,
$num_type::max_value()
).0 as $upper_type,
@@ -900,7 +948,7 @@ macro_rules! implement_per_thing {
2,
);
assert_eq!(
$name::from_rational_approximation(1, $num_type::max_value()),
$name::from_rational(1, $num_type::max_value()),
$name::zero(),
);
};
@@ -914,28 +962,28 @@ macro_rules! implement_per_thing {
// almost at the edge
assert_eq!(
$name::from_rational_approximation(max_value - 1, max_value + 1),
$name::from_rational(max_value - 1, max_value + 1),
$name::from_parts($max - 2),
);
assert_eq!(
$name::from_rational_approximation(1, $max - 1),
$name::from_rational(1, $max - 1),
$name::from_parts(1),
);
assert_eq!(
$name::from_rational_approximation(1, $max),
$name::from_rational(1, $max),
$name::from_parts(1),
);
assert_eq!(
$name::from_rational_approximation(2, 2 * max_value - 1),
$name::from_rational(2, 2 * max_value - 1),
$name::from_parts(1),
);
assert_eq!(
$name::from_rational_approximation(1, max_value + 1),
$name::from_rational(1, max_value + 1),
$name::zero(),
);
assert_eq!(
$name::from_rational_approximation(3 * max_value / 2, 3 * max_value),
$name::from_fraction(0.5),
$name::from_rational(3 * max_value / 2, 3 * max_value),
$name::from_float(0.5),
);
$(per_thing_from_rationale_approx_test!($test_units);)*
@@ -943,66 +991,66 @@ macro_rules! implement_per_thing {
#[test]
fn per_things_mul_operates_in_output_type() {
// assert_eq!($name::from_fraction(0.5) * 100u32, 50u32);
assert_eq!($name::from_fraction(0.5) * 100u64, 50u64);
assert_eq!($name::from_fraction(0.5) * 100u128, 50u128);
// assert_eq!($name::from_float(0.5) * 100u32, 50u32);
assert_eq!($name::from_float(0.5) * 100u64, 50u64);
assert_eq!($name::from_float(0.5) * 100u128, 50u128);
}
#[test]
fn per_thing_saturating_op_works() {
assert_eq_error_rate!(
$name::from_fraction(0.5).saturating_add($name::from_fraction(0.4)).0 as $upper_type,
$name::from_fraction(0.9).0 as $upper_type,
$name::from_float(0.5).saturating_add($name::from_float(0.4)).0 as $upper_type,
$name::from_float(0.9).0 as $upper_type,
2,
);
assert_eq_error_rate!(
$name::from_fraction(0.5).saturating_add($name::from_fraction(0.5)).0 as $upper_type,
$name::from_float(0.5).saturating_add($name::from_float(0.5)).0 as $upper_type,
$name::one().0 as $upper_type,
2,
);
assert_eq!(
$name::from_fraction(0.6).saturating_add($name::from_fraction(0.5)),
$name::from_float(0.6).saturating_add($name::from_float(0.5)),
$name::one(),
);
assert_eq_error_rate!(
$name::from_fraction(0.6).saturating_sub($name::from_fraction(0.5)).0 as $upper_type,
$name::from_fraction(0.1).0 as $upper_type,
$name::from_float(0.6).saturating_sub($name::from_float(0.5)).0 as $upper_type,
$name::from_float(0.1).0 as $upper_type,
2,
);
assert_eq!(
$name::from_fraction(0.6).saturating_sub($name::from_fraction(0.6)),
$name::from_fraction(0.0),
$name::from_float(0.6).saturating_sub($name::from_float(0.6)),
$name::from_float(0.0),
);
assert_eq!(
$name::from_fraction(0.6).saturating_sub($name::from_fraction(0.7)),
$name::from_fraction(0.0),
$name::from_float(0.6).saturating_sub($name::from_float(0.7)),
$name::from_float(0.0),
);
assert_eq_error_rate!(
$name::from_fraction(0.5).saturating_mul($name::from_fraction(0.5)).0 as $upper_type,
$name::from_fraction(0.25).0 as $upper_type,
$name::from_float(0.5).saturating_mul($name::from_float(0.5)).0 as $upper_type,
$name::from_float(0.25).0 as $upper_type,
2,
);
assert_eq_error_rate!(
$name::from_fraction(0.2).saturating_mul($name::from_fraction(0.2)).0 as $upper_type,
$name::from_fraction(0.04).0 as $upper_type,
$name::from_float(0.2).saturating_mul($name::from_float(0.2)).0 as $upper_type,
$name::from_float(0.04).0 as $upper_type,
2,
);
assert_eq_error_rate!(
$name::from_fraction(0.1).saturating_mul($name::from_fraction(0.1)).0 as $upper_type,
$name::from_fraction(0.01).0 as $upper_type,
$name::from_float(0.1).saturating_mul($name::from_float(0.1)).0 as $upper_type,
$name::from_float(0.01).0 as $upper_type,
1,
);
}
#[test]
fn per_thing_square_works() {
assert_eq!($name::from_fraction(1.0).square(), $name::from_fraction(1.0));
assert_eq!($name::from_fraction(0.5).square(), $name::from_fraction(0.25));
assert_eq!($name::from_fraction(0.1).square(), $name::from_fraction(0.01));
assert_eq!($name::from_float(1.0).square(), $name::from_float(1.0));
assert_eq!($name::from_float(0.5).square(), $name::from_float(0.25));
assert_eq!($name::from_float(0.1).square(), $name::from_float(0.01));
assert_eq!(
$name::from_fraction(0.02).square(),
$name::from_float(0.02).square(),
$name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type)
);
}
@@ -1011,30 +1059,30 @@ macro_rules! implement_per_thing {
fn per_things_div_works() {
// normal
assert_eq_error_rate!(
($name::from_fraction(0.1) / $name::from_fraction(0.20)).0 as $upper_type,
$name::from_fraction(0.50).0 as $upper_type,
($name::from_float(0.1) / $name::from_float(0.20)).0 as $upper_type,
$name::from_float(0.50).0 as $upper_type,
2,
);
assert_eq_error_rate!(
($name::from_fraction(0.1) / $name::from_fraction(0.10)).0 as $upper_type,
$name::from_fraction(1.0).0 as $upper_type,
($name::from_float(0.1) / $name::from_float(0.10)).0 as $upper_type,
$name::from_float(1.0).0 as $upper_type,
2,
);
assert_eq_error_rate!(
($name::from_fraction(0.1) / $name::from_fraction(0.0)).0 as $upper_type,
$name::from_fraction(1.0).0 as $upper_type,
($name::from_float(0.1) / $name::from_float(0.0)).0 as $upper_type,
$name::from_float(1.0).0 as $upper_type,
2,
);
// will not overflow
assert_eq_error_rate!(
($name::from_fraction(0.10) / $name::from_fraction(0.05)).0 as $upper_type,
$name::from_fraction(1.0).0 as $upper_type,
($name::from_float(0.10) / $name::from_float(0.05)).0 as $upper_type,
$name::from_float(1.0).0 as $upper_type,
2,
);
assert_eq_error_rate!(
($name::from_fraction(1.0) / $name::from_fraction(0.5)).0 as $upper_type,
$name::from_fraction(1.0).0 as $upper_type,
($name::from_float(1.0) / $name::from_float(0.5)).0 as $upper_type,
$name::from_float(1.0).0 as $upper_type,
2,
);
}
@@ -95,15 +95,15 @@ mod tests {
Assignment {
who: 1u32,
distribution: vec![
(10u32, Perbill::from_fraction(0.5)),
(20, Perbill::from_fraction(0.5)),
(10u32, Perbill::from_float(0.5)),
(20, Perbill::from_float(0.5)),
],
},
Assignment {
who: 2u32,
distribution: vec![
(10, Perbill::from_fraction(0.33)),
(20, Perbill::from_fraction(0.67)),
(10, Perbill::from_float(0.33)),
(20, Perbill::from_float(0.67)),
],
},
];
@@ -371,7 +371,7 @@ impl<AccountId: IdentifierT> Voter<AccountId> {
.edges
.into_iter()
.filter_map(|e| {
let per_thing = P::from_rational_approximation(e.weight, budget);
let per_thing = P::from_rational(e.weight, budget);
// trim zero edges.
if per_thing.is_zero() { None } else { Some((e.who, per_thing)) }
}).collect::<Vec<_>>();
@@ -551,7 +551,7 @@ impl<AccountId> StakedAssignment<AccountId> {
let distribution = self.distribution
.into_iter()
.filter_map(|(target, w)| {
let per_thing = P::from_rational_approximation(w, stake);
let per_thing = P::from_rational(w, stake);
if per_thing == Bounded::min_value() {
None
} else {
@@ -345,7 +345,7 @@ pub(crate) fn run_and_compare<Output: PerThing128>(
for (candidate, per_thingy) in distribution {
if let Some(float_assignment) = float_assignments.1.iter().find(|x| x.0 == candidate ) {
assert_eq_error_rate!(
Output::from_fraction(float_assignment.1).deconstruct(),
Output::from_float(float_assignment.1).deconstruct(),
per_thingy.deconstruct(),
Output::Inner::one(),
);
@@ -101,7 +101,7 @@ pub(crate) fn calculate_max_score<AccountId: IdentifierT, P: PerThing>(
for edge in voter.edges.iter() {
let edge_candidate = edge.candidate.borrow();
if edge_candidate.elected {
let edge_contribution: ExtendedBalance = P::from_rational_approximation(
let edge_contribution: ExtendedBalance = P::from_rational(
edge.weight,
edge_candidate.backed_stake,
).deconstruct().into();
@@ -37,7 +37,6 @@ use crate::{
use sp_std::{rc::Rc, vec::Vec};
use sp_std::collections::btree_map::BTreeMap;
use sp_arithmetic::{traits::Zero, Perbill};
/// The type used as the threshold.
///
/// Just some reading sugar; Must always be same as [`ExtendedBalance`];
@@ -364,7 +363,7 @@ fn slack<AccountId: IdentifierT>(voter: &Voter<AccountId>, t: Threshold) -> Exte
let candidate = edge.candidate.borrow();
if candidate.elected {
let extra =
Perbill::one().min(Perbill::from_rational_approximation(t, candidate.backed_stake))
Perbill::one().min(Perbill::from_rational(t, candidate.backed_stake))
* edge.weight;
acc.saturating_add(extra)
} else {
@@ -1095,7 +1095,7 @@ mod score {
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(1u32, 10_000),
Perbill::from_rational(1u32, 10_000),
),
true,
);
@@ -1104,7 +1104,7 @@ mod score {
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(2u32, 10_000),
Perbill::from_rational(2u32, 10_000),
),
true,
);
@@ -1113,7 +1113,7 @@ mod score {
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(3u32, 10_000),
Perbill::from_rational(3u32, 10_000),
),
true,
);
@@ -1122,7 +1122,7 @@ mod score {
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(4u32, 10_000),
Perbill::from_rational(4u32, 10_000),
),
true,
);
@@ -1131,7 +1131,7 @@ mod score {
is_score_better(
claim.clone(),
initial.clone(),
Perbill::from_rational_approximation(5u32, 10_000),
Perbill::from_rational(5u32, 10_000),
),
false,
);