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

Update polkadot inflation to take into account auctions #5872

Merged
merged 12 commits into from
Nov 8, 2022
Merged
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions runtime/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "m
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-staking-reward-fn = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-vesting = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
Expand Down Expand Up @@ -84,6 +85,7 @@ std = [
"pallet-beefy-mmr/std",
"pallet-session/std",
"pallet-staking/std",
"pallet-staking-reward-fn/std",
"pallet-timestamp/std",
"pallet-vesting/std",
"pallet-transaction-payment/std",
Expand Down
81 changes: 81 additions & 0 deletions runtime/common/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

use crate::NegativeImbalance;
use frame_support::traits::{Currency, Imbalance, OnUnbalanced};
use primitives::v2::Balance;
use sp_runtime::Perquintill;

/// Logic for the author to get a portion of fees.
pub struct ToAuthor<R>(sp_std::marker::PhantomData<R>);
Expand Down Expand Up @@ -57,6 +59,45 @@ where
}
}

pub fn era_payout(
total_staked: Balance,
non_gilt_issuance: Balance,
max_annual_inflation: Perquintill,
period_fraction: Perquintill,
auctioned_slots: u64,
) -> (Balance, Balance) {
use pallet_staking_reward_fn::compute_inflation;
use sp_runtime::traits::Saturating;

let min_annual_inflation = Perquintill::from_rational(25u64, 1000u64);
let delta_annual_inflation = max_annual_inflation.saturating_sub(min_annual_inflation);

// 30% reserved for up to 60 slots.
let auction_proportion = Perquintill::from_rational(auctioned_slots.min(60), 200u64);
Comment on lines +75 to +76
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this hardcoded amount problematic?

Copy link
Member

Choose a reason for hiding this comment

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

No - it just sets an effective upper limit to how much of the inflation can be accounted for by the slot auctions.


// Therefore the ideal amount at stake (as a percentage of total issuance) is 75% less the
// amount that we expect to be taken up with auctions.
let ideal_stake = Perquintill::from_percent(75).saturating_sub(auction_proportion);

let stake = Perquintill::from_rational(total_staked, non_gilt_issuance);
let falloff = Perquintill::from_percent(5);
let adjustment = compute_inflation(stake, ideal_stake, falloff);
let staking_inflation =
min_annual_inflation.saturating_add(delta_annual_inflation * adjustment);

let max_payout = period_fraction * max_annual_inflation * non_gilt_issuance;
let staking_payout = (period_fraction * staking_inflation) * non_gilt_issuance;
let rest = max_payout.saturating_sub(staking_payout);

let other_issuance = non_gilt_issuance.saturating_sub(total_staked);
if total_staked > other_issuance {
let _cap_rest = Perquintill::from_rational(other_issuance, total_staked) * staking_payout;
// We don't do anything with this, but if we wanted to, we could introduce a cap on the
// treasury amount with: `rest = rest.min(cap_rest);`
}
(staking_payout, rest)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -209,4 +250,44 @@ mod tests {
assert_eq!(Balances::free_balance(Treasury::account_id()), 8);
});
}

#[test]
fn compute_inflation_should_give_sensible_results() {
assert_eq!(
pallet_staking_reward_fn::compute_inflation(
Perquintill::from_percent(75),
Perquintill::from_percent(75),
Perquintill::from_percent(5),
),
Perquintill::one()
);
assert_eq!(
pallet_staking_reward_fn::compute_inflation(
Perquintill::from_percent(50),
Perquintill::from_percent(75),
Perquintill::from_percent(5),
),
Perquintill::from_rational(2u64, 3u64)
);
assert_eq!(
pallet_staking_reward_fn::compute_inflation(
Perquintill::from_percent(80),
Perquintill::from_percent(75),
Perquintill::from_percent(5),
),
Perquintill::from_rational(1u64, 2u64)
);
}

