Skip to content

Commit

Permalink
Cache the treasury account when applying the slash. (paritytech#151)
Browse files Browse the repository at this point in the history
  • Loading branch information
liuchengxu authored Jul 22, 2020
1 parent d40999d commit af6c435
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 94 deletions.
4 changes: 2 additions & 2 deletions cli/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ fn testnet_genesis(
minimum_validator_count: 4,
sessions_per_era: 12,
vesting_account: get_account_id_from_seed::<sr25519::Public>("vesting"),
glob_dist_ratio: (12, 88),
mining_ratio: (10, 90),
glob_dist_ratio: (12, 88), // (Treasury, X-type Asset and Staking) = (12, 88)
mining_ratio: (10, 90), // (Asset Mining, Staking) = (10, 90)
..Default::default()
}),
xpallet_dex_spot: Some(XSpotConfig {
Expand Down
2 changes: 1 addition & 1 deletion xpallets/mining/asset/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Error, Trait};
use crate::Trait;
use chainx_primitives::AssetId;
use codec::{Decode, Encode};
use sp_runtime::RuntimeDebug;
Expand Down
3 changes: 1 addition & 2 deletions xpallets/mining/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ use xp_mining_staking::{AssetMining, SessionIndex, TreasuryAccount, UnbondedInde
use xpallet_assets::{AssetErr, AssetType};
use xpallet_support::debug;

use types::*;

pub use impls::{IdentificationTuple, SimpleValidatorRewardPotAccountDeterminer};
pub use types::*;

/// Session reward of the first 210_000 sessions.
const INITIAL_REWARD: u64 = 5_000_000_000;
Expand Down
37 changes: 7 additions & 30 deletions xpallets/mining/staking/src/slashing.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
use super::*;

impl<T: Trait> Module<T> {
/// Actually slash the account being punished, all slashed balance will go to the treasury.
fn apply_slash(reward_pot: &T::AccountId, value: T::Balance) {
// FIXME: cache the treasury_account?
let treasury_account = T::TreasuryAccount::treasury_account();
let _ = <xpallet_assets::Module<T>>::pcx_move_free_balance(
reward_pot,
&treasury_account,
value,
);
}

/// Average reward for validator per block.
fn reward_per_block(staking_reward: T::Balance, validator_count: usize) -> u128 {
let session_length = T::SessionDuration::get();
Expand All @@ -21,21 +10,6 @@ impl<T: Trait> Module<T> {
per_reward
}

/// Returns Ok(_) if the reward pot of offender has enough balance to cover the slashing,
/// otherwise slash the reward pot as much as possible and returns the value actually slashed.
fn try_slash(offender: &T::AccountId, expected_slash: T::Balance) -> Result<(), T::Balance> {
let reward_pot = Self::reward_pot_for(offender);
let reward_pot_balance = <xpallet_assets::Module<T>>::pcx_free_balance(&reward_pot);

if expected_slash <= reward_pot_balance {
Self::apply_slash(&reward_pot, expected_slash);
Ok(())
} else {
Self::apply_slash(&reward_pot, reward_pot_balance);
Err(reward_pot_balance)
}
}

/// TODO: flexiable slash according to slash fraction?
fn expected_slash_of(reward_per_block: u128) -> T::Balance {
let ideal_slash = reward_per_block * u128::from(Self::offence_severity());
Expand All @@ -49,11 +23,14 @@ impl<T: Trait> Module<T> {
let validators = T::SessionInterface::validators();
let valid_offenders = Self::offenders_in_session()
.into_iter()
.filter(|o| validators.contains(o))
.filter(|offender| validators.contains(offender))
.collect::<Vec<_>>();

let reward_per_block = Self::reward_per_block(staking_reward, validators.len());

let treasury_account = T::TreasuryAccount::treasury_account();
let slasher = Slasher::<T>::new(treasury_account);

let minimum_validator_count = Self::minimum_validator_count() as usize;

let active_validators = Self::active_validator_set().collect::<Vec<_>>();
Expand All @@ -63,8 +40,8 @@ impl<T: Trait> Module<T> {
.into_iter()
.flat_map(|offender| {
let expected_slash = Self::expected_slash_of(reward_per_block);
match Self::try_slash(&offender, expected_slash) {
Ok(_) => None,
match slasher.try_slash(&offender, expected_slash) {
Ok(_) => None, // Slash the offender successfully.
Err(actual_slashed) => {
debug!(
"[slash_offenders_in_session]expected_slash:{:?}, actual_slashed:{:?}",
Expand All @@ -73,7 +50,7 @@ impl<T: Trait> Module<T> {
if active_count > minimum_validator_count {
Self::apply_force_chilled(&offender);
active_count -= 1;
Some(offender)
Some(offender) // The offender does not have enough balance for the slashing.
} else {
None
}
Expand Down
118 changes: 59 additions & 59 deletions xpallets/mining/staking/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sp_runtime::RuntimeDebug;
#[cfg(feature = "std")]
use sp_runtime::{Deserialize, Serialize};
use xp_mining_common::WeightType;
use xp_mining_staking::MiningPower;

/// Destination for minted fresh PCX on each new session.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
Expand All @@ -17,7 +18,7 @@ pub enum MintedDestination<AccountId> {
///
/// If the (potential) validator failed to meet this requirement, force it to be chilled on new election round.
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug)]
pub struct BondRequirement<Balance: Default> {
pub struct BondRequirement<Balance> {
/// The minimal amount of self-bonded balance to be a qualified validator candidate.
pub self_bonded: Balance,
/// The minimal amount of total-bonded balance to be a qualified validator candidate.
Expand All @@ -30,7 +31,7 @@ pub struct BondRequirement<Balance: Default> {
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct Unbonded<Balance: Default, BlockNumber: Default> {
pub struct Unbonded<Balance, BlockNumber> {
/// Amount of funds to be unlocked.
pub value: Balance,
/// Block number at which point it'll be unlocked.
Expand All @@ -55,9 +56,9 @@ pub struct ValidatorLedger<Balance, BlockNumber> {
pub struct NominatorLedger<Balance, BlockNumber> {
/// The amount of
pub nomination: Balance,
///
/// Last calculated total vote weight of current nominator.
pub last_vote_weight: WeightType,
///
/// Block number at which point `last_vote_weight` just updated.
pub last_vote_weight_update: BlockNumber,
}

Expand All @@ -67,10 +68,12 @@ pub struct NominatorLedger<Balance, BlockNumber> {
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct ValidatorProfile<BlockNumber: Default> {
pub struct ValidatorProfile<BlockNumber> {
/// Block number at which point it's registered on chain.
pub registered_at: BlockNumber,
/// Validator is chilled right now.
///
/// Declared no desire to be a validator or forced to be chilled due to `MinimumCandidateThreshold`.
pub is_chilled: bool,
/// Block number of last performed `chill` operation.
pub last_chilled: Option<BlockNumber>,
Expand All @@ -80,46 +83,30 @@ pub struct ValidatorProfile<BlockNumber: Default> {
#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct NominatorProfile<Balance: Default, BlockNumber: Default> {
pub struct NominatorProfile<Balance, BlockNumber> {
/// Block number of last `rebond` operation.
pub last_rebond: Option<BlockNumber>,
///
/// Total unbonded entries.
pub unbonded_chunks: Vec<Unbonded<Balance, BlockNumber>>,
}

/// Status of (potential) validator in staking module.
///
/// For RPC usage.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub enum ValidatorStatus {
/// Declared no desire to be a validator or forced to be chilled due to `MinimumCandidateThreshold`.
Chilled,
/// Declared desire to be a validator but haven't won one place.
Candidate,
/// Being a validator, responsible for authoring the new blocks.
Validating,
}

impl Default for ValidatorStatus {
fn default() -> Self {
Self::Candidate
}
}

#[derive(PartialEq, Eq, Clone, Default, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
pub struct ValidatorInfo<AccountId: Default, Balance: Default, BlockNumber: Default> {
pub struct ValidatorInfo<AccountId, Balance, BlockNumber> {
/// AccountId of this (potential) validator.
pub account: AccountId,
#[cfg_attr(feature = "std", serde(flatten))]
pub profile: ValidatorProfile<BlockNumber>,
#[cfg_attr(feature = "std", serde(flatten))]
pub ledger: ValidatorLedger<Balance, BlockNumber>,
pub status: ValidatorStatus,
/// Being a validator, reponsible for authoring the new blocks.
pub is_validating: bool,
/// How much balances the validator has bonded itself.
pub self_bonded: Balance,
/// AccountId of the reward pot of this validator.
pub reward_pot_account: AccountId,
/// Balance of the reward pot account.
pub reward_pot_balance: Balance,
}

Expand Down Expand Up @@ -155,54 +142,30 @@ impl Default for Forcing {
}
}

// Shares of various reward destinations.
#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)]
// Top level shares of various reward destinations.
#[derive(Copy, Clone, PartialEq, Eq, Default, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct GlobalDistribution {
pub treasury: u32,
pub mining: u32,
}

impl Default for GlobalDistribution {
/// According to the ChainX 1.0 referendum proposal09:
/// (Treasury, Airdrop Asset, X-type Asset and Staking) = (12, 8, 80)
///
/// Airdrop Assets have been retired in ChainX 2.0, now only treasury and mining destinations.
/// (Treasury, X-type Asset and Staking) = (12, 88)
fn default() -> Self {
Self {
treasury: 12u32,
mining: 88u32,
}
}
}

impl GlobalDistribution {
/// Calculates the rewards for treasury and mining accordingly.
pub fn calc_rewards<T: Trait>(&self, reward: T::Balance) -> (T::Balance, T::Balance) {
let treasury_reward = reward * self.treasury.saturated_into()
/ (self.treasury + self.mining).saturated_into();
(treasury_reward, reward - treasury_reward)
}
}

#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)]
#[derive(Copy, Clone, PartialEq, Eq, Default, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct MiningDistribution {
pub asset: u32,
pub staking: u32,
}

impl Default for MiningDistribution {
/// According to the ChainX 1.0 referendum proposal09,
/// (Asset Mining, Staking) = (10, 90)
fn default() -> Self {
Self {
asset: 10u32,
staking: 90u32,
}
}
}

impl MiningDistribution {
/// Returns the reward for Staking given the total reward according to the Staking proportion.
pub fn calc_staking_reward<T: Trait>(&self, reward: T::Balance) -> T::Balance {
Expand All @@ -216,7 +179,7 @@ impl MiningDistribution {
/// the mining assets, but its unit mining power starts to decrease compared to the inital FixedPower.
fn asset_mining_vs_staking<T: Trait>(&self) -> (u128, u128) {
let total_staking_power =
crate::Module::<T>::total_staked().saturated_into::<xp_mining_staking::MiningPower>();
crate::Module::<T>::total_staked().saturated_into::<MiningPower>();
let total_asset_mining_power = T::AssetMining::total_asset_mining_power();

// When:
Expand Down Expand Up @@ -257,3 +220,40 @@ impl MiningDistribution {
}
}
}

/// Struct for performing the slash.
///
/// Abstracted for caching the treasury account.
#[derive(Copy, Clone, PartialEq, Eq, Default, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct Slasher<T: Trait>(T::AccountId);

impl<T: Trait> Slasher<T> {
pub fn new(treasury_account: T::AccountId) -> Self {
Self(treasury_account)
}

/// Returns Ok(_) if the reward pot of offender has enough balance to cover the slashing,
/// otherwise slash the reward pot as much as possible and returns the value actually slashed.
pub fn try_slash(
&self,
offender: &T::AccountId,
expected_slash: T::Balance,
) -> Result<(), T::Balance> {
let reward_pot = T::DetermineRewardPotAccount::reward_pot_account_for(offender);
let reward_pot_balance = <xpallet_assets::Module<T>>::pcx_free_balance(&reward_pot);

if expected_slash <= reward_pot_balance {
self.apply_slash(&reward_pot, expected_slash);
Ok(())
} else {
self.apply_slash(&reward_pot, reward_pot_balance);
Err(reward_pot_balance)
}
}

/// Actually slash the account being punished, all slashed balance will go to the treasury.
fn apply_slash(&self, reward_pot: &T::AccountId, value: T::Balance) {
let _ = <xpallet_assets::Module<T>>::pcx_move_free_balance(reward_pot, &self.0, value);
}
}

0 comments on commit af6c435

Please sign in to comment.