1use crate::{
53 asset, BalanceOf, Config, Error, Exposure, NegativeImbalanceOf, NominatorSlashInEra, Pallet,
54 Perbill, SpanSlash, UnappliedSlash, ValidatorSlashInEra,
55};
56use alloc::vec::Vec;
57use codec::{Decode, Encode, MaxEncodedLen};
58use frame_support::{
59 ensure,
60 traits::{Defensive, DefensiveSaturating, Imbalance, OnUnbalanced},
61};
62use scale_info::TypeInfo;
63use sp_runtime::{
64 traits::{Saturating, Zero},
65 DispatchResult, RuntimeDebug,
66};
67use sp_staking::{EraIndex, StakingInterface};
68
69const REWARD_F1: Perbill = Perbill::from_percent(50);
72
73pub type SpanIndex = u32;
75
76#[derive(Encode, Decode, TypeInfo)]
78#[cfg_attr(test, derive(Debug, PartialEq))]
79pub(crate) struct SlashingSpan {
80 pub(crate) index: SpanIndex,
81 pub(crate) start: EraIndex,
82 pub(crate) length: Option<EraIndex>, }
84
85impl SlashingSpan {
86 fn contains_era(&self, era: EraIndex) -> bool {
87 self.start <= era && self.length.map_or(true, |l| self.start.saturating_add(l) > era)
88 }
89}
90
91#[derive(Encode, Decode, RuntimeDebug, TypeInfo)]
93pub struct SlashingSpans {
94 span_index: SpanIndex,
97 last_start: EraIndex,
99 last_nonzero_slash: EraIndex,
101 prior: Vec<EraIndex>,
104}
105
106impl SlashingSpans {
107 pub(crate) fn new(window_start: EraIndex) -> Self {
110 SlashingSpans {
111 span_index: 0,
112 last_start: window_start,
113 last_nonzero_slash: 0,
117 prior: Vec::new(),
118 }
119 }
120
121 pub(crate) fn end_span(&mut self, now: EraIndex) -> bool {
125 let next_start = now.defensive_saturating_add(1);
126 if next_start <= self.last_start {
127 return false
128 }
129
130 let last_length = next_start.defensive_saturating_sub(self.last_start);
131 self.prior.insert(0, last_length);
132 self.last_start = next_start;
133 self.span_index.defensive_saturating_accrue(1);
134 true
135 }
136
137 pub(crate) fn iter(&'_ self) -> impl Iterator<Item = SlashingSpan> + '_ {
139 let mut last_start = self.last_start;
140 let mut index = self.span_index;
141 let last = SlashingSpan { index, start: last_start, length: None };
142 let prior = self.prior.iter().cloned().map(move |length| {
143 let start = last_start.defensive_saturating_sub(length);
144 last_start = start;
145 index.defensive_saturating_reduce(1);
146
147 SlashingSpan { index, start, length: Some(length) }
148 });
149
150 core::iter::once(last).chain(prior)
151 }
152
153 pub fn last_nonzero_slash(&self) -> EraIndex {
155 self.last_nonzero_slash
156 }
157
158 fn prune(&mut self, window_start: EraIndex) -> Option<(SpanIndex, SpanIndex)> {
163 let old_idx = self
164 .iter()
165 .skip(1) .position(|span| {
167 span.length
168 .map_or(false, |len| span.start.defensive_saturating_add(len) <= window_start)
169 });
170
171 let earliest_span_index =
172 self.span_index.defensive_saturating_sub(self.prior.len() as SpanIndex);
173 let pruned = match old_idx {
174 Some(o) => {
175 self.prior.truncate(o);
176 let new_earliest =
177 self.span_index.defensive_saturating_sub(self.prior.len() as SpanIndex);
178 Some((earliest_span_index, new_earliest))
179 },
180 None => None,
181 };
182
183 self.last_start = core::cmp::max(self.last_start, window_start);
185 pruned
186 }
187}
188
189#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
191pub(crate) struct SpanRecord<Balance> {
192 slashed: Balance,
193 paid_out: Balance,
194}
195
196impl<Balance> SpanRecord<Balance> {
197 #[cfg(test)]
199 pub(crate) fn amount(&self) -> &Balance {
200 &self.slashed
201 }
202}
203
204#[derive(Clone)]
206pub(crate) struct SlashParams<'a, T: 'a + Config> {
207 pub(crate) stash: &'a T::AccountId,
209 pub(crate) slash: Perbill,
211 pub(crate) exposure: &'a Exposure<T::AccountId, BalanceOf<T>>,
213 pub(crate) slash_era: EraIndex,
215 pub(crate) window_start: EraIndex,
217 pub(crate) now: EraIndex,
219 pub(crate) reward_proportion: Perbill,
222}
223
224pub(crate) fn compute_slash<T: Config>(
231 params: SlashParams<T>,
232) -> Option<UnappliedSlash<T::AccountId, BalanceOf<T>>> {
233 let mut reward_payout = Zero::zero();
234 let mut val_slashed = Zero::zero();
235
236 let own_slash = params.slash * params.exposure.own;
238 if params.slash * params.exposure.total == Zero::zero() {
239 kick_out_if_recent::<T>(params);
242 return None
243 }
244
245 let prior_slash_p = ValidatorSlashInEra::<T>::get(¶ms.slash_era, params.stash)
246 .map_or(Zero::zero(), |(prior_slash_proportion, _)| prior_slash_proportion);
247
248 if params.slash.deconstruct() > prior_slash_p.deconstruct() {
251 ValidatorSlashInEra::<T>::insert(
252 ¶ms.slash_era,
253 params.stash,
254 &(params.slash, own_slash),
255 );
256 } else {
257 return None
265 }
266
267 {
269 let mut spans = fetch_spans::<T>(
270 params.stash,
271 params.window_start,
272 &mut reward_payout,
273 &mut val_slashed,
274 params.reward_proportion,
275 );
276
277 let target_span = spans.compare_and_update_span_slash(params.slash_era, own_slash);
278
279 if target_span == Some(spans.span_index()) {
280 spans.end_span(params.now);
283 }
284 }
285
286 let mut nominators_slashed = Vec::new();
287 reward_payout += slash_nominators::<T>(params.clone(), prior_slash_p, &mut nominators_slashed);
288
289 Some(UnappliedSlash {
290 validator: params.stash.clone(),
291 own: val_slashed,
292 others: nominators_slashed,
293 reporters: Vec::new(),
294 payout: reward_payout,
295 })
296}
297
298fn kick_out_if_recent<T: Config>(params: SlashParams<T>) {
301 let mut reward_payout = Zero::zero();
303 let mut val_slashed = Zero::zero();
304 let mut spans = fetch_spans::<T>(
305 params.stash,
306 params.window_start,
307 &mut reward_payout,
308 &mut val_slashed,
309 params.reward_proportion,
310 );
311
312 if spans.era_span(params.slash_era).map(|s| s.index) == Some(spans.span_index()) {
313 spans.end_span(params.now);
315 }
316}
317
318fn slash_nominators<T: Config>(
322 params: SlashParams<T>,
323 prior_slash_p: Perbill,
324 nominators_slashed: &mut Vec<(T::AccountId, BalanceOf<T>)>,
325) -> BalanceOf<T> {
326 let mut reward_payout = Zero::zero();
327
328 nominators_slashed.reserve(params.exposure.others.len());
329 for nominator in ¶ms.exposure.others {
330 let stash = &nominator.who;
331 let mut nom_slashed = Zero::zero();
332
333 let era_slash = {
336 let own_slash_prior = prior_slash_p * nominator.value;
337 let own_slash_by_validator = params.slash * nominator.value;
338 let own_slash_difference = own_slash_by_validator.saturating_sub(own_slash_prior);
339
340 let mut era_slash =
341 NominatorSlashInEra::<T>::get(¶ms.slash_era, stash).unwrap_or_else(Zero::zero);
342 era_slash += own_slash_difference;
343 NominatorSlashInEra::<T>::insert(¶ms.slash_era, stash, &era_slash);
344
345 era_slash
346 };
347
348 {
350 let mut spans = fetch_spans::<T>(
351 stash,
352 params.window_start,
353 &mut reward_payout,
354 &mut nom_slashed,
355 params.reward_proportion,
356 );
357
358 let target_span = spans.compare_and_update_span_slash(params.slash_era, era_slash);
359
360 if target_span == Some(spans.span_index()) {
361 spans.end_span(params.now);
363 }
364 }
365 nominators_slashed.push((stash.clone(), nom_slashed));
366 }
367
368 reward_payout
369}
370
371struct InspectingSpans<'a, T: Config + 'a> {
379 dirty: bool,
380 window_start: EraIndex,
381 stash: &'a T::AccountId,
382 spans: SlashingSpans,
383 paid_out: &'a mut BalanceOf<T>,
384 slash_of: &'a mut BalanceOf<T>,
385 reward_proportion: Perbill,
386 _marker: core::marker::PhantomData<T>,
387}
388
389fn fetch_spans<'a, T: Config + 'a>(
391 stash: &'a T::AccountId,
392 window_start: EraIndex,
393 paid_out: &'a mut BalanceOf<T>,
394 slash_of: &'a mut BalanceOf<T>,
395 reward_proportion: Perbill,
396) -> InspectingSpans<'a, T> {
397 let spans = crate::SlashingSpans::<T>::get(stash).unwrap_or_else(|| {
398 let spans = SlashingSpans::new(window_start);
399 crate::SlashingSpans::<T>::insert(stash, &spans);
400 spans
401 });
402
403 InspectingSpans {
404 dirty: false,
405 window_start,
406 stash,
407 spans,
408 slash_of,
409 paid_out,
410 reward_proportion,
411 _marker: core::marker::PhantomData,
412 }
413}
414
415impl<'a, T: 'a + Config> InspectingSpans<'a, T> {
416 fn span_index(&self) -> SpanIndex {
417 self.spans.span_index
418 }
419
420 fn end_span(&mut self, now: EraIndex) {
421 self.dirty = self.spans.end_span(now) || self.dirty;
422 }
423
424 fn add_slash(&mut self, amount: BalanceOf<T>, slash_era: EraIndex) {
428 *self.slash_of += amount;
429 self.spans.last_nonzero_slash = core::cmp::max(self.spans.last_nonzero_slash, slash_era);
430 }
431
432 fn era_span(&self, era: EraIndex) -> Option<SlashingSpan> {
434 self.spans.iter().find(|span| span.contains_era(era))
435 }
436
437 fn compare_and_update_span_slash(
442 &mut self,
443 slash_era: EraIndex,
444 slash: BalanceOf<T>,
445 ) -> Option<SpanIndex> {
446 let target_span = self.era_span(slash_era)?;
447 let span_slash_key = (self.stash.clone(), target_span.index);
448 let mut span_record = SpanSlash::<T>::get(&span_slash_key);
449 let mut changed = false;
450
451 let reward = if span_record.slashed < slash {
452 let difference = slash.defensive_saturating_sub(span_record.slashed);
454 span_record.slashed = slash;
455
456 let reward =
458 REWARD_F1 * (self.reward_proportion * slash).saturating_sub(span_record.paid_out);
459
460 self.add_slash(difference, slash_era);
461 changed = true;
462
463 reward
464 } else if span_record.slashed == slash {
465 REWARD_F1 * (self.reward_proportion * slash).saturating_sub(span_record.paid_out)
467 } else {
468 Zero::zero()
469 };
470
471 if !reward.is_zero() {
472 changed = true;
473 span_record.paid_out += reward;
474 *self.paid_out += reward;
475 }
476
477 if changed {
478 self.dirty = true;
479 SpanSlash::<T>::insert(&span_slash_key, &span_record);
480 }
481
482 Some(target_span.index)
483 }
484}
485
486impl<'a, T: 'a + Config> Drop for InspectingSpans<'a, T> {
487 fn drop(&mut self) {
488 if !self.dirty {
490 return
491 }
492
493 if let Some((start, end)) = self.spans.prune(self.window_start) {
494 for span_index in start..end {
495 SpanSlash::<T>::remove(&(self.stash.clone(), span_index));
496 }
497 }
498
499 crate::SlashingSpans::<T>::insert(self.stash, &self.spans);
500 }
501}
502
503pub(crate) fn clear_era_metadata<T: Config>(obsolete_era: EraIndex) {
505 #[allow(deprecated)]
506 ValidatorSlashInEra::<T>::remove_prefix(&obsolete_era, None);
507 #[allow(deprecated)]
508 NominatorSlashInEra::<T>::remove_prefix(&obsolete_era, None);
509}
510
511pub(crate) fn clear_stash_metadata<T: Config>(
513 stash: &T::AccountId,
514 num_slashing_spans: u32,
515) -> DispatchResult {
516 let spans = match crate::SlashingSpans::<T>::get(stash) {
517 None => return Ok(()),
518 Some(s) => s,
519 };
520
521 ensure!(
522 num_slashing_spans as usize >= spans.iter().count(),
523 Error::<T>::IncorrectSlashingSpans
524 );
525
526 crate::SlashingSpans::<T>::remove(stash);
527
528 for span in spans.iter() {
534 SpanSlash::<T>::remove(&(stash.clone(), span.index));
535 }
536
537 Ok(())
538}
539
540pub fn do_slash<T: Config>(
544 stash: &T::AccountId,
545 value: BalanceOf<T>,
546 reward_payout: &mut BalanceOf<T>,
547 slashed_imbalance: &mut NegativeImbalanceOf<T>,
548 slash_era: EraIndex,
549) {
550 let mut ledger =
551 match Pallet::<T>::ledger(sp_staking::StakingAccount::Stash(stash.clone())).defensive() {
552 Ok(ledger) => ledger,
553 Err(_) => return, };
555
556 let value = ledger.slash(value, asset::existential_deposit::<T>(), slash_era);
557 if value.is_zero() {
558 return
560 }
561
562 if !Pallet::<T>::is_virtual_staker(stash) {
564 let (imbalance, missing) = asset::slash::<T>(stash, value);
565 slashed_imbalance.subsume(imbalance);
566
567 if !missing.is_zero() {
568 *reward_payout = reward_payout.saturating_sub(missing);
570 }
571 }
572
573 let _ = ledger
574 .update()
575 .defensive_proof("ledger fetched from storage so it exists in storage; qed.");
576
577 <Pallet<T>>::deposit_event(super::Event::<T>::Slashed { staker: stash.clone(), amount: value });
579}
580
581pub(crate) fn apply_slash<T: Config>(
583 unapplied_slash: UnappliedSlash<T::AccountId, BalanceOf<T>>,
584 slash_era: EraIndex,
585) {
586 let mut slashed_imbalance = NegativeImbalanceOf::<T>::zero();
587 let mut reward_payout = unapplied_slash.payout;
588
589 do_slash::<T>(
590 &unapplied_slash.validator,
591 unapplied_slash.own,
592 &mut reward_payout,
593 &mut slashed_imbalance,
594 slash_era,
595 );
596
597 for &(ref nominator, nominator_slash) in &unapplied_slash.others {
598 do_slash::<T>(
599 nominator,
600 nominator_slash,
601 &mut reward_payout,
602 &mut slashed_imbalance,
603 slash_era,
604 );
605 }
606
607 pay_reporters::<T>(reward_payout, slashed_imbalance, &unapplied_slash.reporters);
608}
609
610fn pay_reporters<T: Config>(
612 reward_payout: BalanceOf<T>,
613 slashed_imbalance: NegativeImbalanceOf<T>,
614 reporters: &[T::AccountId],
615) {
616 if reward_payout.is_zero() || reporters.is_empty() {
617 T::Slash::on_unbalanced(slashed_imbalance);
620 return
621 }
622
623 let reward_payout = reward_payout.min(slashed_imbalance.peek());
625 let (mut reward_payout, mut value_slashed) = slashed_imbalance.split(reward_payout);
626
627 let per_reporter = reward_payout.peek() / (reporters.len() as u32).into();
628 for reporter in reporters {
629 let (reporter_reward, rest) = reward_payout.split(per_reporter);
630 reward_payout = rest;
631
632 asset::deposit_slashed::<T>(reporter, reporter_reward);
635 }
636
637 value_slashed.subsume(reward_payout); T::Slash::on_unbalanced(value_slashed);
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
647 fn span_contains_era() {
648 let span = SlashingSpan { index: 0, start: 1000, length: None };
650 assert!(!span.contains_era(0));
651 assert!(!span.contains_era(999));
652
653 assert!(span.contains_era(1000));
654 assert!(span.contains_era(1001));
655 assert!(span.contains_era(10000));
656
657 let span = SlashingSpan { index: 0, start: 1000, length: Some(10) };
659 assert!(!span.contains_era(0));
660 assert!(!span.contains_era(999));
661
662 assert!(span.contains_era(1000));
663 assert!(span.contains_era(1001));
664 assert!(span.contains_era(1009));
665 assert!(!span.contains_era(1010));
666 assert!(!span.contains_era(1011));
667 }
668
669 #[test]
670 fn single_slashing_span() {
671 let spans = SlashingSpans {
672 span_index: 0,
673 last_start: 1000,
674 last_nonzero_slash: 0,
675 prior: Vec::new(),
676 };
677
678 assert_eq!(
679 spans.iter().collect::<Vec<_>>(),
680 vec![SlashingSpan { index: 0, start: 1000, length: None }],
681 );
682 }
683
684 #[test]
685 fn many_prior_spans() {
686 let spans = SlashingSpans {
687 span_index: 10,
688 last_start: 1000,
689 last_nonzero_slash: 0,
690 prior: vec![10, 9, 8, 10],
691 };
692
693 assert_eq!(
694 spans.iter().collect::<Vec<_>>(),
695 vec![
696 SlashingSpan { index: 10, start: 1000, length: None },
697 SlashingSpan { index: 9, start: 990, length: Some(10) },
698 SlashingSpan { index: 8, start: 981, length: Some(9) },
699 SlashingSpan { index: 7, start: 973, length: Some(8) },
700 SlashingSpan { index: 6, start: 963, length: Some(10) },
701 ],
702 )
703 }
704
705 #[test]
706 fn pruning_spans() {
707 let mut spans = SlashingSpans {
708 span_index: 10,
709 last_start: 1000,
710 last_nonzero_slash: 0,
711 prior: vec![10, 9, 8, 10],
712 };
713
714 assert_eq!(spans.prune(981), Some((6, 8)));
715 assert_eq!(
716 spans.iter().collect::<Vec<_>>(),
717 vec![
718 SlashingSpan { index: 10, start: 1000, length: None },
719 SlashingSpan { index: 9, start: 990, length: Some(10) },
720 SlashingSpan { index: 8, start: 981, length: Some(9) },
721 ],
722 );
723
724 assert_eq!(spans.prune(982), None);
725 assert_eq!(
726 spans.iter().collect::<Vec<_>>(),
727 vec![
728 SlashingSpan { index: 10, start: 1000, length: None },
729 SlashingSpan { index: 9, start: 990, length: Some(10) },
730 SlashingSpan { index: 8, start: 981, length: Some(9) },
731 ],
732 );
733
734 assert_eq!(spans.prune(989), None);
735 assert_eq!(
736 spans.iter().collect::<Vec<_>>(),
737 vec![
738 SlashingSpan { index: 10, start: 1000, length: None },
739 SlashingSpan { index: 9, start: 990, length: Some(10) },
740 SlashingSpan { index: 8, start: 981, length: Some(9) },
741 ],
742 );
743
744 assert_eq!(spans.prune(1000), Some((8, 10)));
745 assert_eq!(
746 spans.iter().collect::<Vec<_>>(),
747 vec![SlashingSpan { index: 10, start: 1000, length: None },],
748 );
749
750 assert_eq!(spans.prune(2000), None);
751 assert_eq!(
752 spans.iter().collect::<Vec<_>>(),
753 vec![SlashingSpan { index: 10, start: 2000, length: None },],
754 );
755
756 let mut spans = SlashingSpans {
758 span_index: 10,
759 last_start: 1000,
760 last_nonzero_slash: 0,
761 prior: vec![10, 9, 8, 10],
762 };
763 assert_eq!(spans.prune(2000), Some((6, 10)));
764 assert_eq!(
765 spans.iter().collect::<Vec<_>>(),
766 vec![SlashingSpan { index: 10, start: 2000, length: None },],
767 );
768 }
769
770 #[test]
771 fn ending_span() {
772 let mut spans = SlashingSpans {
773 span_index: 1,
774 last_start: 10,
775 last_nonzero_slash: 0,
776 prior: Vec::new(),
777 };
778
779 assert!(spans.end_span(10));
780
781 assert_eq!(
782 spans.iter().collect::<Vec<_>>(),
783 vec![
784 SlashingSpan { index: 2, start: 11, length: None },
785 SlashingSpan { index: 1, start: 10, length: Some(1) },
786 ],
787 );
788
789 assert!(spans.end_span(15));
790 assert_eq!(
791 spans.iter().collect::<Vec<_>>(),
792 vec![
793 SlashingSpan { index: 3, start: 16, length: None },
794 SlashingSpan { index: 2, start: 11, length: Some(5) },
795 SlashingSpan { index: 1, start: 10, length: Some(1) },
796 ],
797 );
798
799 assert!(!spans.end_span(15));
801 assert_eq!(
802 spans.iter().collect::<Vec<_>>(),
803 vec![
804 SlashingSpan { index: 3, start: 16, length: None },
805 SlashingSpan { index: 2, start: 11, length: Some(5) },
806 SlashingSpan { index: 1, start: 10, length: Some(1) },
807 ],
808 );
809 }
810}