Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 05a6abb

Browse files
Offence implementations can disable offenders independently from slashing (#10201)
* Offence implementations can disable offenders independently from slashing * Fix build on CI * Run cargo fmt * Fixes based on review comments * Make parameter naming consistent * Fix migration and some English * Fix migration - again * cargo fmt * Cover 2 new cases with a test
1 parent 6d61f48 commit 05a6abb

File tree

8 files changed

+129
-46
lines changed

8 files changed

+129
-46
lines changed

frame/offences/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ where
150150
&concurrent_offenders,
151151
&slash_perbill,
152152
offence.session_index(),
153+
offence.disable_strategy(),
153154
);
154155

155156
// Deposit the event.

frame/offences/src/migration.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{Config, OffenceDetails, Perbill, SessionIndex};
1919
use frame_support::{
2020
generate_storage_alias, pallet_prelude::ValueQuery, traits::Get, weights::Weight,
2121
};
22-
use sp_staking::offence::OnOffenceHandler;
22+
use sp_staking::offence::{DisableStrategy, OnOffenceHandler};
2323
use sp_std::vec::Vec;
2424

2525
/// Type of data stored as a deferred offence
@@ -41,7 +41,12 @@ pub fn remove_deferred_storage<T: Config>() -> Weight {
4141
let deferred = <DeferredOffences<T>>::take();
4242
log::info!(target: "runtime::offences", "have {} deferred offences, applying.", deferred.len());
4343
for (offences, perbill, session) in deferred.iter() {
44-
let consumed = T::OnOffenceHandler::on_offence(&offences, &perbill, *session);
44+
let consumed = T::OnOffenceHandler::on_offence(
45+
&offences,
46+
&perbill,
47+
*session,
48+
DisableStrategy::WhenSlashed,
49+
);
4550
weight = weight.saturating_add(consumed);
4651
}
4752

frame/offences/src/mock.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use sp_runtime::{
3636
Perbill,
3737
};
3838
use sp_staking::{
39-
offence::{self, Kind, OffenceDetails},
39+
offence::{self, DisableStrategy, Kind, OffenceDetails},
4040
SessionIndex,
4141
};
4242
use std::cell::RefCell;
@@ -55,6 +55,7 @@ impl<Reporter, Offender> offence::OnOffenceHandler<Reporter, Offender, Weight>
5555
_offenders: &[OffenceDetails<Reporter, Offender>],
5656
slash_fraction: &[Perbill],
5757
_offence_session: SessionIndex,
58+
_disable_strategy: DisableStrategy,
5859
) -> Weight {
5960
ON_OFFENCE_PERBILL.with(|f| {
6061
*f.borrow_mut() = slash_fraction.to_vec();

frame/staking/src/mock.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use sp_runtime::{
3333
testing::{Header, TestXt, UintAuthorityId},
3434
traits::{IdentityLookup, Zero},
3535
};
36-
use sp_staking::offence::{OffenceDetails, OnOffenceHandler};
36+
use sp_staking::offence::{DisableStrategy, OffenceDetails, OnOffenceHandler};
3737
use std::cell::RefCell;
3838

3939
pub const INIT_TIMESTAMP: u64 = 30_000;
@@ -765,11 +765,12 @@ pub(crate) fn on_offence_in_era(
765765
>],
766766
slash_fraction: &[Perbill],
767767
era: EraIndex,
768+
disable_strategy: DisableStrategy,
768769
) {
769770
let bonded_eras = crate::BondedEras::<Test>::get();
770771
for &(bonded_era, start_session) in bonded_eras.iter() {
771772
if bonded_era == era {
772-
let _ = Staking::on_offence(offenders, slash_fraction, start_session);
773+
let _ = Staking::on_offence(offenders, slash_fraction, start_session, disable_strategy);
773774
return
774775
} else if bonded_era > era {
775776
break
@@ -781,6 +782,7 @@ pub(crate) fn on_offence_in_era(
781782
offenders,
782783
slash_fraction,
783784
Staking::eras_start_session_index(era).unwrap(),
785+
disable_strategy,
784786
);
785787
} else {
786788
panic!("cannot slash in era {}", era);
@@ -795,7 +797,7 @@ pub(crate) fn on_offence_now(
795797
slash_fraction: &[Perbill],
796798
) {
797799
let now = Staking::active_era().unwrap().index;
798-
on_offence_in_era(offenders, slash_fraction, now)
800+
on_offence_in_era(offenders, slash_fraction, now, DisableStrategy::WhenSlashed)
799801
}
800802

801803
pub(crate) fn add_slash(who: &AccountId) {

frame/staking/src/pallet/impls.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use sp_runtime::{
3636
Perbill,
3737
};
3838
use sp_staking::{
39-
offence::{OffenceDetails, OnOffenceHandler},
39+
offence::{DisableStrategy, OffenceDetails, OnOffenceHandler},
4040
SessionIndex,
4141
};
4242
use sp_std::{collections::btree_map::BTreeMap, prelude::*};
@@ -1137,6 +1137,7 @@ where
11371137
>],
11381138
slash_fraction: &[Perbill],
11391139
slash_session: SessionIndex,
1140+
disable_strategy: DisableStrategy,
11401141
) -> Weight {
11411142
let reward_proportion = SlashRewardFraction::<T>::get();
11421143
let mut consumed_weight: Weight = 0;
@@ -1206,6 +1207,7 @@ where
12061207
window_start,
12071208
now: active_era,
12081209
reward_proportion,
1210+
disable_strategy,
12091211
});
12101212

12111213
if let Some(mut unapplied) = unapplied {

frame/staking/src/slashing.rs

+35-35
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use sp_runtime::{
6363
traits::{Saturating, Zero},
6464
DispatchResult, RuntimeDebug,
6565
};
66+
use sp_staking::offence::DisableStrategy;
6667
use sp_std::vec::Vec;
6768

6869
/// The proportion of the slashing reward to be paid out on the first slashing detection.
@@ -213,6 +214,8 @@ pub(crate) struct SlashParams<'a, T: 'a + Config> {
213214
/// The maximum percentage of a slash that ever gets paid out.
214215
/// This is f_inf in the paper.
215216
pub(crate) reward_proportion: Perbill,
217+
/// When to disable offenders.
218+
pub(crate) disable_strategy: DisableStrategy,
216219
}
217220

218221
/// Computes a slash of a validator and nominators. It returns an unapplied
@@ -224,29 +227,30 @@ pub(crate) struct SlashParams<'a, T: 'a + Config> {
224227
pub(crate) fn compute_slash<T: Config>(
225228
params: SlashParams<T>,
226229
) -> Option<UnappliedSlash<T::AccountId, BalanceOf<T>>> {
227-
let SlashParams { stash, slash, exposure, slash_era, window_start, now, reward_proportion } =
228-
params.clone();
229-
230230
let mut reward_payout = Zero::zero();
231231
let mut val_slashed = Zero::zero();
232232

233233
// is the slash amount here a maximum for the era?
234-
let own_slash = slash * exposure.own;
235-
if slash * exposure.total == Zero::zero() {
234+
let own_slash = params.slash * params.exposure.own;
235+
if params.slash * params.exposure.total == Zero::zero() {
236236
// kick out the validator even if they won't be slashed,
237237
// as long as the misbehavior is from their most recent slashing span.
238238
kick_out_if_recent::<T>(params);
239239
return None
240240
}
241241

242242
let (prior_slash_p, _era_slash) =
243-
<Pallet<T> as Store>::ValidatorSlashInEra::get(&slash_era, stash)
243+
<Pallet<T> as Store>::ValidatorSlashInEra::get(&params.slash_era, params.stash)
244244
.unwrap_or((Perbill::zero(), Zero::zero()));
245245

246246
// compare slash proportions rather than slash values to avoid issues due to rounding
247247
// error.
248-
if slash.deconstruct() > prior_slash_p.deconstruct() {
249-
<Pallet<T> as Store>::ValidatorSlashInEra::insert(&slash_era, stash, &(slash, own_slash));
248+
if params.slash.deconstruct() > prior_slash_p.deconstruct() {
249+
<Pallet<T> as Store>::ValidatorSlashInEra::insert(
250+
&params.slash_era,
251+
params.stash,
252+
&(params.slash, own_slash),
253+
);
250254
} else {
251255
// we slash based on the max in era - this new event is not the max,
252256
// so neither the validator or any nominators will need an update.
@@ -261,35 +265,34 @@ pub(crate) fn compute_slash<T: Config>(
261265
// apply slash to validator.
262266
{
263267
let mut spans = fetch_spans::<T>(
264-
stash,
265-
window_start,
268+
params.stash,
269+
params.window_start,
266270
&mut reward_payout,
267271
&mut val_slashed,
268-
reward_proportion,
272+
params.reward_proportion,
269273
);
270274

271-
let target_span = spans.compare_and_update_span_slash(slash_era, own_slash);
275+
let target_span = spans.compare_and_update_span_slash(params.slash_era, own_slash);
272276

273277
if target_span == Some(spans.span_index()) {
274278
// misbehavior occurred within the current slashing span - take appropriate
275279
// actions.
276280

277281
// chill the validator - it misbehaved in the current span and should
278282
// not continue in the next election. also end the slashing span.
279-
spans.end_span(now);
280-
<Pallet<T>>::chill_stash(stash);
283+
spans.end_span(params.now);
284+
<Pallet<T>>::chill_stash(params.stash);
281285
}
282286
}
283287

284-
// add the validator to the offenders list and make sure it is disabled for
285-
// the duration of the era
286-
add_offending_validator::<T>(params.stash, true);
288+
let disable_when_slashed = params.disable_strategy != DisableStrategy::Never;
289+
add_offending_validator::<T>(params.stash, disable_when_slashed);
287290

288291
let mut nominators_slashed = Vec::new();
289-
reward_payout += slash_nominators::<T>(params, prior_slash_p, &mut nominators_slashed);
292+
reward_payout += slash_nominators::<T>(params.clone(), prior_slash_p, &mut nominators_slashed);
290293

291294
Some(UnappliedSlash {
292-
validator: stash.clone(),
295+
validator: params.stash.clone(),
293296
own: val_slashed,
294297
others: nominators_slashed,
295298
reporters: Vec::new(),
@@ -316,9 +319,8 @@ fn kick_out_if_recent<T: Config>(params: SlashParams<T>) {
316319
<Pallet<T>>::chill_stash(params.stash);
317320
}
318321

319-
// add the validator to the offenders list but since there's no slash being
320-
// applied there's no need to disable the validator
321-
add_offending_validator::<T>(params.stash, false);
322+
let disable_without_slash = params.disable_strategy == DisableStrategy::Always;
323+
add_offending_validator::<T>(params.stash, disable_without_slash);
322324
}
323325

324326
/// Add the given validator to the offenders list and optionally disable it.
@@ -371,29 +373,27 @@ fn slash_nominators<T: Config>(
371373
prior_slash_p: Perbill,
372374
nominators_slashed: &mut Vec<(T::AccountId, BalanceOf<T>)>,
373375
) -> BalanceOf<T> {
374-
let SlashParams { stash: _, slash, exposure, slash_era, window_start, now, reward_proportion } =
375-
params;
376-
377376
let mut reward_payout = Zero::zero();
378377

379-
nominators_slashed.reserve(exposure.others.len());
380-
for nominator in &exposure.others {
378+
nominators_slashed.reserve(params.exposure.others.len());
379+
for nominator in &params.exposure.others {
381380
let stash = &nominator.who;
382381
let mut nom_slashed = Zero::zero();
383382

384383
// the era slash of a nominator always grows, if the validator
385384
// had a new max slash for the era.
386385
let era_slash = {
387386
let own_slash_prior = prior_slash_p * nominator.value;
388-
let own_slash_by_validator = slash * nominator.value;
387+
let own_slash_by_validator = params.slash * nominator.value;
389388
let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior);
390389

391-
let mut era_slash = <Pallet<T> as Store>::NominatorSlashInEra::get(&slash_era, stash)
392-
.unwrap_or_else(|| Zero::zero());
390+
let mut era_slash =
391+
<Pallet<T> as Store>::NominatorSlashInEra::get(&params.slash_era, stash)
392+
.unwrap_or_else(|| Zero::zero());
393393

394394
era_slash += own_slash_difference;
395395

396-
<Pallet<T> as Store>::NominatorSlashInEra::insert(&slash_era, stash, &era_slash);
396+
<Pallet<T> as Store>::NominatorSlashInEra::insert(&params.slash_era, stash, &era_slash);
397397

398398
era_slash
399399
};
@@ -402,18 +402,18 @@ fn slash_nominators<T: Config>(
402402
{
403403
let mut spans = fetch_spans::<T>(
404404
stash,
405-
window_start,
405+
params.window_start,
406406
&mut reward_payout,
407407
&mut nom_slashed,
408-
reward_proportion,
408+
params.reward_proportion,
409409
);
410410

411-
let target_span = spans.compare_and_update_span_slash(slash_era, era_slash);
411+
let target_span = spans.compare_and_update_span_slash(params.slash_era, era_slash);
412412

413413
if target_span == Some(spans.span_index()) {
414414
// End the span, but don't chill the nominator. its nomination
415415
// on this validator will be ignored in the future.
416-
spans.end_span(now);
416+
spans.end_span(params.now);
417417
}
418418
}
419419

0 commit comments

Comments
 (0)