Skip to content

Commit

Permalink
feat: modification of reward and punishment (#271)
Browse files Browse the repository at this point in the history
  • Loading branch information
ytqaljn authored Dec 11, 2023
1 parent 7d526a9 commit 3468de7
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 67 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 2 additions & 3 deletions pallets/audit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -791,13 +791,12 @@ pub mod pallet {

let _ = T::MinerControl::clear_punish(
&miner,
count,
challenge_info.miner_snapshot.idle_space,
challenge_info.miner_snapshot.service_space,
);
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 1));
//For Testing
if count >= 6 {

if count >= 3 {
let result = T::MinerControl::force_miner_exit(&miner);
weight = weight.saturating_add(T::DbWeight::get().reads_writes(5, 5));
if result.is_err() {
Expand Down
1 change: 1 addition & 0 deletions pallets/cess-treasury/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ readme = "README.md"
[dependencies]
scale-info = { workspace = true, features = ["derive"] }
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
log = { workspace = true }

#substrate pallet
frame-support = { workspace = true }
Expand Down
3 changes: 3 additions & 0 deletions pallets/cess-treasury/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use super::*;

pub(super) const REWARD_BASE_MUTI: Perbill = Perbill::from_percent(75);
100 changes: 94 additions & 6 deletions pallets/cess-treasury/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ use frame_support::{
};
// use sp_std::prelude::*;
use sp_runtime::{
SaturatedConversion,
traits::{CheckedAdd, CheckedSub, AccountIdConversion},
SaturatedConversion, Perbill,
traits::{CheckedAdd, CheckedSub, AccountIdConversion, Zero},
};
use frame_system::{
pallet_prelude::OriginFor,
ensure_signed, ensure_root,
};

mod constants;
use constants::*;

pub use pallet::*;

type BalanceOf<T> =
Expand Down Expand Up @@ -53,6 +56,8 @@ pub mod pallet {

type SpaceTreasuryId: Get<PalletId>;

type ReserveRewardId: Get<PalletId>;

type BurnDestination: OnUnbalanced<NegativeImbalanceOf<Self>>;
}

Expand All @@ -73,6 +78,14 @@ pub mod pallet {
#[pallet::getter(fn currency_reward)]
pub(super) type CurrencyReward<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn era_reward)]
pub(super) type EraReward<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn reserve_reward)]
pub(super) type ReserveReward<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(0)]
Expand Down Expand Up @@ -184,13 +197,47 @@ pub mod pallet {
}

impl<T: Config> Pallet<T> {
pub fn get_reward() -> BalanceOf<T> {
<CurrencyReward<T>>::get()
}

pub fn get_reward_base() -> BalanceOf<T> {
let era_reward = <EraReward<T>>::get();
let base_reward = REWARD_BASE_MUTI.mul_floor(era_reward);

base_reward
}

pub fn add_reward(amount: BalanceOf<T>) -> DispatchResult {
<CurrencyReward<T>>::mutate(|v| -> DispatchResult {
*v = v.checked_add(&amount).ok_or(Error::<T>::Overflow)?;
Ok(())
})?;

Ok(())
}

pub fn add_miner_reward_pool(amount: BalanceOf<T>) -> DispatchResult {
<EraReward<T>>::put(amount);
let base_reward = REWARD_BASE_MUTI.mul_floor(amount);
let mut reserve_output: BalanceOf<T> = BalanceOf::<T>::zero();

<CurrencyReward<T>>::mutate(|v| -> DispatchResult {
if *v > base_reward {
reserve_output = v.checked_sub(&base_reward).ok_or(Error::<T>::Overflow)?;
*v = v.checked_sub(&reserve_output).ok_or(Error::<T>::Overflow)?;
}
// The total issuance amount will not exceed u128::Max, so there is no overflow risk
*v = v.checked_add(&amount).ok_or(Error::<T>::Overflow)?;

Ok(())
})
})?;