#[test]
fn era_payout_should_give_sensible_results() {
assert_eq!(
era_payout(75, 100, Perquintill::from_percent(10), Perquintill::one(), 0,),
(10, 0)
);
assert_eq!(
era_payout(80, 100, Perquintill::from_percent(10), Perquintill::one(), 0,),
(6, 4)
);
}
}
2 changes: 0 additions & 2 deletions runtime/kusama/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ pallet-session = { git = "https://github.com/paritytech/substrate", branch = "ma
pallet-society = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-staking-reward-fn = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system = {git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
frame-system-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
Expand Down Expand Up @@ -170,7 +169,6 @@ std = [
"pallet-session/std",
"pallet-society/std",
"pallet-staking/std",
"pallet-staking-reward-fn/std",
"pallet-timestamp/std",
"pallet-tips/std",
"pallet-treasury/std",
Expand Down
53 changes: 10 additions & 43 deletions runtime/kusama/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use primitives::v2::{
CoreState, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage,
Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes,
SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
LOWEST_PUBLIC_ID,
};
use runtime_common::{
auctions, claims, crowdloan, impl_runtime_weights, impls::DealWithFees, paras_registrar,
Expand Down Expand Up @@ -504,59 +505,25 @@ impl pallet_bags_list::Config<VoterBagsListInstance> for Runtime {
type Score = sp_npos_elections::VoteWeight;
}

fn era_payout(
total_staked: Balance,
non_gilt_issuance: Balance,
max_annual_inflation: Perquintill,
period_fraction: Perquintill,
auctioned_slots: u64,
) -> (Balance, Balance) {
use pallet_staking_reward_fn::compute_inflation;
use sp_arithmetic::traits::Saturating;

let min_annual_inflation = Perquintill::from_rational(25u64, 1000u64);
let delta_annual_inflation = max_annual_inflation.saturating_sub(min_annual_inflation);

// 30% reserved for up to 60 slots.
let auction_proportion = Perquintill::from_rational(auctioned_slots.min(60), 200u64);

// Therefore the ideal amount at stake (as a percentage of total issuance) is 75% less the amount that we expect
// to be taken up with auctions.
let ideal_stake = Perquintill::from_percent(75).saturating_sub(auction_proportion);

let stake = Perquintill::from_rational(total_staked, non_gilt_issuance);
let falloff = Perquintill::from_percent(5);
let adjustment = compute_inflation(stake, ideal_stake, falloff);
let staking_inflation =
min_annual_inflation.saturating_add(delta_annual_inflation * adjustment);

let max_payout = period_fraction * max_annual_inflation * non_gilt_issuance;
let staking_payout = (period_fraction * staking_inflation) * non_gilt_issuance;
let rest = max_payout.saturating_sub(staking_payout);

let other_issuance = non_gilt_issuance.saturating_sub(total_staked);
if total_staked > other_issuance {
let _cap_rest = Perquintill::from_rational(other_issuance, total_staked) * staking_payout;
// We don't do anything with this, but if we wanted to, we could introduce a cap on the treasury amount
// with: `rest = rest.min(cap_rest);`
}
(staking_payout, rest)
}

pub struct EraPayout;
impl pallet_staking::EraPayout<Balance> for EraPayout {
fn era_payout(
total_staked: Balance,
_total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance) {
// TODO: #3011 Update with proper auctioned slots tracking.
// This should be fine for the first year of parachains.
let auctioned_slots: u64 = auctions::Pallet::<Runtime>::auction_counter().into();
// all para-ids that are currently active.
let auctioned_slots = Paras::parachains()
.into_iter()
// all active para-ids that do not belong to a system or common good chain is the number
// of parachains that we should take into account for inflation.
Copy link
Member

Choose a reason for hiding this comment

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

is this true? what about parathreads which did not secure an auction slot?

Copy link
Member

Choose a reason for hiding this comment

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

the ::parachains() api only returns the list of paras which have upgraded to a parachain. It does not include parathreads.

.filter(|i| *i >= LOWEST_PUBLIC_ID)
.count() as u64;

const MAX_ANNUAL_INFLATION: Perquintill = Perquintill::from_percent(10);
const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100;

era_payout(
runtime_common::impls::era_payout(
total_staked,
Gilt::issuance().non_gilt,
MAX_ANNUAL_INFLATION,
Expand Down
34 changes: 0 additions & 34 deletions runtime/kusama/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,40 +137,6 @@ fn nominator_limit() {
println!("can support {} nominators to yield a weight of {}", active, weight_with(active));
}

#[test]
fn compute_inflation_should_give_sensible_results() {
assert_eq!(
pallet_staking_reward_fn::compute_inflation(
Perquintill::from_percent(75),
Perquintill::from_percent(75),
Perquintill::from_percent(5),
),
Perquintill::one()
);
assert_eq!(
pallet_staking_reward_fn::compute_inflation(
Perquintill::from_percent(50),
Perquintill::from_percent(75),
Perquintill::from_percent(5),
),
Perquintill::from_rational(2u64, 3u64)
);
assert_eq!(
pallet_staking_reward_fn::compute_inflation(
Perquintill::from_percent(80),
Perquintill::from_percent(75),
Perquintill::from_percent(5),
),
Perquintill::from_rational(1u64, 2u64)
);
}

#[test]
fn era_payout_should_give_sensible_results() {
assert_eq!(era_payout(75, 100, Perquintill::from_percent(10), Perquintill::one(), 0,), (10, 0));
assert_eq!(era_payout(80, 100, Perquintill::from_percent(10), Perquintill::one(), 0,), (6, 4));
}

#[test]
fn call_size() {
RuntimeCall::assert_size_under(230);
Expand Down
32 changes: 31 additions & 1 deletion runtime/polkadot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ use primitives::v2::{
CoreState, GroupRotationInfo, Hash, Id as ParaId, InboundDownwardMessage, InboundHrmpMessage,
Moment, Nonce, OccupiedCoreAssumption, PersistedValidationData, ScrapedOnChainVotes,
SessionInfo, Signature, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
LOWEST_PUBLIC_ID,
};
use sp_core::OpaqueMetadata;
use sp_mmr_primitives as mmr;
Expand Down Expand Up @@ -536,6 +537,35 @@ type SlashCancelOrigin = EitherOfDiverse<
pallet_collective::EnsureProportionAtLeast<AccountId, CouncilCollective, 3, 4>,
>;

pub struct EraPayout;
impl pallet_staking::EraPayout<Balance> for EraPayout {
fn era_payout(
total_staked: Balance,
_total_issuance: Balance,
era_duration_millis: u64,
) -> (Balance, Balance) {
// all para-ids that are not active.
let auctioned_slots = Paras::parachains()
.into_iter()
// all active para-ids that do not belong to a system or common good chain is the number
kianenigma marked this conversation as resolved.
Show resolved Hide resolved
// of parachains that we should take into account for inflation.
.filter(|i| *i >= LOWEST_PUBLIC_ID)
.count() as u64;

const MAX_ANNUAL_INFLATION: Perquintill = Perquintill::from_percent(10);
const MILLISECONDS_PER_YEAR: u64 = 1000 * 3600 * 24 * 36525 / 100;

runtime_common::impls::era_payout(
total_staked,
// Polkadot has no notion of gilts, the entire issuance is non-guilt.
Balances::total_issuance(),
kianenigma marked this conversation as resolved.
Show resolved Hide resolved
MAX_ANNUAL_INFLATION,
Perquintill::from_rational(era_duration_millis, MILLISECONDS_PER_YEAR),
auctioned_slots,
)
}
}

impl pallet_staking::Config for Runtime {
type MaxNominations = MaxNominations;
type Currency = Balances;
Expand All @@ -552,7 +582,7 @@ impl pallet_staking::Config for Runtime {
// A super-majority of the council can cancel the slash.
type SlashCancelOrigin = SlashCancelOrigin;
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type EraPayout = EraPayout;
type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
Expand Down