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

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

Merged
26 commits merged into from
Mar 16, 2021
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ rls*.log
.cargo/
.cargo-remote.toml
*.bin
*.iml
shawntabrizi marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion bin/node/runtime/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() -> () {
Expand Down
9 changes: 5 additions & 4 deletions bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 = ();
Expand Down
2 changes: 1 addition & 1 deletion frame/babe/src/equivocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
2 changes: 1 addition & 1 deletion frame/babe/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
)),
Expand Down
2 changes: 1 addition & 1 deletion frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,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;
Expand Down
2 changes: 1 addition & 1 deletion frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
60 changes: 48 additions & 12 deletions frame/gilt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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>;
Comment on lines +101 to +105
Copy link
Contributor

@kianenigma kianenigma Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only alternative that I can think of is to add a where <T::Currency as _>::Balance: From<u64> to most functions of this pallet, and while this is not pretty either, I guess it has less footprint than sprinkling wheres.


/// Origin required for setting the target proportion to be under gilt.
type AdminOrigin: EnsureOrigin<Self::Origin>;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 nongilt_issuance = total_issuance.saturating_sub(totals.frozen);
let effective_issuance = totals.proportion.left_from_one()
.saturating_reciprocal_mul(nongilt_issuance);

IssuanceInfo {
reserved: totals.frozen,
non_gilt: nongilt_issuance,
effective: effective_issuance,
}
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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 {
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions frame/gilt/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ ord_parameter_types! {
impl pallet_gilt::Config for Test {
type Event = Event;
type Currency = Balances;
type CurrencyBalance = u64;
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
type AdminOrigin = frame_system::EnsureSignedBy<One, Self::AccountId>;
type Deficit = ();
type Surplus = ();
Expand Down
2 changes: 1 addition & 1 deletion frame/grandpa/src/equivocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
2 changes: 1 addition & 1 deletion frame/grandpa/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,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;
Expand Down
8 changes: 2 additions & 6 deletions frame/im-online/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion frame/offences/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,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;
Expand Down
2 changes: 1 addition & 1 deletion frame/session/benchmarking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,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;
Expand Down
4 changes: 2 additions & 2 deletions frame/session/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
))
Expand Down
2 changes: 1 addition & 1 deletion frame/staking/fuzzer/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,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;
Expand Down
2 changes: 1 addition & 1 deletion frame/staking/src/inflation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
53 changes: 39 additions & 14 deletions frame/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,36 @@ impl<T: Config> SessionInterface<<T as frame_system::Config>::AccountId> for T w
}
}

pub trait EraPayout<Balance> {
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
gavofyork marked this conversation as resolved.
Show resolved Hide resolved
fn era_payout(
total_staked: Balance,
total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance);
}

pub struct ConvertCurve<T>(sp_std::marker::PhantomData<T>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably could use a description

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>;
Expand Down Expand Up @@ -838,9 +868,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>;
Expand Down Expand Up @@ -2413,7 +2443,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,
);
Expand All @@ -2428,7 +2458,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,
);
Expand All @@ -2445,7 +2475,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,
);
Expand Down Expand Up @@ -2837,15 +2867,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));

Expand Down
Loading