Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GPoS] Enable Slashing for Staking Module #163

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

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

34 changes: 23 additions & 11 deletions cstrml/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#[cfg(test)]
mod mock;

mod slashing;
#[cfg(test)]
mod tests;
Expand Down Expand Up @@ -1204,8 +1205,18 @@ impl<T: Trait> Module<T> {
fn chill_validator(v_stash: &T::AccountId) {
let validations = Self::validators(v_stash);

for guarantor in validations.guarantors {
<GuaranteeRel<T>>::remove(&guarantor, &v_stash);
for g_stash in validations.guarantors {

<Guarantors<T>>::mutate(&g_stash, |nominations| {
badkk marked this conversation as resolved.
Show resolved Hide resolved
if let Some(n) = nominations {
n.targets.retain(|stash| {
stash != v_stash
});
n.total -= Self::guarantee_rel(&g_stash, &v_stash).iter()
.fold(Zero::zero(), |acc, (_, value)| acc + value.clone());
}
});
<GuaranteeRel<T>>::remove(&g_stash, &v_stash);
}

<Validators<T>>::remove(v_stash);
Expand Down Expand Up @@ -1775,19 +1786,20 @@ impl<T: Trait> Module<T> {
// II. Traverse guarantors, update guarantor's ledger
<Guarantors<T>>::iter().for_each(|(g_stash, nominations)| {
let Nominations {
submitted_in: _,
submitted_in,
total,
targets: _,
mut targets,
suppressed: _,
} = nominations;

/*// Filter out nomination targets which were guaranteed before the most recent
// slashing span.
// TODO: uncomment it when figured out the slash strategy
targets.retain(|target| {
<Self as Store>::SlashingSpans::get(&target.who)
.map_or(true, |spans| submitted_in >= spans.last_start())
});*/
// Filter out nomination targets which were guaranteed before the most recent
// slashing span.
targets.retain(|stash| {
<Self as Store>::SlashingSpans::get(&stash).map_or(
true,
|spans| submitted_in >= spans.last_nonzero_slash(),
)
});

// 1. Init all guarantor's valid stakes
let g_controller = Self::bonded(&g_stash).unwrap();
Expand Down
8 changes: 4 additions & 4 deletions cstrml/staking/src/slashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@ impl SlashingSpans {
sp_std::iter::once(last).chain(prior)
}

/// Yields the era index where the most recent non-zero slash occurred.
// pub fn last_nonzero_slash(&self) -> EraIndex {
// self.last_nonzero_slash
// }
/// Yields the era index where the most recent non-zero slash occurred.
pub fn last_nonzero_slash(&self) -> EraIndex {
self.last_nonzero_slash
}

// prune the slashing spans against a window, whose start era index is given.
//
Expand Down
146 changes: 131 additions & 15 deletions cstrml/staking/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2774,11 +2774,11 @@ fn garbage_collection_on_window_pruning() {
}

#[test]
// TODO: this slashing test case should be changed
/*fn slashing_guarantors_by_span_max() {
fn slashing_guarantors_by_span_max() {
ExtBuilder::default().build().execute_with(|| {
start_era(1);
start_era(2);
start_era(1, false);
start_era(2, false);
start_era(3, false);
badkk marked this conversation as resolved.
Show resolved Hide resolved

assert_eq!(Balances::free_balance(&11), 1000);
assert_eq!(Balances::free_balance(&21), 2000);
Expand Down Expand Up @@ -2876,13 +2876,14 @@ fn garbage_collection_on_window_pruning() {
// only the maximum slash in a single span is taken.
assert_eq!(Balances::free_balance(&101), 2000 - slash_2_amount);
});
}*/
}

#[test]
// TODO: this slashing test case should be changed
/*fn slashes_are_summed_across_spans() {
fn slashes_are_summed_across_spans() {
ExtBuilder::default().build().execute_with(|| {
start_era(1);
start_era(1, false);
start_era(2, false);
start_era(3, false);

assert_eq!(Balances::free_balance(&21), 2000);
assert_eq!(Staking::slashable_balance_of(&21), 1000);
Expand All @@ -2900,13 +2901,13 @@ fn garbage_collection_on_window_pruning() {
let expected_spans = vec![
slashing::SlashingSpan {
index: 1,
start: 2,
start: 4,
length: None,
},
slashing::SlashingSpan {
index: 0,
start: 0,
length: Some(2),
length: Some(4),
},
];

Expand All @@ -2916,7 +2917,7 @@ fn garbage_collection_on_window_pruning() {
// 21 has been force-chilled. re-signal intent to validate.
Staking::validate(Origin::signed(20), Default::default()).unwrap();

start_era(4);
start_era(4, false);

assert_eq!(Staking::slashable_balance_of(&21), 900);

Expand All @@ -2936,20 +2937,20 @@ fn garbage_collection_on_window_pruning() {
},
slashing::SlashingSpan {
index: 1,
start: 2,
length: Some(3),
start: 4,
length: Some(1),
},
slashing::SlashingSpan {
index: 0,
start: 0,
length: Some(2),
length: Some(4),
},
];

assert_eq!(get_span(21).iter().collect::<Vec<_>>(), expected_spans);
assert_eq!(Balances::free_balance(&21), 1810);
});
}*/
}

#[test]
fn deferred_slashes_are_deferred() {
Expand Down Expand Up @@ -4267,3 +4268,118 @@ fn cut_guarantee_should_work() {
);
});
}

#[test]
fn chill_stash_should_work() {
ExtBuilder::default()
.guarantee(false)
.build()
.execute_with(|| {

for i in 1..10 {
let _ = Balances::deposit_creating(&i, 5000);
}

Staking::upsert_stake_limit(&5, 5000);
assert_eq!(Staking::stake_limit(&5).unwrap_or_default(), 5000);
Staking::upsert_stake_limit(&7, 5000);
assert_eq!(Staking::stake_limit(&7).unwrap_or_default(), 5000);

// add a new validator candidate
assert_ok!(Staking::bond(
Origin::signed(5),
6,
1000,
RewardDestination::Controller
));
assert_ok!(Staking::validate(Origin::signed(6), Perbill::default()));
// add a new validator candidate
assert_ok!(Staking::bond(
Origin::signed(7),
8,
2000,
RewardDestination::Controller
));
assert_ok!(Staking::validate(Origin::signed(8), Perbill::default()));

// add guarantor
assert_ok!(Staking::bond(
Origin::signed(1),
2,
2000,
RewardDestination::Controller
));

// add guarantor
assert_ok!(Staking::bond(
Origin::signed(3),
4,
2000,
RewardDestination::Controller
));

assert_ok!(Staking::guarantee(Origin::signed(2), (5, 250)));
assert_ok!(Staking::guarantee(Origin::signed(2), (5, 250)));
assert_ok!(Staking::guarantee(Origin::signed(2), (7, 250)));
assert_ok!(Staking::guarantee(Origin::signed(2), (7, 250)));
assert_ok!(Staking::guarantee(Origin::signed(4), (5, 250)));
assert_ok!(Staking::guarantee(Origin::signed(4), (7, 250)));

assert_eq!(Staking::guarantee_rel(1, 5).get(&(0 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(1, 5).get(&(1 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(1, 7).get(&(0 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(1, 7).get(&(1 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(3, 5).get(&(0 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(3, 7).get(&(0 as u32)), Some(&(250 as Balance)));

assert_eq!(
Staking::guarantors(&1),
Some(Nominations {
targets: vec![5, 7],
total: 1000,
submitted_in: 0,
suppressed: false
})
);

assert_eq!(
Staking::validators(&5),
Validations{
total: 750,
guarantee_fee: Default::default(),
guarantors: vec![1, 1, 3]
}
);

assert_ok!(Staking::chill(Origin::signed(6)));

assert_eq!(Staking::guarantee_rel(1, 5).get(&(0 as u32)), None);
assert_eq!(Staking::guarantee_rel(1, 5).get(&(1 as u32)), None);
assert_eq!(Staking::guarantee_rel(1, 7).get(&(0 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(1, 7).get(&(1 as u32)), Some(&(250 as Balance)));
assert_eq!(Staking::guarantee_rel(3, 5).get(&(0 as u32)), None);
assert_eq!(Staking::guarantee_rel(3, 7).get(&(0 as u32)), Some(&(250 as Balance)));

assert_eq!(
Staking::guarantors(&1),
Some(Nominations {
targets: vec![7],
total: 500,
submitted_in: 0,
suppressed: false
})
);

assert_eq!(
Staking::guarantors(&3),
Some(Nominations {
targets: vec![7],
total: 250,
submitted_in: 0,
suppressed: false
})
);

assert!(!<Validators<Test>>::contains_key(&5));
});
}
2 changes: 2 additions & 0 deletions runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ timestamp = { default-features = false, package = 'pallet-timestamp', version =
transaction-payment = { default-features = false, package = 'pallet-transaction-payment', version = '2.0.0-rc2' }
scheduler = { default-features = false, package = 'pallet-scheduler', version = '2.0.0-rc2' }
sudo = { default-features = false, package = 'pallet-sudo', version = '2.0.0-rc2' }
offences = { default-features = false, package = 'pallet-offences', version = '2.0.0-rc2' }

# substrate primitives
authority-discovery-primitives = { default-features = false, package = 'sp-authority-discovery', version = '2.0.0-rc2' }
Expand Down Expand Up @@ -99,6 +100,7 @@ std = [
'timestamp/std',
'transaction-payment/std',
'scheduler/std',
'offences/std',
'sp-runtime-interface/std',
'tee/std',
'staking/std',
Expand Down
14 changes: 13 additions & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl im_online::Trait for Runtime {
type AuthorityId = ImOnlineId;
type Event = Event;
type SessionDuration = SessionDuration;
type ReportUnresponsiveness = ();
type ReportUnresponsiveness = Offences;
type UnsignedPriority = ImOnlineUnsignedPriority;
}

Expand Down Expand Up @@ -347,6 +347,17 @@ impl staking::Trait for Runtime {
type TeeInterface = Self;
}

parameter_types! {
pub const OffencesWeightSoftLimit: Weight = Perbill::from_percent(60) * MaximumBlockWeight::get();
}

impl offences::Trait for Runtime {
type Event = Event;
type IdentificationTuple = session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
type WeightSoftLimit = OffencesWeightSoftLimit;
}

parameter_types! {
pub const ExistentialDeposit: u128 = 1 * CENTS;
}
Expand Down Expand Up @@ -441,6 +452,7 @@ construct_runtime! {
Grandpa: grandpa::{Module, Call, Storage, Config, Event},
ImOnline: im_online::{Module, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
AuthorityDiscovery: authority_discovery::{Module, Call, Config},
Offences: offences::{Module, Call, Storage, Event},
Scheduler: scheduler::{Module, Call, Storage, Event<T>},

// Crust modules
Expand Down