@@ -280,7 +280,7 @@ use sr_primitives::traits::{
280280} ;
281281use sr_staking_primitives:: {
282282 SessionIndex , CurrentElectedSet ,
283- offence:: { OnOffenceHandler , OffenceDetails } ,
283+ offence:: { OnOffenceHandler , OffenceDetails , Offence , ReportOffence } ,
284284} ;
285285#[ cfg( feature = "std" ) ]
286286use sr_primitives:: { Serialize , Deserialize } ;
@@ -441,6 +441,15 @@ pub struct Exposure<AccountId, Balance: HasCompact> {
441441 pub others : Vec < IndividualExposure < AccountId , Balance > > ,
442442}
443443
444+ /// A slashing event occurred, slashing a validator for a given amount of balance.
445+ #[ derive( PartialEq , Eq , PartialOrd , Ord , Clone , Encode , Decode , Default ) ]
446+ #[ cfg_attr( feature = "std" , derive( Debug ) ) ]
447+ pub struct SlashJournalEntry < AccountId , Balance : HasCompact > {
448+ who : AccountId ,
449+ amount : Balance ,
450+ own_slash : Balance , // the amount of `who`'s own exposure that was slashed
451+ }
452+
444453pub type BalanceOf < T > =
445454 <<T as Trait >:: Currency as Currency < <T as system:: Trait >:: AccountId > >:: Balance ;
446455type PositiveImbalanceOf < T > =
@@ -614,6 +623,10 @@ decl_storage! {
614623
615624 /// A mapping from still-bonded eras to the first session index of that era.
616625 BondedEras : Vec <( EraIndex , SessionIndex ) >;
626+
627+ /// All slashes that have occurred in a given era.
628+ EraSlashJournal get( era_slash_journal) :
629+ map EraIndex => Vec <SlashJournalEntry <T :: AccountId , BalanceOf <T >>>;
617630 }
618631 add_extra_genesis {
619632 config( stakers) :
@@ -659,6 +672,9 @@ decl_event!(
659672 Reward ( Balance ) ,
660673 /// One validator (and its nominators) has been slashed by the given amount.
661674 Slash ( AccountId , Balance ) ,
675+ /// An old slashing report from a prior era was discarded because it could
676+ /// not be processed.
677+ OldSlashingReportDiscarded ( SessionIndex ) ,
662678 }
663679) ;
664680
@@ -1030,17 +1046,33 @@ impl<T: Trait> Module<T> {
10301046 /// Removes the slash from the validator's balance by preference,
10311047 /// and reduces the nominators' balance if needed.
10321048 ///
1033- /// Returns the resulting `NegativeImbalance` to allow distributing the slashed amount.
1049+ /// Returns the resulting `NegativeImbalance` to allow distributing the slashed amount and
1050+ /// pushes an entry onto the slash journal.
10341051 fn slash_validator (
10351052 stash : & T :: AccountId ,
10361053 slash : BalanceOf < T > ,
10371054 exposure : & Exposure < T :: AccountId , BalanceOf < T > > ,
1055+ journal : & mut Vec < SlashJournalEntry < T :: AccountId , BalanceOf < T > > > ,
10381056 ) -> NegativeImbalanceOf < T > {
10391057 // The amount we are actually going to slash (can't be bigger than the validator's total
10401058 // exposure)
10411059 let slash = slash. min ( exposure. total ) ;
1060+
1061+ // limit what we'll slash of the stash's own to only what's in
1062+ // the exposure.
1063+ //
1064+ // note: this is fine only because we limit reports of the current era.
1065+ // otherwise, these funds may have already been slashed due to something
1066+ // reported from a prior era.
1067+ let already_slashed_own = journal. iter ( )
1068+ . filter ( |entry| & entry. who == stash)
1069+ . map ( |entry| entry. own_slash )
1070+ . fold ( <BalanceOf < T > >:: zero ( ) , |a, c| a. saturating_add ( c) ) ;
1071+
1072+ let own_remaining = exposure. own . saturating_sub ( already_slashed_own) ;
1073+
10421074 // The amount we'll slash from the validator's stash directly.
1043- let own_slash = exposure . own . min ( slash) ;
1075+ let own_slash = own_remaining . min ( slash) ;
10441076 let ( mut imbalance, missing) = T :: Currency :: slash ( stash, own_slash) ;
10451077 let own_slash = own_slash - missing;
10461078 // The amount remaining that we can't slash from the validator,
@@ -1058,6 +1090,12 @@ impl<T: Trait> Module<T> {
10581090 }
10591091 }
10601092
1093+ journal. push ( SlashJournalEntry {
1094+ who : stash. clone ( ) ,
1095+ own_slash : own_slash. clone ( ) ,
1096+ amount : slash,
1097+ } ) ;
1098+
10611099 // trigger the event
10621100 Self :: deposit_event (
10631101 RawEvent :: Slash ( stash. clone ( ) , slash)
@@ -1178,6 +1216,10 @@ impl<T: Trait> Module<T> {
11781216
11791217 // Increment current era.
11801218 let current_era = CurrentEra :: mutate ( |s| { * s += 1 ; * s } ) ;
1219+
1220+ // prune journal for last era.
1221+ <EraSlashJournal < T > >:: remove ( current_era - 1 ) ;
1222+
11811223 CurrentEraStartSessionIndex :: mutate ( |v| {
11821224 * v = start_session_index;
11831225 } ) ;
@@ -1471,6 +1513,7 @@ impl<T: Trait> SelectInitialValidators<T::AccountId> for Module<T> {
14711513 }
14721514}
14731515
1516+ /// This is intended to be used with `FilterHistoricalOffences`.
14741517impl < T : Trait > OnOffenceHandler < T :: AccountId , session:: historical:: IdentificationTuple < T > > for Module < T > where
14751518 T : session:: Trait < ValidatorId = <T as system:: Trait >:: AccountId > ,
14761519 T : session:: historical:: Trait <
@@ -1489,6 +1532,8 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, session::historical::Identificati
14891532 let mut remaining_imbalance = <NegativeImbalanceOf < T > >:: zero ( ) ;
14901533 let slash_reward_fraction = SlashRewardFraction :: get ( ) ;
14911534
1535+ let era_now = Self :: current_era ( ) ;
1536+ let mut journal = Self :: era_slash_journal ( era_now) ;
14921537 for ( details, slash_fraction) in offenders. iter ( ) . zip ( slash_fraction) {
14931538 let stash = & details. offender . 0 ;
14941539 let exposure = & details. offender . 1 ;
@@ -1512,7 +1557,7 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, session::historical::Identificati
15121557 // force a new era, to select a new validator set
15131558 ForceEra :: put ( Forcing :: ForceNew ) ;
15141559 // actually slash the validator
1515- let slashed_amount = Self :: slash_validator ( stash, amount, exposure) ;
1560+ let slashed_amount = Self :: slash_validator ( stash, amount, exposure, & mut journal ) ;
15161561
15171562 // distribute the rewards according to the slash
15181563 let slash_reward = slash_reward_fraction * slashed_amount. peek ( ) ;
@@ -1533,12 +1578,37 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, session::historical::Identificati
15331578 remaining_imbalance. subsume ( slashed_amount) ;
15341579 }
15351580 }
1581+ <EraSlashJournal < T > >:: insert ( era_now, journal) ;
15361582
15371583 // Handle the rest of imbalances
15381584 T :: Slash :: on_unbalanced ( remaining_imbalance) ;
15391585 }
15401586}
15411587
1588+ /// Filter historical offences out and only allow those from the current era.
1589+ pub struct FilterHistoricalOffences < T , R > {
1590+ _inner : rstd:: marker:: PhantomData < ( T , R ) > ,
1591+ }
1592+
1593+ impl < T , Reporter , Offender , R , O > ReportOffence < Reporter , Offender , O >
1594+ for FilterHistoricalOffences < Module < T > , R > where
1595+ T : Trait ,
1596+ R : ReportOffence < Reporter , Offender , O > ,
1597+ O : Offence < Offender > ,
1598+ {
1599+ fn report_offence ( reporters : Vec < Reporter > , offence : O ) {
1600+ // disallow any slashing from before the current era.
1601+ let offence_session = offence. session_index ( ) ;
1602+ if offence_session >= <Module < T > >:: current_era_start_session_index ( ) {
1603+ R :: report_offence ( reporters, offence)
1604+ } else {
1605+ <Module < T > >:: deposit_event (
1606+ RawEvent :: OldSlashingReportDiscarded ( offence_session) . into ( )
1607+ )
1608+ }
1609+ }
1610+ }
1611+
15421612/// Returns the currently elected validator set represented by their stash accounts.
15431613pub struct CurrentElectedStashAccounts < T > ( rstd:: marker:: PhantomData < T > ) ;
15441614
0 commit comments