if reserve_output != BalanceOf::<T>::zero() {
Self::reward_reserve(reserve_output)?;
}

Ok(())
}

pub fn send_to_pid(acc: AccountOf<T>, amount: BalanceOf<T>) -> DispatchResult {
Expand All @@ -202,9 +249,40 @@ impl<T: Config> Pallet<T> {
let sid = T::SpaceTreasuryId::get().into_account_truncating();
<T as pallet::Config>::Currency::transfer(&acc, &sid, amount, KeepAlive)
}

pub fn send_to_rid(acc: AccountOf<T>, amount: BalanceOf<T>) -> DispatchResult {
let rid = T::ReserveRewardId::get().into_account_truncating();
<ReserveReward<T>>::mutate(|v| -> DispatchResult {
*v = v.checked_add(&amount).ok_or(Error::<T>::Overflow)?;
Ok(())
})?;
<T as pallet::Config>::Currency::transfer(&acc, &rid, amount, KeepAlive)
}

pub fn reward_reserve(amount: BalanceOf<T>) -> DispatchResult {
let mrid = T::MinerRewardId::get().into_account_truncating();
Self::send_to_rid(mrid, amount)
}

pub fn sluice(amount: BalanceOf<T>) -> DispatchResult {
<ReserveReward<T>>::mutate(|v| -> DispatchResult {
*v = v.checked_sub(&amount).ok_or(Error::<T>::Overflow)?;
Ok(())
})?;

<CurrencyReward<T>>::mutate(|v| -> DispatchResult {
*v = v.checked_add(&amount).ok_or(Error::<T>::Overflow)?;
Ok(())
})?;

let rid = T::ReserveRewardId::get().into_account_truncating();
let mrid = T::MinerRewardId::get().into_account_truncating();
<T as pallet::Config>::Currency::transfer(&rid, &mrid, amount, KeepAlive)
}
}

