Offences Weight for OnInitialize (#5961)

* Weight accounting for on_offence.

* Try to compute weight.

* Guesstimate upper bounds on db read/writes for slashing

* greater than or equal to

* add new trait

* Update mock.rs

* Add basic weight test

* one more test

* Update frame/staking/src/lib.rs

Co-authored-by: thiolliere <gui.thiolliere@gmail.com>

* Update frame/staking/src/lib.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Add test for offences queue

Co-authored-by: Tomasz Drwięga <tomasz@parity.io>
Co-authored-by: thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Shawn Tabrizi
2020-05-21 14:00:24 +02:00
committed by GitHub
parent 0ddd5cc278
commit ac049a97be
9 changed files with 219 additions and 38 deletions
+43 -7
View File
@@ -3218,7 +3218,9 @@ impl<T: Trait> Convert<T::AccountId, Option<Exposure<T::AccountId, BalanceOf<T>>
}
/// This is intended to be used with `FilterHistoricalOffences`.
impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>> for Module<T> where
impl <T: Trait>
OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>, Weight>
for Module<T> where
T: pallet_session::Trait<ValidatorId = <T as frame_system::Trait>::AccountId>,
T: pallet_session::historical::Trait<
FullIdentification = Exposure<<T as frame_system::Trait>::AccountId, BalanceOf<T>>,
@@ -3226,24 +3228,32 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::Ident
>,
T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Trait>::AccountId>,
T::SessionManager: pallet_session::SessionManager<<T as frame_system::Trait>::AccountId>,
T::ValidatorIdOf: Convert<<T as frame_system::Trait>::AccountId, Option<<T as frame_system::Trait>::AccountId>>
T::ValidatorIdOf: Convert<
<T as frame_system::Trait>::AccountId,
Option<<T as frame_system::Trait>::AccountId>,
>,
{
fn on_offence(
offenders: &[OffenceDetails<T::AccountId, pallet_session::historical::IdentificationTuple<T>>],
slash_fraction: &[Perbill],
slash_session: SessionIndex,
) -> Result<(), ()> {
) -> Result<Weight, ()> {
if !Self::can_report() {
return Err(())
}
let reward_proportion = SlashRewardFraction::get();
let mut consumed_weight: Weight = 0;
let mut add_db_reads_writes = |reads, writes| {
consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
};
let active_era = {
let active_era = Self::active_era();
add_db_reads_writes(1, 0);
if active_era.is_none() {
// this offence need not be re-submitted.
return Ok(())
return Ok(consumed_weight)
}
active_era.expect("value checked not to be `None`; qed").index
};
@@ -3252,6 +3262,7 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::Ident
frame_support::print("Error: start_session_index must be set for current_era");
0
});
add_db_reads_writes(1, 0);
let window_start = active_era.saturating_sub(T::BondingDuration::get());
@@ -3261,11 +3272,13 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::Ident
active_era
} else {
let eras = BondedEras::get();
add_db_reads_writes(1, 0);
// reverse because it's more likely to find reports from recent eras.
match eras.iter().rev().filter(|&&(_, ref sesh)| sesh <= &slash_session).next() {
None => return Ok(()), // before bonding period. defensive - should be filtered out.
Some(&(ref slash_era, _)) => *slash_era,
// before bonding period. defensive - should be filtered out.
None => return Ok(consumed_weight),
}
};
@@ -3274,14 +3287,18 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::Ident
*earliest = Some(active_era)
}
});
add_db_reads_writes(1, 1);
let slash_defer_duration = T::SlashDeferDuration::get();
let invulnerables = Self::invulnerables();
add_db_reads_writes(1, 0);
for (details, slash_fraction) in offenders.iter().zip(slash_fraction) {
let (stash, exposure) = &details.offender;
// Skip if the validator is invulnerable.
if Self::invulnerables().contains(stash) {
if invulnerables.contains(stash) {
continue
}
@@ -3296,21 +3313,40 @@ impl <T: Trait> OnOffenceHandler<T::AccountId, pallet_session::historical::Ident
});
if let Some(mut unapplied) = unapplied {
let nominators_len = unapplied.others.len() as u64;
let reporters_len = details.reporters.len() as u64;
{
let upper_bound = 1 /* Validator/NominatorSlashInEra */ + 2 /* fetch_spans */;
let rw = upper_bound + nominators_len * upper_bound;
add_db_reads_writes(rw, rw);
}
unapplied.reporters = details.reporters.clone();
if slash_defer_duration == 0 {
// apply right away.
slashing::apply_slash::<T>(unapplied);
{
let slash_cost = (6, 5);
let reward_cost = (2, 2);
add_db_reads_writes(
(1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len,
(1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len
);
}
} else {
// defer to end of some `slash_defer_duration` from now.
<Self as Store>::UnappliedSlashes::mutate(
active_era,
move |for_later| for_later.push(unapplied),
);
add_db_reads_writes(1, 1);
}
} else {
add_db_reads_writes(4 /* fetch_spans */, 5 /* kick_out_if_recent */)
}
}
Ok(())
Ok(consumed_weight)
}
fn can_report() -> bool {
+43
View File
@@ -4667,6 +4667,49 @@ fn migrate_era_should_handle_errors_2() {
});
}
#[test]
fn offences_weight_calculated_correctly() {
ExtBuilder::default().nominate(true).build_and_execute(|| {
// On offence with zero offenders: 4 Reads, 1 Write
let zero_offence_weight = <Test as frame_system::Trait>::DbWeight::get().reads_writes(4, 1);
assert_eq!(Staking::on_offence(&[], &[Perbill::from_percent(50)], 0), Ok(zero_offence_weight));
// On Offence with N offenders, Unapplied: 4 Reads, 1 Write + 4 Reads, 5 Writes
let n_offence_unapplied_weight = <Test as frame_system::Trait>::DbWeight::get().reads_writes(4, 1)
+ <Test as frame_system::Trait>::DbWeight::get().reads_writes(4, 5);
let offenders: Vec<OffenceDetails<<Test as frame_system::Trait>::AccountId, pallet_session::historical::IdentificationTuple<Test>>>
= (1..10).map(|i|
OffenceDetails {
offender: (i, Staking::eras_stakers(Staking::active_era().unwrap().index, i)),
reporters: vec![],
}
).collect();
assert_eq!(Staking::on_offence(&offenders, &[Perbill::from_percent(50)], 0), Ok(n_offence_unapplied_weight));
// On Offence with one offenders, Applied
let one_offender = [
OffenceDetails {
offender: (11, Staking::eras_stakers(Staking::active_era().unwrap().index, 11)),
reporters: vec![1],
},
];
let n = 1; // Number of offenders
let rw = 3 + 3 * n; // rw reads and writes
let one_offence_unapplied_weight = <Test as frame_system::Trait>::DbWeight::get().reads_writes(4, 1)
+ <Test as frame_system::Trait>::DbWeight::get().reads_writes(rw, rw)
// One `slash_cost`
+ <Test as frame_system::Trait>::DbWeight::get().reads_writes(6, 5)
// `slash_cost` * nominators (1)
+ <Test as frame_system::Trait>::DbWeight::get().reads_writes(6, 5)
// `reward_cost` * reporters (1)
+ <Test as frame_system::Trait>::DbWeight::get().reads_writes(2, 2);
assert_eq!(Staking::on_offence(&one_offender, &[Perbill::from_percent(50)], 0), Ok(one_offence_unapplied_weight));
});
}
#[test]
fn on_initialize_weight_is_correct() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {