|
| 1 | +use super::*; |
| 2 | +use xaccounts::IntentionJackpotAccountIdFor; |
| 3 | + |
| 4 | +pub trait OnRewardCalculation<AccountId: Default, Balance> { |
| 5 | + fn psedu_intentions_info() -> Vec<(RewardHolder<AccountId>, Balance)>; |
| 6 | +} |
| 7 | + |
| 8 | +impl<AccountId: Default, Balance> OnRewardCalculation<AccountId, Balance> for () { |
| 9 | + fn psedu_intentions_info() -> Vec<(RewardHolder<AccountId>, Balance)> { |
| 10 | + Vec::new() |
| 11 | + } |
| 12 | +} |
| 13 | + |
| 14 | +pub trait OnReward<AccountId: Default, Balance> { |
| 15 | + fn reward(_: &Token, _: Balance); |
| 16 | +} |
| 17 | + |
| 18 | +impl<AccountId: Default, Balance> OnReward<AccountId, Balance> for () { |
| 19 | + fn reward(_: &Token, _: Balance) {} |
| 20 | +} |
| 21 | + |
| 22 | +impl<T: Trait> Module<T> { |
| 23 | + /// Get the reward for the session, assuming it ends with this block. |
| 24 | + fn this_session_reward() -> T::Balance { |
| 25 | + let current_index = <xsession::Module<T>>::current_index().as_(); |
| 26 | + let reward = Self::initial_reward().as_() |
| 27 | + / u64::from(u32::pow(2, (current_index / SESSIONS_PER_ROUND) as u32)); |
| 28 | + T::Balance::sa(reward as u64) |
| 29 | + } |
| 30 | + |
| 31 | + /// Reward a given (potential) validator by a specific amount. |
| 32 | + /// Add the reward to their balance, and their jackpot, pro-rata. |
| 33 | + fn reward(who: &T::AccountId, reward: T::Balance) { |
| 34 | + // Validator only gains 10%, the rest 90% goes to the jackpot. |
| 35 | + let off_the_table = T::Balance::sa(reward.as_() / 10); |
| 36 | + let _ = <xassets::Module<T>>::pcx_issue(who, off_the_table); |
| 37 | + |
| 38 | + let to_jackpot = reward - off_the_table; |
| 39 | + // issue to jackpot |
| 40 | + let jackpot_addr = T::DetermineIntentionJackpotAccountId::accountid_for_unsafe(who); |
| 41 | + let _ = <xassets::Module<T>>::pcx_issue(&jackpot_addr, to_jackpot); |
| 42 | + debug!( |
| 43 | + "[reward] issue to {:?}'s jackpot: {:?}", |
| 44 | + who!(who), |
| 45 | + to_jackpot |
| 46 | + ); |
| 47 | + } |
| 48 | + |
| 49 | + /// Collect the active intentions and psedu intentions. |
| 50 | + fn collect_reward_holders() -> Vec<(RewardHolder<T::AccountId>, T::Balance)> { |
| 51 | + let mut active_intentions = Self::intention_set() |
| 52 | + .into_iter() |
| 53 | + .filter(|i| Self::is_active(i)) |
| 54 | + .map(|id| { |
| 55 | + let total_nomination = Self::total_nomination_of(&id); |
| 56 | + (RewardHolder::Intention(id), total_nomination) |
| 57 | + }) |
| 58 | + .collect::<Vec<_>>(); |
| 59 | + |
| 60 | + // Extend non-intention reward holders, i.e., Tokens currently, who are always considered as active. |
| 61 | + let psedu_intentions = T::OnRewardCalculation::psedu_intentions_info(); |
| 62 | + active_intentions.extend(psedu_intentions); |
| 63 | + |
| 64 | + active_intentions |
| 65 | + } |
| 66 | + |
| 67 | + /// In the first round, 20% reward of each session goes to the team. |
| 68 | + fn try_fund_team(this_session_reward: T::Balance) -> T::Balance { |
| 69 | + let current_index = <xsession::Module<T>>::current_index().as_(); |
| 70 | + |
| 71 | + if current_index < SESSIONS_PER_ROUND { |
| 72 | + let to_team = T::Balance::sa(this_session_reward.as_() / 5); |
| 73 | + debug!("[reward] issue to the team: {:?}", to_team); |
| 74 | + let _ = |
| 75 | + <xassets::Module<T>>::pcx_issue(&xaccounts::Module::<T>::team_account(), to_team); |
| 76 | + this_session_reward - to_team |
| 77 | + } else { |
| 78 | + this_session_reward |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + /// Distribute the session reward for (psedu-)intentions. |
| 83 | + pub(super) fn distribute_session_reward(validators: &mut Vec<T::AccountId>) { |
| 84 | + // apply good session reward |
| 85 | + let this_session_reward = Self::this_session_reward(); |
| 86 | + |
| 87 | + let mut session_reward = Self::try_fund_team(this_session_reward); |
| 88 | + |
| 89 | + let active_intentions = Self::collect_reward_holders(); |
| 90 | + |
| 91 | + let mut total_active_stake = active_intentions |
| 92 | + .iter() |
| 93 | + .fold(Zero::zero(), |acc: T::Balance, (_, x)| acc + *x); |
| 94 | + |
| 95 | + Self::deposit_event(RawEvent::Reward(total_active_stake, this_session_reward)); |
| 96 | + |
| 97 | + for (holder, stake) in active_intentions.iter() { |
| 98 | + // May become zero after meeting the last one. |
| 99 | + if !total_active_stake.is_zero() { |
| 100 | + // stake * session_reward could overflow. |
| 101 | + let reward = match (u128::from(stake.as_())) |
| 102 | + .checked_mul(u128::from(session_reward.as_())) |
| 103 | + { |
| 104 | + Some(x) => { |
| 105 | + let r = x / u128::from(total_active_stake.as_()); |
| 106 | + if r < u128::from(u64::max_value()) { |
| 107 | + T::Balance::sa(r as u64) |
| 108 | + } else { |
| 109 | + panic!("reward of per intention definitely less than u64::max_value()") |
| 110 | + } |
| 111 | + } |
| 112 | + None => panic!("stake * session_reward overflow!"), |
| 113 | + }; |
| 114 | + match holder { |
| 115 | + RewardHolder::Intention(ref intention) => { |
| 116 | + Self::reward(intention, reward); |
| 117 | + |
| 118 | + // It the intention was an offline validator, we should enforce a slash. |
| 119 | + if <MissedOfPerSession<T>>::exists(intention) { |
| 120 | + // FIXME Don't pass validators in slash_active_offline_validator() |
| 121 | + Self::slash_active_offline_validator(intention, reward, validators); |
| 122 | + } |
| 123 | + } |
| 124 | + RewardHolder::PseduIntention(ref token) => { |
| 125 | + // Reward to token entity. |
| 126 | + T::OnReward::reward(token, reward) |
| 127 | + } |
| 128 | + } |
| 129 | + total_active_stake -= *stake; |
| 130 | + session_reward -= reward; |
| 131 | + } |
| 132 | + } |
| 133 | + } |
| 134 | +} |
0 commit comments