mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-24 08:55:47 +00:00
Off-the-table staking preference (#656)
This commit is contained in:
Generated
+323
-230
File diff suppressed because it is too large
Load Diff
@@ -23,9 +23,6 @@ substrate-runtime-system = { path = "../system", default_features = false }
|
|||||||
substrate-runtime-session = { path = "../session", default_features = false }
|
substrate-runtime-session = { path = "../session", default_features = false }
|
||||||
substrate-runtime-timestamp = { path = "../timestamp", default_features = false }
|
substrate-runtime-timestamp = { path = "../timestamp", default_features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
wabt = "0.4"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ extern crate serde;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate wabt;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate substrate_runtime_support as runtime_support;
|
extern crate substrate_runtime_support as runtime_support;
|
||||||
|
|
||||||
@@ -78,23 +75,26 @@ pub enum LockStatus<BlockNumber: Parameter> {
|
|||||||
LockedUntil(BlockNumber),
|
LockedUntil(BlockNumber),
|
||||||
Bonded,
|
Bonded,
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
/// Preference of what happens on a slash event.
|
/// Preference of what happens on a slash event.
|
||||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||||
#[derive(Encode, Decode, Eq, PartialEq, Clone, Copy)]
|
#[derive(Encode, Decode, Eq, PartialEq, Clone, Copy)]
|
||||||
pub struct SlashPreference {
|
pub struct ValidatorPrefs<Balance: Parameter + Codec + MaybeSerializeDebug + MaybeDeserialize> {
|
||||||
/// Validator should ensure this many more slashes than is necessary before being unstaked.
|
/// Validator should ensure this many more slashes than is necessary before being unstaked.
|
||||||
pub unstake_threshold: u32,
|
pub unstake_threshold: u32,
|
||||||
|
// Reward that validator takes up-front; only the rest is split between themself and nominators.
|
||||||
|
pub validator_payment: Balance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SlashPreference {
|
impl<B: Parameter + Codec + Default> Default for ValidatorPrefs<B> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
SlashPreference {
|
ValidatorPreferences {
|
||||||
unstake_threshold: 3,
|
unstake_threshold: 3,
|
||||||
|
off_the_table: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
pub trait Trait: balances::Trait + session::Trait {
|
pub trait Trait: balances::Trait + session::Trait {
|
||||||
/// The overarching event type.
|
/// The overarching event type.
|
||||||
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||||
@@ -109,7 +109,7 @@ decl_module! {
|
|||||||
fn unstake(aux, intentions_index: u32) -> Result = 1;
|
fn unstake(aux, intentions_index: u32) -> Result = 1;
|
||||||
fn nominate(aux, target: Address<T::AccountId, T::AccountIndex>) -> Result = 2;
|
fn nominate(aux, target: Address<T::AccountId, T::AccountIndex>) -> Result = 2;
|
||||||
fn unnominate(aux, target_index: u32) -> Result = 3;
|
fn unnominate(aux, target_index: u32) -> Result = 3;
|
||||||
fn register_slash_preference(aux, intentions_index: u32, p: SlashPreference) -> Result = 4;
|
fn register_preferences(aux, intentions_index: u32, unstake_threshold: u32, validator_payment: T::Balance) -> Result = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||||
@@ -158,8 +158,8 @@ decl_storage! {
|
|||||||
|
|
||||||
// The current era index.
|
// The current era index.
|
||||||
pub CurrentEra get(current_era): required T::BlockNumber;
|
pub CurrentEra get(current_era): required T::BlockNumber;
|
||||||
// Preference over how many times the validator should get slashed for being offline before they are automatically unstaked.
|
// Preferences that a validator has.
|
||||||
pub SlashPreferenceOf get(slash_preference_of): default map [ T::AccountId => SlashPreference ];
|
pub ValidatorPreferences: map [ T::AccountId => (u32, T::Balance) ];
|
||||||
// All the accounts with a desire to stake.
|
// All the accounts with a desire to stake.
|
||||||
pub Intentions get(intentions): default Vec<T::AccountId>;
|
pub Intentions get(intentions): default Vec<T::AccountId>;
|
||||||
// All nominator -> nominee relationships.
|
// All nominator -> nominee relationships.
|
||||||
@@ -190,6 +190,12 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
// PUBLIC IMMUTABLES
|
// PUBLIC IMMUTABLES
|
||||||
|
|
||||||
|
/// ValidatorPreferences getter, introduces a default.
|
||||||
|
pub fn validator_preferences(who: &T::AccountId) -> (u32, T::Balance) {
|
||||||
|
<ValidatorPreferences<T>>::get(who).unwrap_or_else(|| (3, Zero::zero()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MinimumValidatorCount getter, introduces a default.
|
||||||
pub fn minimum_validator_count() -> usize {
|
pub fn minimum_validator_count() -> usize {
|
||||||
<MinimumValidatorCount<T>>::get().map(|v| v as usize).unwrap_or(DEFAULT_MINIMUM_VALIDATOR_COUNT)
|
<MinimumValidatorCount<T>>::get().map(|v| v as usize).unwrap_or(DEFAULT_MINIMUM_VALIDATOR_COUNT)
|
||||||
}
|
}
|
||||||
@@ -301,10 +307,11 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Set the given account's preference for slashing behaviour should they be a validator.
|
/// Set the given account's preference for slashing behaviour should they be a validator.
|
||||||
///
|
///
|
||||||
/// An error (no-op) if `Self::intentions()[intentions_index] != aux`.
|
/// An error (no-op) if `Self::intentions()[intentions_index] != aux`.
|
||||||
fn register_slash_preference(
|
fn register_preferences(
|
||||||
aux: &T::PublicAux,
|
aux: &T::PublicAux,
|
||||||
intentions_index: u32,
|
intentions_index: u32,
|
||||||
p: SlashPreference
|
unstake_threshold: u32,
|
||||||
|
validator_payment: T::Balance
|
||||||
) -> Result {
|
) -> Result {
|
||||||
let aux = aux.ref_into();
|
let aux = aux.ref_into();
|
||||||
|
|
||||||
@@ -312,7 +319,7 @@ impl<T: Trait> Module<T> {
|
|||||||
return Err("Invalid index")
|
return Err("Invalid index")
|
||||||
}
|
}
|
||||||
|
|
||||||
<SlashPreferenceOf<T>>::insert(aux, p);
|
<ValidatorPreferences<T>>::insert(aux, (unstake_threshold, validator_payment));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -381,15 +388,23 @@ impl<T: Trait> Module<T> {
|
|||||||
/// Reward a given validator by a specific amount. Add the reward to their, and their nominators'
|
/// Reward a given validator by a specific amount. Add the reward to their, and their nominators'
|
||||||
/// balance, pro-rata.
|
/// balance, pro-rata.
|
||||||
fn reward_validator(who: &T::AccountId, reward: T::Balance) {
|
fn reward_validator(who: &T::AccountId, reward: T::Balance) {
|
||||||
let noms = Self::current_nominators_for(who);
|
let off_the_table = reward.min(Self::validator_preferences(who).1);
|
||||||
let total = noms.iter().map(<balances::Module<T>>::total_balance).fold(<balances::Module<T>>::total_balance(who), |acc, x| acc + x);
|
let reward = reward - off_the_table;
|
||||||
if !total.is_zero() {
|
let validator_cut = if reward.is_zero() {
|
||||||
|
Zero::zero()
|
||||||
|
} else {
|
||||||
|
let noms = Self::current_nominators_for(who);
|
||||||
|
let total = noms.iter()
|
||||||
|
.map(<balances::Module<T>>::total_balance)
|
||||||
|
.fold(<balances::Module<T>>::total_balance(who), |acc, x| acc + x)
|
||||||
|
.max(One::one());
|
||||||
let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow
|
let safe_mul_rational = |b| b * reward / total;// TODO: avoid overflow
|
||||||
for n in noms.iter() {
|
for n in noms.iter() {
|
||||||
let _ = <balances::Module<T>>::reward(n, safe_mul_rational(<balances::Module<T>>::total_balance(n)));
|
let _ = <balances::Module<T>>::reward(n, safe_mul_rational(<balances::Module<T>>::total_balance(n)));
|
||||||
}
|
}
|
||||||
let _ = <balances::Module<T>>::reward(who, safe_mul_rational(<balances::Module<T>>::total_balance(who)));
|
safe_mul_rational(<balances::Module<T>>::total_balance(who))
|
||||||
}
|
};
|
||||||
|
let _ = <balances::Module<T>>::reward(who, validator_cut + off_the_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Actually carry out the unstake operation.
|
/// Actually carry out the unstake operation.
|
||||||
@@ -401,7 +416,7 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
intentions.swap_remove(intentions_index);
|
intentions.swap_remove(intentions_index);
|
||||||
<Intentions<T>>::put(intentions);
|
<Intentions<T>>::put(intentions);
|
||||||
<SlashPreferenceOf<T>>::remove(who);
|
<ValidatorPreferences<T>>::remove(who);
|
||||||
<SlashCount<T>>::remove(who);
|
<SlashCount<T>>::remove(who);
|
||||||
<Bondage<T>>::insert(who, <system::Module<T>>::block_number() + Self::bonding_duration());
|
<Bondage<T>>::insert(who, <system::Module<T>>::block_number() + Self::bonding_duration());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -531,7 +546,7 @@ impl<T: Trait> consensus::OnOfflineValidator for Module<T> {
|
|||||||
let slash = Self::early_era_slash() << instances;
|
let slash = Self::early_era_slash() << instances;
|
||||||
let next_slash = slash << 1u32;
|
let next_slash = slash << 1u32;
|
||||||
let _ = Self::slash_validator(&v, slash);
|
let _ = Self::slash_validator(&v, slash);
|
||||||
if instances >= Self::slash_preference_of(&v).unstake_threshold
|
if instances >= Self::validator_preferences(&v).0
|
||||||
|| Self::slashable_balance(&v) < next_slash
|
|| Self::slashable_balance(&v) < next_slash
|
||||||
{
|
{
|
||||||
if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) {
|
if let Some(pos) = Self::intentions().into_iter().position(|x| &x == &v) {
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ fn note_offline_auto_unstake_session_change_should_work() {
|
|||||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||||
Balances::set_free_balance(&10, 7000);
|
Balances::set_free_balance(&10, 7000);
|
||||||
Balances::set_free_balance(&20, 7000);
|
Balances::set_free_balance(&20, 7000);
|
||||||
assert_ok!(Staking::register_slash_preference(&10, 0, SlashPreference { unstake_threshold: 1 }));
|
assert_ok!(Staking::register_preferences(&10, 0, 1, 0));
|
||||||
|
|
||||||
assert_eq!(Staking::intentions(), vec![10, 20]);
|
assert_eq!(Staking::intentions(), vec![10, 20]);
|
||||||
|
|
||||||
@@ -165,8 +165,6 @@ fn note_offline_auto_unstake_session_change_should_work() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rewards_should_work() {
|
fn rewards_should_work() {
|
||||||
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
with_externalities(&mut new_test_ext(0, 3, 3, 0, true, 10), || {
|
||||||
@@ -343,6 +341,28 @@ fn nominating_and_rewards_should_work() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rewards_with_off_the_table_should_work() {
|
||||||
|
with_externalities(&mut new_test_ext(0, 1, 1, 0, true, 10), || {
|
||||||
|
System::set_block_number(1);
|
||||||
|
assert_ok!(Staking::stake(&1));
|
||||||
|
assert_ok!(Staking::nominate(&2, 1.into()));
|
||||||
|
assert_ok!(Staking::stake(&3));
|
||||||
|
Session::check_rotate_session();
|
||||||
|
assert_eq!(Session::validators(), vec![1, 3]); // 1 + 2, 3
|
||||||
|
assert_eq!(Balances::total_balance(&1), 10);
|
||||||
|
assert_eq!(Balances::total_balance(&2), 20);
|
||||||
|
assert_eq!(Balances::total_balance(&3), 30);
|
||||||
|
|
||||||
|
System::set_block_number(2);
|
||||||
|
assert_ok!(Staking::register_preferences(&1, Staking::intentions().into_iter().position(|i| i == 1).unwrap() as u32, 3, 4));
|
||||||
|
Session::check_rotate_session();
|
||||||
|
assert_eq!(Balances::total_balance(&1), 16);
|
||||||
|
assert_eq!(Balances::total_balance(&2), 24);
|
||||||
|
assert_eq!(Balances::total_balance(&3), 40);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nominating_slashes_should_work() {
|
fn nominating_slashes_should_work() {
|
||||||
with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || {
|
with_externalities(&mut new_test_ext(0, 2, 2, 0, true, 10), || {
|
||||||
|
|||||||
Reference in New Issue
Block a user