pub trait RewardPool<AccountId, Balance> {
fn get_reward_base() -> Balance;
fn get_reward() -> Balance;
fn get_reward_128() -> u128;
fn add_reward(amount: Balance) -> DispatchResult;
Expand All @@ -213,25 +291,35 @@ pub trait RewardPool<AccountId, Balance> {
}

impl<T: Config> RewardPool<AccountOf<T>, BalanceOf<T>> for Pallet<T> {
fn get_reward_base() -> BalanceOf<T> {
Self::get_reward_base()
}

fn get_reward() -> BalanceOf<T> {
<CurrencyReward<T>>::get()
Self::get_reward()
}

fn get_reward_128() -> u128 {
<CurrencyReward<T>>::get().saturated_into()
}

fn add_reward(amount: BalanceOf<T>) -> DispatchResult {
Self::add_miner_reward_pool(amount)?;
Self::add_reward(amount)?;

Ok(())
}

fn sub_reward(amount: BalanceOf<T>) -> DispatchResult {
let reward = Self::get_reward();
if amount > reward {
if let Err(e) = Self::sluice(amount) {
log::info!("Insufficient reservoir reserves");
return Err(e);
}
}
<CurrencyReward<T>>::mutate(|v| -> DispatchResult {
// The total issuance amount will not exceed u128::Max, so there is no overflow risk
*v = v.checked_sub(&amount).ok_or(Error::<T>::Overflow)?;

Ok(())
})?;

Expand Down
Empty file.
6 changes: 1 addition & 5 deletions pallets/sminer/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@ pub(super) const IDLE_MUTI: Perbill = Perbill::from_percent(30);

pub(super) const SERVICE_MUTI: Perbill = Perbill::from_percent(70);

pub(super) const ISSUE_MUTI: Perbill = Perbill::from_percent(20);

pub(super) const EACH_SHARE_MUTI: Perbill = Perbill::from_percent(80);

pub(super) const RELEASE_NUMBER: u8 = 180;

pub(super) const IDLE_PUNI_MUTI: Perbill = Perbill::from_percent(10);

pub(super) const SERVICE_PUNI_MUTI: Perbill = Perbill::from_percent(25);
pub(super) const SERVICE_PUNI_MUTI: Perbill = Perbill::from_percent(5);

pub(super) const BASE_UNIT: u128 = 4_000_000_000_000_000;
21 changes: 10 additions & 11 deletions pallets/sminer/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,23 +78,22 @@ impl<T: Config> Pallet<T> {
// Note: that it is necessary to determine whether the state meets the exit conditions before use.
pub(super) fn execute_exit(acc: &AccountOf<T>) -> DispatchResult {
// T::Currency::unreserve(acc, miner.collaterals);
if let Ok(reward_info) = <RewardMap<T>>::try_get(acc).map_err(|_| Error::<T>::NotExisted) {
let reward = reward_info.total_reward
.checked_sub(&reward_info.reward_issued).ok_or(Error::<T>::Overflow)?;
T::RewardPool::add_reward(reward)?;
}
<MinerItems<T>>::try_mutate(acc, |miner_opt| -> DispatchResult {
let miner_info = miner_opt.as_mut().ok_or(Error::<T>::NotMiner)?;
miner_info.state = Self::str_to_bound(STATE_EXIT)?;
if let Ok(reward_info) = <RewardMap<T>>::try_get(acc).map_err(|_| Error::<T>::NotExisted) {
T::RewardPool::send_reward_to_miner(miner_info.beneficiary.clone(), reward_info.total_reward)?;
}
Ok(())
})?;

let mut miner_list = AllMiner::<T>::get();
miner_list.retain(|s| s != acc);
AllMiner::<T>::put(miner_list);

<RewardMap<T>>::remove(acc);
<MinerItems<T>>::try_mutate(acc, |miner_opt| -> DispatchResult {
let miner_info = miner_opt.as_mut().ok_or(Error::<T>::NotMiner)?;
miner_info.state = Self::str_to_bound(STATE_EXIT)?;

Ok(())
})

Ok(())
}

pub(super) fn create_restoral_target(miner: &AccountOf<T>, service_space: u128) -> DispatchResult {
Expand Down
53 changes: 18 additions & 35 deletions pallets/sminer/src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,65 +163,49 @@ impl<T: Config> Pallet<T> {
miner_idle_space: u128,
miner_service_space: u128,
) -> DispatchResult {
let total_reward = T::RewardPool::get_reward();
let total_reward = T::RewardPool::get_reward_base();
let total_power = Self::calculate_power(total_idle_space, total_service_space);
let miner_power = Self::calculate_power(miner_idle_space, miner_service_space);

let miner_prop = Perbill::from_rational(miner_power, total_power);
let this_round_reward = miner_prop.mul_floor(total_reward);
let each_share = EACH_SHARE_MUTI.mul_floor(this_round_reward);
let each_share = each_share.checked_div(&RELEASE_NUMBER.into()).ok_or(Error::<T>::Overflow)?;
let issued: BalanceOf<T> = ISSUE_MUTI.mul_floor(this_round_reward).try_into().map_err(|_| Error::<T>::Overflow)?;

let order = RewardOrder::<BalanceOf<T>>{
order_reward: this_round_reward.try_into().map_err(|_| Error::<T>::Overflow)?,
each_share: each_share.try_into().map_err(|_| Error::<T>::Overflow)?,
award_count: 1,
has_issued: true,
};
// calculate available reward
RewardMap::<T>::try_mutate(miner, |opt_reward_info| -> DispatchResult {
let reward_info = opt_reward_info.as_mut().ok_or(Error::<T>::Unexpected)?;
// traverse the order list
for order_temp in reward_info.order_list.iter_mut() {
// skip if the order has been issued for 180 times
if order_temp.award_count == RELEASE_NUMBER {
continue;
}
reward_info.currently_available_reward = reward_info.currently_available_reward
.checked_add(&order_temp.each_share).ok_or(Error::<T>::Overflow)?;

order_temp.award_count += 1;
}
reward_info.currently_available_reward = reward_info.currently_available_reward
.checked_add(&this_round_reward).ok_or(Error::<T>::Overflow)?;

if reward_info.order_list.len() == RELEASE_NUMBER as usize {
reward_info.order_list.remove(0);
}

reward_info.currently_available_reward = reward_info.currently_available_reward
.checked_add(&issued).ok_or(Error::<T>::Overflow)?
.checked_add(&order.each_share).ok_or(Error::<T>::Overflow)?;
reward_info.total_reward = reward_info.total_reward
.checked_add(&order.order_reward).ok_or(Error::<T>::Overflow)?;
.checked_add(&this_round_reward).ok_or(Error::<T>::Overflow)?;
reward_info.order_list.try_push(order.clone()).map_err(|_| Error::<T>::BoundedVecError)?;

Ok(())
})?;

T::RewardPool::sub_reward(order.order_reward)?;

Ok(())
}

pub(super) fn clear_punish(miner: &AccountOf<T>, level: u8, idle_space: u128, service_space: u128) -> DispatchResult {
pub(super) fn clear_punish(miner: &AccountOf<T>, idle_space: u128, service_space: u128) -> DispatchResult {
let power = Self::calculate_power(idle_space, service_space);
let limit: BalanceOf<T> = Self::calculate_limit_by_space(power)?
.try_into().map_err(|_| Error::<T>::Overflow)?;
let miner_reward = <RewardMap<T>>::try_get(&miner).map_err(|_| Error::<T>::NotMiner)?;

// FOR TESTING
let punish_amount = match level {
1 => Perbill::from_percent(30).mul_floor(limit),
2 => Perbill::from_percent(50).mul_floor(limit),
3 | 4 | 5 | 6 => limit,
_ => return Err(Error::<T>::Unexpected)?,
let reward: u128 = miner_reward.total_reward.try_into().map_err(|_| Error::<T>::Overflow)?;
let punish_amount = match reward {
0 => 100u128.try_into().map_err(|_| Error::<T>::Overflow)?,
_ => Perbill::from_percent(5).mul_floor(limit),
};

Self::deposit_punish(miner, punish_amount)?;
Expand Down Expand Up @@ -254,19 +238,16 @@ impl<T: Config> Pallet<T> {
}
// Note: that it is necessary to determine whether the state meets the exit conditions before use.
pub(super) fn force_miner_exit(acc: &AccountOf<T>) -> DispatchResult {
if let Ok(reward_info) = <RewardMap<T>>::try_get(acc).map_err(|_| Error::<T>::NotExisted) {
let reward = reward_info.total_reward
.checked_sub(&reward_info.reward_issued).ok_or(Error::<T>::Overflow)?;
T::RewardPool::add_reward(reward)?;
}

let mut miner_list = AllMiner::<T>::get();
miner_list.retain(|s| s != acc);
AllMiner::<T>::put(miner_list);

<RewardMap<T>>::remove(acc);

<MinerItems<T>>::try_mutate(acc, |miner_opt| -> DispatchResult {
let miner = miner_opt.as_mut().ok_or(Error::<T>::Unexpected)?;
if let Ok(reward_info) = <RewardMap<T>>::try_get(acc).map_err(|_| Error::<T>::NotExisted) {
T::RewardPool::send_reward_to_miner(miner.beneficiary.clone(), reward_info.total_reward)?;
}
T::StorageHandle::sub_total_idle_space(miner.idle_space + miner.lock_space)?;
T::Currency::unreserve(&miner.staking_account, miner.collaterals);
Self::create_restoral_target(acc, miner.service_space)?;
Expand All @@ -278,6 +259,8 @@ impl<T: Config> Pallet<T> {
Ok(())
})?;

<RewardMap<T>>::remove(acc);

Ok(())
}

Expand Down
Loading

0 comments on commit 3468de7

Please sign in to comment.