mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-29 05:37:58 +00:00
Pass max-total to RewardRemainder on end_era (#5697)
* Pass max-total to RewardRemainder on end_era * add test and event * add doc Co-authored-by: thiolliere <gui.thiolliere@gmail.com>
This commit is contained in:
@@ -21,10 +21,11 @@
|
||||
|
||||
use sp_runtime::{Perbill, traits::AtLeast32Bit, curve::PiecewiseLinear};
|
||||
|
||||
/// The total payout to all validators (and their nominators) per era.
|
||||
/// The total payout to all validators (and their nominators) per era and maximum payout.
|
||||
///
|
||||
/// Defined as such:
|
||||
/// `payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year`
|
||||
/// `staker-payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year`
|
||||
/// `maximum-payout = max_yearly_inflation * total_tokens / era_per_year`
|
||||
///
|
||||
/// `era_duration` is expressed in millisecond.
|
||||
pub fn compute_total_payout<N>(
|
||||
|
||||
@@ -172,6 +172,22 @@
|
||||
//!
|
||||
//! ## Implementation Details
|
||||
//!
|
||||
//! ### Era payout
|
||||
//!
|
||||
//! The era payout is computed using yearly inflation curve defined at
|
||||
//! [`T::RewardCurve`](./trait.Trait.html#associatedtype.RewardCurve) as such:
|
||||
//!
|
||||
//! ```nocompile
|
||||
//! staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year
|
||||
//! ```
|
||||
//! This payout is used to reward stakers as defined in next section
|
||||
//!
|
||||
//! ```nocompile
|
||||
//! remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout
|
||||
//! ```
|
||||
//! The remaining reward is send to the configurable end-point
|
||||
//! [`T::RewardRemainder`](./trait.Trait.html#associatedtype.RewardRemainder).
|
||||
//!
|
||||
//! ### Reward Calculation
|
||||
//!
|
||||
//! Validators and nominators are rewarded at the end of each era. The total reward of an era is
|
||||
@@ -744,6 +760,7 @@ pub trait Trait: frame_system::Trait {
|
||||
type CurrencyToVote: Convert<BalanceOf<Self>, VoteWeight> + Convert<u128, BalanceOf<Self>>;
|
||||
|
||||
/// Tokens have been minted and are unused for validator-reward.
|
||||
/// See [Era payout](./index.html#era-payout).
|
||||
type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
||||
|
||||
/// The overarching event type.
|
||||
@@ -772,7 +789,8 @@ pub trait Trait: frame_system::Trait {
|
||||
/// Interface for interacting with a session module.
|
||||
type SessionInterface: self::SessionInterface<Self::AccountId>;
|
||||
|
||||
/// The NPoS reward curve to use.
|
||||
/// The NPoS reward curve used to define yearly inflation.
|
||||
/// See [Era payout](./index.html#era-payout).
|
||||
type RewardCurve: Get<&'static PiecewiseLinear<'static>>;
|
||||
|
||||
/// Something that can estimate the next session change, accurately or as a best effort guess.
|
||||
@@ -1059,6 +1077,9 @@ decl_storage! {
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> where Balance = BalanceOf<T>, <T as frame_system::Trait>::AccountId {
|
||||
/// The era payout has been set; the first balance is the validator-payout; the second is
|
||||
/// the remainder from the maximum amount of reward.
|
||||
EraPayout(EraIndex, Balance, Balance),
|
||||
/// The staker has been rewarded by this amount. `AccountId` is the stash account.
|
||||
Reward(AccountId, Balance),
|
||||
/// One validator (and its nominators) has been slashed by the given amount.
|
||||
@@ -2570,16 +2591,20 @@ impl<T: Trait> Module<T> {
|
||||
let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
|
||||
|
||||
let era_duration = now_as_millis_u64 - active_era_start;
|
||||
let (total_payout, _max_payout) = inflation::compute_total_payout(
|
||||
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);
|
||||
|
||||
Self::deposit_event(RawEvent::EraPayout(active_era.index, validator_payout, rest));
|
||||
|
||||
// Set ending era reward.
|
||||
<ErasValidatorReward<T>>::insert(&active_era.index, total_payout);
|
||||
<ErasValidatorReward<T>>::insert(&active_era.index, validator_payout);
|
||||
T::RewardRemainder::on_unbalanced(T::Currency::issue(rest));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -277,11 +277,26 @@ parameter_types! {
|
||||
pub const UnsignedPriority: u64 = 1 << 20;
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
pub static REWARD_REMAINDER_UNBALANCED: RefCell<u128> = RefCell::new(0);
|
||||
}
|
||||
|
||||
pub struct RewardRemainderMock;
|
||||
|
||||
impl OnUnbalanced<NegativeImbalanceOf<Test>> for RewardRemainderMock {
|
||||
fn on_nonzero_unbalanced(amount: NegativeImbalanceOf<Test>) {
|
||||
REWARD_REMAINDER_UNBALANCED.with(|v| {
|
||||
*v.borrow_mut() += amount.peek();
|
||||
});
|
||||
drop(amount);
|
||||
}
|
||||
}
|
||||
|
||||
impl Trait for Test {
|
||||
type Currency = Balances;
|
||||
type UnixTime = Timestamp;
|
||||
type CurrencyToVote = CurrencyToVoteHandler;
|
||||
type RewardRemainder = ();
|
||||
type RewardRemainder = RewardRemainderMock;
|
||||
type Event = MetaEvent;
|
||||
type Slash = ();
|
||||
type Reward = ();
|
||||
@@ -976,3 +991,13 @@ macro_rules! assert_session_era {
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn staking_events() -> Vec<Event<Test>> {
|
||||
System::events().into_iter().map(|r| r.event).filter_map(|e| {
|
||||
if let MetaEvent::staking(inner) = e {
|
||||
Some(inner)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ fn rewards_should_work() {
|
||||
// should check that:
|
||||
// * rewards get recorded per session
|
||||
// * rewards get paid per Era
|
||||
// * `RewardRemainder::on_unbalanced` is called
|
||||
// * Check that nominators are also rewarded
|
||||
ExtBuilder::default().nominate(true).build_and_execute(|| {
|
||||
let init_balance_10 = Balances::total_balance(&10);
|
||||
@@ -197,6 +198,8 @@ fn rewards_should_work() {
|
||||
start_session(3);
|
||||
|
||||
assert_eq!(Staking::active_era().unwrap().index, 1);
|
||||
assert_eq!(mock::REWARD_REMAINDER_UNBALANCED.with(|v| *v.borrow()), 7050);
|
||||
assert_eq!(*mock::staking_events().last().unwrap(), RawEvent::EraPayout(0, 2350, 7050));
|
||||
mock::make_all_reward_payment(0);
|
||||
|
||||
assert_eq_error_rate!(Balances::total_balance(&10), init_balance_10 + part_for_10 * total_payout_0*2/3, 2);
|
||||
@@ -220,6 +223,8 @@ fn rewards_should_work() {
|
||||
assert!(total_payout_1 > 10); // Test is meaningful if reward something
|
||||
|
||||
mock::start_era(2);
|
||||
assert_eq!(mock::REWARD_REMAINDER_UNBALANCED.with(|v| *v.borrow()), 7050*2);
|
||||
assert_eq!(*mock::staking_events().last().unwrap(), RawEvent::EraPayout(1, 2350, 7050));
|
||||
mock::make_all_reward_payment(1);
|
||||
|
||||
assert_eq_error_rate!(Balances::total_balance(&10), init_balance_10 + part_for_10 * (total_payout_0 * 2/3 + total_payout_1), 2);
|
||||
|
||||
Reference in New Issue
Block a user